PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mediaCenterPlugins/XbmcXml.cpp

https://github.com/zabinko/MediaElch
C++ | 1951 lines | 1524 code | 190 blank | 237 comment | 362 complexity | 5039a23fe7903bb37b94871978fd39c0 MD5 | raw file
Possible License(s): GPL-3.0
  1. #include "XbmcXml.h"
  2. #include <QApplication>
  3. #include <QDebug>
  4. #include <QDir>
  5. #include <QFileInfo>
  6. #include <QXmlStreamWriter>
  7. #include "globals/Globals.h"
  8. #include "globals/Helper.h"
  9. #include "globals/Manager.h"
  10. #include "settings/Settings.h"
  11. /**
  12. * @brief XbmcXml::XbmcXml
  13. * @param parent
  14. */
  15. XbmcXml::XbmcXml(QObject *parent)
  16. {
  17. setParent(parent);
  18. }
  19. /**
  20. * @brief XbmcXml::~XbmcXml
  21. */
  22. XbmcXml::~XbmcXml()
  23. {
  24. }
  25. /**
  26. * @brief Checks if our MediaCenterPlugin supports a feature
  27. * @param feature Feature to check
  28. * @return Feature is supported or not
  29. */
  30. bool XbmcXml::hasFeature(int feature)
  31. {
  32. if (feature == MediaCenterFeatures::HandleMovieSetImages)
  33. return false;
  34. return true;
  35. }
  36. /**
  37. * @brief Writes movie elements to an xml stream
  38. * @param xml XML stream
  39. * @param movie Movie to save
  40. */
  41. void XbmcXml::writeMovieXml(QXmlStreamWriter &xml, Movie *movie)
  42. {
  43. qDebug() << "Entered, movie=" << movie->name();
  44. xml.writeStartElement("movie");
  45. xml.writeTextElement("title", movie->name());
  46. xml.writeTextElement("originaltitle", movie->originalName());
  47. xml.writeTextElement("rating", QString("%1").arg(movie->rating()));
  48. xml.writeTextElement("votes", QString::number(movie->votes()));
  49. xml.writeTextElement("top250", QString::number(movie->top250()));
  50. xml.writeTextElement("year", movie->released().toString("yyyy"));
  51. xml.writeTextElement("plot", movie->overview());
  52. xml.writeTextElement("outline", movie->outline());
  53. xml.writeTextElement("tagline", movie->tagline());
  54. if (movie->runtime() > 0)
  55. xml.writeTextElement("runtime", QString("%1").arg(movie->runtime()));
  56. xml.writeTextElement("mpaa", movie->certification());
  57. xml.writeTextElement("credits", movie->writer());
  58. xml.writeTextElement("director", movie->director());
  59. xml.writeTextElement("playcount", QString("%1").arg(movie->playcount()));
  60. xml.writeTextElement("lastplayed", movie->lastPlayed().toString("yyyy-MM-dd HH:mm:ss"));
  61. xml.writeTextElement("id", movie->id());
  62. xml.writeTextElement("tmdbid", movie->tmdbId());
  63. xml.writeTextElement("set", movie->set());
  64. xml.writeTextElement("sorttitle", movie->sortTitle());
  65. xml.writeTextElement("trailer", Helper::formatTrailerUrl(movie->trailer().toString()));
  66. xml.writeTextElement("watched", (movie->watched()) ? "true" : "false");
  67. foreach (const QString &studio, movie->studios())
  68. xml.writeTextElement("studio", studio);
  69. foreach (const QString &genre, movie->genres())
  70. xml.writeTextElement("genre", genre);
  71. foreach (const QString &country, movie->countries())
  72. xml.writeTextElement("country", country);
  73. foreach (const QString &tag, movie->tags())
  74. xml.writeTextElement("tag", tag);
  75. foreach (const Actor &actor, movie->actors()) {
  76. xml.writeStartElement("actor");
  77. xml.writeTextElement("name", actor.name);
  78. xml.writeTextElement("role", actor.role);
  79. xml.writeTextElement("thumb", actor.thumb);
  80. xml.writeEndElement();
  81. }
  82. foreach (const Poster &poster, movie->posters()) {
  83. xml.writeStartElement("thumb");
  84. xml.writeAttribute("preview", poster.thumbUrl.toString());
  85. xml.writeCharacters(poster.originalUrl.toString());
  86. xml.writeEndElement();
  87. }
  88. xml.writeStartElement("fanart");
  89. foreach (const Poster &poster, movie->backdrops()) {
  90. xml.writeStartElement("thumb");
  91. xml.writeAttribute("preview", poster.thumbUrl.toString());
  92. xml.writeCharacters(poster.originalUrl.toString());
  93. xml.writeEndElement();
  94. }
  95. xml.writeEndElement();
  96. xml.writeStartElement("fileinfo");
  97. writeStreamDetails(xml, movie->streamDetails());
  98. xml.writeEndElement();
  99. xml.writeEndElement();
  100. }
  101. /**
  102. * @brief Saves a movie (including images)
  103. * @param movie Movie to save
  104. * @return Saving success
  105. * @see XbmcXml::writeMovieXml
  106. */
  107. bool XbmcXml::saveMovie(Movie *movie)
  108. {
  109. qDebug() << "Entered, movie=" << movie->name();
  110. QByteArray xmlContent;
  111. QXmlStreamWriter xml(&xmlContent);
  112. xml.setAutoFormatting(true);
  113. xml.writeStartDocument("1.0", true);
  114. writeMovieXml(xml, movie);
  115. xml.writeEndDocument();
  116. if (movie->files().size() == 0) {
  117. qWarning() << "Movie has no files";
  118. return false;
  119. }
  120. movie->setNfoContent(xmlContent);
  121. Manager::instance()->database()->update(movie);
  122. bool saved = false;
  123. QFileInfo fi(movie->files().at(0));
  124. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieNfo)) {
  125. QString saveFileName = dataFile.saveFileName(fi.fileName());
  126. QFile file(fi.absolutePath() + QDir::separator() + saveFileName);
  127. qDebug() << "Saving to" << file.fileName();
  128. if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
  129. qWarning() << "File could not be openend";
  130. } else {
  131. file.write(xmlContent);
  132. file.close();
  133. saved = true;
  134. }
  135. }
  136. if (!saved)
  137. return false;
  138. if (movie->posterImageChanged() && !movie->posterImage()->isNull()) {
  139. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MoviePoster)) {
  140. QString saveFileName = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
  141. qDebug() << "Saving poster to" << fi.absolutePath() + QDir::separator() + saveFileName;
  142. movie->posterImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
  143. }
  144. }
  145. if (movie->backdropImageChanged() && !movie->backdropImage()->isNull()) {
  146. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieBackdrop)) {
  147. QString saveFileName = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
  148. qDebug() << "Saving fanart to" << fi.absolutePath() + QDir::separator() + saveFileName;
  149. movie->backdropImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
  150. }
  151. }
  152. saveAdditionalImages(movie);
  153. foreach (const Actor &actor, movie->actors()) {
  154. if (!actor.image.isNull()) {
  155. QDir dir;
  156. dir.mkdir(fi.absolutePath() + QDir::separator() + ".actors");
  157. QString actorName = actor.name;
  158. actorName = actorName.replace(" ", "_");
  159. actor.image.save(fi.absolutePath() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn", "jpg", 100);
  160. }
  161. }
  162. return true;
  163. }
  164. /**
  165. * @brief Saves additional movie images (logo, clear art, cd art)
  166. * @param movie Movie object
  167. */
  168. void XbmcXml::saveAdditionalImages(Movie *movie)
  169. {
  170. QFileInfo fi(movie->files().at(0));
  171. if (movie->logoImageChanged() && !movie->logoImage()->isNull()) {
  172. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieLogo)) {
  173. QString saveFileName = dataFile.saveFileName(fi.fileName());
  174. qDebug() << "Saving logo to" << fi.absolutePath() + QDir::separator() + saveFileName;
  175. movie->logoImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  176. }
  177. }
  178. if (movie->clearArtImageChanged() && !movie->clearArtImage()->isNull()) {
  179. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieClearArt)) {
  180. QString saveFileName = dataFile.saveFileName(fi.fileName());
  181. qDebug() << "Saving clear art to" << fi.absolutePath() + QDir::separator() + saveFileName;
  182. movie->clearArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  183. }
  184. }
  185. if (movie->cdArtImageChanged() && !movie->cdArtImage()->isNull()) {
  186. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieCdArt)) {
  187. QString saveFileName = dataFile.saveFileName(fi.fileName());
  188. qDebug() << "Saving cd art to" << fi.absolutePath() + QDir::separator() + saveFileName;
  189. movie->cdArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  190. }
  191. }
  192. if (movie->inSeparateFolder() && !movie->files().isEmpty()) {
  193. foreach (const QString &file, movie->extraFanartsToRemove())
  194. QFile::remove(file);
  195. QDir dir(QFileInfo(movie->files().first()).absolutePath() + "/extrafanart");
  196. if (!dir.exists() && !movie->extraFanartImagesToAdd().isEmpty())
  197. QDir(QFileInfo(movie->files().first()).absolutePath()).mkdir("extrafanart");
  198. foreach (QImage img, movie->extraFanartImagesToAdd()) {
  199. int num = 1;
  200. while (QFileInfo(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num)).exists())
  201. ++num;
  202. img.save(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num), "jpg", 100);
  203. }
  204. }
  205. }
  206. /**
  207. * @brief Tries to find an nfo file for the movie
  208. * @param movie Movie
  209. * @return Path to nfo file, if none found returns an empty string
  210. */
  211. QString XbmcXml::nfoFilePath(Movie *movie)
  212. {
  213. QString nfoFile;
  214. if (movie->files().size() == 0) {
  215. qWarning() << "Movie has no files";
  216. return nfoFile;
  217. }
  218. QFileInfo fi(movie->files().at(0));
  219. if (!fi.isFile() ) {
  220. qWarning() << "First file of the movie is not readable" << movie->files().at(0);
  221. return nfoFile;
  222. }
  223. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::MovieNfo)) {
  224. QString file = dataFile.saveFileName(fi.fileName());
  225. QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
  226. if (nfoFi.exists()) {
  227. nfoFile = fi.absolutePath() + QDir::separator() + file;
  228. break;
  229. }
  230. }
  231. return nfoFile;
  232. }
  233. QString XbmcXml::nfoFilePath(TvShowEpisode *episode)
  234. {
  235. QString nfoFile;
  236. if (episode->files().size() == 0) {
  237. qWarning() << "Episode has no files";
  238. return nfoFile;
  239. }
  240. QFileInfo fi(episode->files().at(0));
  241. if (!fi.isFile() ) {
  242. qWarning() << "First file of the episode is not readable" << episode->files().at(0);
  243. return nfoFile;
  244. }
  245. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowEpisodeNfo)) {
  246. QString file = dataFile.saveFileName(fi.fileName());
  247. QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
  248. if (nfoFi.exists()) {
  249. nfoFile = fi.absolutePath() + QDir::separator() + file;
  250. break;
  251. }
  252. }
  253. return nfoFile;
  254. }
  255. /**
  256. * @brief Tries to find an nfo file for the concert
  257. * @param concert Concert
  258. * @return Path to nfo file, if none found returns an empty string
  259. */
  260. QString XbmcXml::nfoFilePath(Concert *concert)
  261. {
  262. QString nfoFile;
  263. if (concert->files().size() == 0) {
  264. qWarning() << "Concert has no files";
  265. return nfoFile;
  266. }
  267. QFileInfo fi(concert->files().at(0));
  268. if (!fi.isFile() ) {
  269. qWarning() << "First file of the concert is not readable" << concert->files().at(0);
  270. return nfoFile;
  271. }
  272. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertNfo)) {
  273. QString file = dataFile.saveFileName(fi.fileName());
  274. QFileInfo nfoFi(fi.absolutePath() + QDir::separator() + file);
  275. if (nfoFi.exists()) {
  276. nfoFile = fi.absolutePath() + QDir::separator() + file;
  277. break;
  278. }
  279. }
  280. return nfoFile;
  281. }
  282. /**
  283. * @brief Loads movie infos (except images)
  284. * @param movie Movie to load
  285. * @return Loading success
  286. */
  287. bool XbmcXml::loadMovie(Movie *movie, QString initialNfoContent)
  288. {
  289. movie->clear();
  290. movie->setChanged(false);
  291. QString nfoContent;
  292. if (initialNfoContent.isEmpty()) {
  293. QString nfoFile = nfoFilePath(movie);
  294. if (nfoFile.isEmpty())
  295. return false;
  296. QFile file(nfoFile);
  297. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  298. qWarning() << "File" << nfoFile << "could not be opened for reading";
  299. return false;
  300. }
  301. nfoContent = QString::fromUtf8(file.readAll());
  302. movie->setNfoContent(nfoContent);
  303. file.close();
  304. } else {
  305. nfoContent = initialNfoContent;
  306. }
  307. QDomDocument domDoc;
  308. domDoc.setContent(nfoContent);
  309. if (!domDoc.elementsByTagName("title").isEmpty() )
  310. movie->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
  311. if (!domDoc.elementsByTagName("originaltitle").isEmpty())
  312. movie->setOriginalName(domDoc.elementsByTagName("originaltitle").at(0).toElement().text());
  313. if (!domDoc.elementsByTagName("rating").isEmpty())
  314. movie->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
  315. if (!domDoc.elementsByTagName("votes").isEmpty())
  316. movie->setVotes(domDoc.elementsByTagName("votes").at(0).toElement().text().toInt());
  317. if (!domDoc.elementsByTagName("top250").isEmpty())
  318. movie->setTop250(domDoc.elementsByTagName("top250").at(0).toElement().text().toInt());
  319. if (!domDoc.elementsByTagName("year").isEmpty())
  320. movie->setReleased(QDate::fromString(domDoc.elementsByTagName("year").at(0).toElement().text(), "yyyy"));
  321. if (!domDoc.elementsByTagName("plot").isEmpty())
  322. movie->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
  323. if (!domDoc.elementsByTagName("outline").isEmpty())
  324. movie->setOutline(domDoc.elementsByTagName("outline").at(0).toElement().text());
  325. if (!domDoc.elementsByTagName("tagline").isEmpty())
  326. movie->setTagline(domDoc.elementsByTagName("tagline").at(0).toElement().text());
  327. if (!domDoc.elementsByTagName("runtime").isEmpty())
  328. movie->setRuntime(domDoc.elementsByTagName("runtime").at(0).toElement().text().toInt());
  329. if (!domDoc.elementsByTagName("mpaa").isEmpty())
  330. movie->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
  331. if (!domDoc.elementsByTagName("credits").isEmpty())
  332. movie->setWriter(domDoc.elementsByTagName("credits").at(0).toElement().text());
  333. if (!domDoc.elementsByTagName("director").isEmpty())
  334. movie->setDirector(domDoc.elementsByTagName("director").at(0).toElement().text());
  335. if (!domDoc.elementsByTagName("playcount").isEmpty())
  336. movie->setPlayCount(domDoc.elementsByTagName("playcount").at(0).toElement().text().toInt());
  337. if (!domDoc.elementsByTagName("lastplayed").isEmpty())
  338. movie->setLastPlayed(QDateTime::fromString(domDoc.elementsByTagName("lastplayed").at(0).toElement().text(), "yyyy-MM-dd HH:mm:ss"));
  339. if (!domDoc.elementsByTagName("id").isEmpty())
  340. movie->setId(domDoc.elementsByTagName("id").at(0).toElement().text());
  341. if (!domDoc.elementsByTagName("tmdbid").isEmpty())
  342. movie->setTmdbId(domDoc.elementsByTagName("tmdbid").at(0).toElement().text());
  343. if (!domDoc.elementsByTagName("set").isEmpty())
  344. movie->setSet(domDoc.elementsByTagName("set").at(0).toElement().text());
  345. if (!domDoc.elementsByTagName("sorttitle").isEmpty())
  346. movie->setSortTitle(domDoc.elementsByTagName("sorttitle").at(0).toElement().text());
  347. if (!domDoc.elementsByTagName("trailer").isEmpty())
  348. movie->setTrailer(QUrl(domDoc.elementsByTagName("trailer").at(0).toElement().text()));
  349. if (!domDoc.elementsByTagName("watched").isEmpty())
  350. movie->setWatched(domDoc.elementsByTagName("watched").at(0).toElement().text() == "true" ? true : false);
  351. for (int i=0, n=domDoc.elementsByTagName("studio").size() ; i<n ; i++)
  352. movie->addStudio(domDoc.elementsByTagName("studio").at(i).toElement().text());
  353. for (int i=0, n=domDoc.elementsByTagName("genre").size() ; i<n ; i++)
  354. movie->addGenre(domDoc.elementsByTagName("genre").at(i).toElement().text());
  355. for (int i=0, n=domDoc.elementsByTagName("country").size() ; i<n ; i++)
  356. movie->addCountry(domDoc.elementsByTagName("country").at(i).toElement().text());
  357. for (int i=0, n=domDoc.elementsByTagName("tag").size() ; i<n ; i++)
  358. movie->addTag(domDoc.elementsByTagName("tag").at(i).toElement().text());
  359. for (int i=0, n=domDoc.elementsByTagName("actor").size() ; i<n ; i++) {
  360. Actor a;
  361. a.imageHasChanged = false;
  362. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").isEmpty())
  363. a.name = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").at(0).toElement().text();
  364. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").isEmpty())
  365. a.role = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").at(0).toElement().text();
  366. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").isEmpty())
  367. a.thumb = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").at(0).toElement().text();
  368. movie->addActor(a);
  369. }
  370. for (int i=0, n=domDoc.elementsByTagName("thumb").size() ; i<n ; i++) {
  371. QString parentTag = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().tagName();
  372. if (parentTag == "movie") {
  373. Poster p;
  374. p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
  375. p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
  376. movie->addPoster(p);
  377. } else if (parentTag == "fanart") {
  378. Poster p;
  379. p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
  380. p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
  381. movie->addBackdrop(p);
  382. }
  383. }
  384. movie->setStreamDetailsLoaded(loadStreamDetails(movie->streamDetails(), domDoc));
  385. // Existence of images
  386. if (initialNfoContent.isEmpty()) {
  387. movie->setHasPoster(!posterImageName(movie).isEmpty());
  388. movie->setHasBackdrop(!backdropImageName(movie).isEmpty());
  389. movie->setHasLogo(!logoImageName(movie).isEmpty());
  390. movie->setHasClearArt(!clearArtImageName(movie).isEmpty());
  391. movie->setHasCdArt(!cdArtImageName(movie).isEmpty());
  392. }
  393. return true;
  394. }
  395. /**
  396. * @brief Loads the stream details from the dom document
  397. * @param streamDetails StreamDetails object
  398. * @param domDoc Nfo document
  399. * @return Infos loaded
  400. */
  401. bool XbmcXml::loadStreamDetails(StreamDetails* streamDetails, QDomDocument domDoc)
  402. {
  403. streamDetails->clear();
  404. if (!domDoc.elementsByTagName("streamdetails").isEmpty()) {
  405. QDomElement elem = domDoc.elementsByTagName("streamdetails").at(0).toElement();
  406. if (!elem.elementsByTagName("video").isEmpty()) {
  407. QDomElement videoElem = elem.elementsByTagName("video").at(0).toElement();
  408. QStringList details = (QStringList() << "codec" << "aspect" << "width" << "height" << "durationinseconds" << "scantype");
  409. foreach (const QString &detail, details) {
  410. if (!videoElem.elementsByTagName(detail).isEmpty())
  411. streamDetails->setVideoDetail(detail, videoElem.elementsByTagName(detail).at(0).toElement().text());
  412. }
  413. }
  414. if (!elem.elementsByTagName("audio").isEmpty()) {
  415. for (int i=0, n=elem.elementsByTagName("audio").count() ; i<n ; ++i) {
  416. QStringList details = QStringList() << "codec" << "language" << "channels";
  417. QDomElement audioElem = elem.elementsByTagName("audio").at(i).toElement();
  418. foreach (const QString &detail, details) {
  419. if (!audioElem.elementsByTagName(detail).isEmpty())
  420. streamDetails->setAudioDetail(i, detail, audioElem.elementsByTagName(detail).at(0).toElement().text());
  421. }
  422. }
  423. }
  424. if (!elem.elementsByTagName("subtitle").isEmpty()) {
  425. for (int i=0, n=elem.elementsByTagName("subtitle").count() ; i<n ; ++i) {
  426. QStringList details = QStringList() << "language";
  427. QDomElement subtitleElem = elem.elementsByTagName("subtitle").at(i).toElement();
  428. foreach (const QString &detail, details) {
  429. if (!subtitleElem.elementsByTagName(detail).isEmpty())
  430. streamDetails->setSubtitleDetail(i, detail, subtitleElem.elementsByTagName(detail).at(0).toElement().text());
  431. }
  432. }
  433. }
  434. return true;
  435. }
  436. return false;
  437. }
  438. /**
  439. * @brief Writes streamdetails to xml stream
  440. * @param xml XML Stream
  441. * @param streamDetails Stream Details object
  442. */
  443. void XbmcXml::writeStreamDetails(QXmlStreamWriter &xml, StreamDetails *streamDetails)
  444. {
  445. xml.writeStartElement("streamdetails");
  446. xml.writeStartElement("video");
  447. QMapIterator<QString, QString> itVideo(streamDetails->videoDetails());
  448. while (itVideo.hasNext()) {
  449. itVideo.next();
  450. if (itVideo.key() == "width" && itVideo.value().toInt() == 0)
  451. continue;
  452. if (itVideo.key() == "height" && itVideo.value().toInt() == 0)
  453. continue;
  454. if (itVideo.key() == "durationinseconds" && itVideo.value().toInt() == 0)
  455. continue;
  456. if (itVideo.value() == "")
  457. continue;
  458. xml.writeTextElement(itVideo.key(), itVideo.value());
  459. }
  460. xml.writeEndElement();
  461. for (int i=0, n=streamDetails->audioDetails().count() ; i<n ; ++i) {
  462. xml.writeStartElement("audio");
  463. QMapIterator<QString, QString> itAudio(streamDetails->audioDetails().at(i));
  464. while (itAudio.hasNext()) {
  465. itAudio.next();
  466. if (itAudio.value() == "")
  467. continue;
  468. xml.writeTextElement(itAudio.key(), itAudio.value());
  469. }
  470. xml.writeEndElement();
  471. }
  472. for (int i=0, n=streamDetails->subtitleDetails().count() ; i<n ; ++i) {
  473. xml.writeStartElement("subtitle");
  474. QMapIterator<QString, QString> itSubtitle(streamDetails->subtitleDetails().at(i));
  475. while (itSubtitle.hasNext()) {
  476. itSubtitle.next();
  477. if (itSubtitle.value() == "")
  478. continue;
  479. xml.writeTextElement(itSubtitle.key(), itSubtitle.value());
  480. }
  481. xml.writeEndElement();
  482. }
  483. xml.writeEndElement();
  484. }
  485. /**
  486. * @brief Get the path to the actor image
  487. * @param movie
  488. * @param actor Actor
  489. * @return Path to actor image
  490. */
  491. QString XbmcXml::actorImageName(Movie *movie, Actor actor)
  492. {
  493. if (movie->files().isEmpty())
  494. return QString();
  495. QFileInfo fi(movie->files().at(0));
  496. QString actorName = actor.name;
  497. actorName = actorName.replace(" ", "_");
  498. QString path = fi.absolutePath() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn";
  499. fi.setFile(path);
  500. if (fi.isFile())
  501. return path;
  502. return QString();
  503. }
  504. /**
  505. * @brief Get the path to the movie poster
  506. * @param movie Movie object
  507. * @return Path to poster image
  508. */
  509. QString XbmcXml::posterImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
  510. {
  511. QString posterFileName;
  512. if (movie->files().size() == 0) {
  513. qWarning() << "Movie has no files";
  514. return posterFileName;
  515. }
  516. QFileInfo fi(movie->files().at(0));
  517. if (!constructName)
  518. dataFiles = Settings::instance()->dataFiles(DataFileType::MoviePoster);
  519. foreach (DataFile dataFile, dataFiles) {
  520. QString file = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
  521. QFileInfo pFi(fi.absolutePath() + QDir::separator() + file);
  522. if (pFi.isFile() || constructName) {
  523. posterFileName = fi.absolutePath() + QDir::separator() + file;
  524. break;
  525. }
  526. }
  527. return posterFileName;
  528. }
  529. /**
  530. * @brief Get the path to the movie backdrop
  531. * @param movie Movie object
  532. * @return Path to backdrop image
  533. */
  534. QString XbmcXml::backdropImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
  535. {
  536. QString fanartFileName;
  537. if (movie->files().size() == 0) {
  538. qWarning() << "Movie has no files";
  539. return fanartFileName;
  540. }
  541. QFileInfo fi(movie->files().at(0));
  542. if (!constructName)
  543. dataFiles = Settings::instance()->dataFiles(DataFileType::MovieBackdrop);
  544. foreach (DataFile dataFile, dataFiles) {
  545. QString file = dataFile.saveFileName(fi.fileName(), -1, movie->files().count() > 1);
  546. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  547. if (bFi.isFile() || constructName) {
  548. fanartFileName = fi.absolutePath() + QDir::separator() + file;
  549. break;
  550. }
  551. }
  552. return fanartFileName;
  553. }
  554. /**
  555. * @brief Get the path to the movie logo
  556. * @param movie Movie object
  557. * @return Path to logo image
  558. */
  559. QString XbmcXml::logoImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
  560. {
  561. QString logoFileName;
  562. if (movie->files().size() == 0) {
  563. qWarning() << "Movie has no files";
  564. return logoFileName;
  565. }
  566. QFileInfo fi(movie->files().at(0));
  567. if (!constructName)
  568. dataFiles = Settings::instance()->dataFiles(DataFileType::MovieLogo);
  569. foreach (DataFile dataFile, dataFiles) {
  570. QString file = dataFile.saveFileName(fi.fileName());
  571. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  572. if (bFi.isFile() || constructName) {
  573. logoFileName = fi.absolutePath() + QDir::separator() + file;
  574. break;
  575. }
  576. }
  577. return logoFileName;
  578. }
  579. /**
  580. * @brief Get the path to the movie clear art
  581. * @param movie Movie object
  582. * @return Path to clear art image
  583. */
  584. QString XbmcXml::clearArtImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
  585. {
  586. QString clearArtFileName;
  587. if (movie->files().size() == 0) {
  588. qWarning() << "Movie has no files";
  589. return clearArtFileName;
  590. }
  591. QFileInfo fi(movie->files().at(0));
  592. if (!constructName)
  593. dataFiles = Settings::instance()->dataFiles(DataFileType::MovieClearArt);
  594. foreach (DataFile dataFile, dataFiles) {
  595. QString file = dataFile.saveFileName(fi.fileName());
  596. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  597. if (bFi.isFile() || constructName) {
  598. clearArtFileName = fi.absolutePath() + QDir::separator() + file;
  599. break;
  600. }
  601. }
  602. return clearArtFileName;
  603. }
  604. /**
  605. * @brief Get the path to the movie cd art
  606. * @param movie Movie object
  607. * @return Path to cd art image
  608. */
  609. QString XbmcXml::cdArtImageName(Movie *movie, QList<DataFile> dataFiles, bool constructName)
  610. {
  611. QString cdArtFileName;
  612. if (movie->files().size() == 0) {
  613. qWarning() << "Movie has no files";
  614. return cdArtFileName;
  615. }
  616. QFileInfo fi(movie->files().at(0));
  617. if (!constructName)
  618. dataFiles = Settings::instance()->dataFiles(DataFileType::MovieCdArt);
  619. foreach (DataFile dataFile, dataFiles) {
  620. QString file = dataFile.saveFileName(fi.fileName());
  621. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  622. if (bFi.isFile() || constructName) {
  623. cdArtFileName = fi.absolutePath() + QDir::separator() + file;
  624. break;
  625. }
  626. }
  627. return cdArtFileName;
  628. }
  629. /**
  630. * @brief Writes concert elements to an xml stream
  631. * @param xml XML stream
  632. * @param concert Concert to save
  633. */
  634. void XbmcXml::writeConcertXml(QXmlStreamWriter &xml, Concert *concert)
  635. {
  636. qDebug() << "Entered, concert=" << concert->name();
  637. xml.writeStartElement("musicvideo");
  638. xml.writeTextElement("title", concert->name());
  639. xml.writeTextElement("artist", concert->artist());
  640. xml.writeTextElement("album", concert->album());
  641. xml.writeTextElement("id", concert->id());
  642. xml.writeTextElement("tmdbid", concert->tmdbId());
  643. xml.writeTextElement("rating", QString("%1").arg(concert->rating()));
  644. xml.writeTextElement("year", concert->released().toString("yyyy"));
  645. xml.writeTextElement("plot", concert->overview());
  646. xml.writeTextElement("outline", concert->overview());
  647. xml.writeTextElement("tagline", concert->tagline());
  648. if (concert->runtime() > 0)
  649. xml.writeTextElement("runtime", QString("%1").arg(concert->runtime()));
  650. xml.writeTextElement("mpaa", concert->certification());
  651. xml.writeTextElement("playcount", QString("%1").arg(concert->playcount()));
  652. xml.writeTextElement("lastplayed", concert->lastPlayed().toString("yyyy-MM-dd HH:mm:ss"));
  653. xml.writeTextElement("trailer", Helper::formatTrailerUrl(concert->trailer().toString()));
  654. xml.writeTextElement("watched", (concert->watched()) ? "true" : "false");
  655. foreach (const QString &genre, concert->genres())
  656. xml.writeTextElement("genre", genre);
  657. foreach (const QString &tag, concert->tags())
  658. xml.writeTextElement("tag", tag);
  659. foreach (const Poster &poster, concert->posters()) {
  660. xml.writeStartElement("thumb");
  661. xml.writeAttribute("preview", poster.thumbUrl.toString());
  662. xml.writeCharacters(poster.originalUrl.toString());
  663. xml.writeEndElement();
  664. }
  665. xml.writeStartElement("fanart");
  666. foreach (const Poster &poster, concert->backdrops()) {
  667. xml.writeStartElement("thumb");
  668. xml.writeAttribute("preview", poster.thumbUrl.toString());
  669. xml.writeCharacters(poster.originalUrl.toString());
  670. xml.writeEndElement();
  671. }
  672. xml.writeEndElement();
  673. xml.writeStartElement("fileinfo");
  674. writeStreamDetails(xml, concert->streamDetails());
  675. xml.writeEndElement();
  676. xml.writeEndElement();
  677. }
  678. /**
  679. * @brief Saves a concert (including images)
  680. * @param concert Concert to save
  681. * @return Saving success
  682. * @see XbmcXml::writeConcertXml
  683. */
  684. bool XbmcXml::saveConcert(Concert *concert)
  685. {
  686. qDebug() << "Entered, movie=" << concert->name();
  687. QByteArray xmlContent;
  688. QXmlStreamWriter xml(&xmlContent);
  689. xml.setAutoFormatting(true);
  690. xml.writeStartDocument("1.0", true);
  691. writeConcertXml(xml, concert);
  692. xml.writeEndDocument();
  693. if (concert->files().size() == 0) {
  694. qWarning() << "Movie has no files";
  695. return false;
  696. }
  697. concert->setNfoContent(xmlContent);
  698. Manager::instance()->database()->update(concert);
  699. bool saved = false;
  700. QFileInfo fi(concert->files().at(0));
  701. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertNfo)) {
  702. QString saveFileName = dataFile.saveFileName(fi.fileName());
  703. QFile file(fi.absolutePath() + QDir::separator() + saveFileName);
  704. qDebug() << "Saving to" << file.fileName();
  705. if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
  706. qWarning() << "File could not be openend";
  707. } else {
  708. file.write(xmlContent);
  709. file.close();
  710. saved = true;
  711. }
  712. }
  713. if (!saved)
  714. return false;
  715. if (concert->posterImageChanged() && !concert->posterImage()->isNull()) {
  716. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertPoster)) {
  717. QString saveFileName = dataFile.saveFileName(fi.fileName());
  718. qDebug() << "Saving poster to" << fi.absolutePath() + QDir::separator() + saveFileName;
  719. concert->posterImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
  720. }
  721. }
  722. if (concert->backdropImageChanged() && !concert->backdropImage()->isNull()) {
  723. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertBackdrop)) {
  724. QString saveFileName = dataFile.saveFileName(fi.fileName());
  725. qDebug() << "Saving fanart to" << fi.absolutePath() + QDir::separator() + saveFileName;
  726. concert->backdropImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
  727. }
  728. }
  729. saveAdditionalImages(concert);
  730. return true;
  731. }
  732. /**
  733. * @brief Saves additional concert images (logo, clear art, cd art)
  734. * @param concert Concert object
  735. */
  736. void XbmcXml::saveAdditionalImages(Concert *concert)
  737. {
  738. QFileInfo fi(concert->files().at(0));
  739. if (concert->logoImageChanged() && !concert->logoImage()->isNull()) {
  740. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertLogo)) {
  741. QString saveFileName = dataFile.saveFileName(fi.fileName());
  742. qDebug() << "Saving logo to" << fi.absolutePath() + QDir::separator() + saveFileName;
  743. concert->logoImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  744. }
  745. }
  746. if (concert->clearArtImageChanged() && !concert->clearArtImage()->isNull()) {
  747. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertClearArt)) {
  748. QString saveFileName = dataFile.saveFileName(fi.fileName());
  749. qDebug() << "Saving clear art to" << fi.absolutePath() + QDir::separator() + saveFileName;
  750. concert->clearArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  751. }
  752. }
  753. if (concert->cdArtImageChanged() && !concert->cdArtImage()->isNull()) {
  754. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::ConcertCdArt)) {
  755. QString saveFileName = dataFile.saveFileName(fi.fileName());
  756. qDebug() << "Saving cd art to" << fi.absolutePath() + QDir::separator() + saveFileName;
  757. concert->cdArtImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "png", 100);
  758. }
  759. }
  760. if (concert->inSeparateFolder() && !concert->files().isEmpty()) {
  761. foreach (const QString &file, concert->extraFanartsToRemove())
  762. QFile::remove(file);
  763. QDir dir(QFileInfo(concert->files().first()).absolutePath() + "/extrafanart");
  764. if (!dir.exists() && !concert->extraFanartImagesToAdd().isEmpty())
  765. QDir(QFileInfo(concert->files().first()).absolutePath()).mkdir("extrafanart");
  766. foreach (QImage img, concert->extraFanartImagesToAdd()) {
  767. int num = 1;
  768. while (QFileInfo(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num)).exists())
  769. ++num;
  770. img.save(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num), "jpg", 100);
  771. }
  772. }
  773. }
  774. /**
  775. * @brief Loads concert infos (except images)
  776. * @param concert Concert to load
  777. * @return Loading success
  778. */
  779. bool XbmcXml::loadConcert(Concert *concert, QString initialNfoContent)
  780. {
  781. concert->clear();
  782. concert->setChanged(false);
  783. QString nfoContent;
  784. if (initialNfoContent.isEmpty()) {
  785. QString nfoFile = nfoFilePath(concert);
  786. if (nfoFile.isEmpty())
  787. return false;
  788. QFile file(nfoFile);
  789. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  790. qWarning() << "File" << nfoFile << "could not be opened for reading";
  791. return false;
  792. }
  793. nfoContent = QString::fromUtf8(file.readAll());
  794. concert->setNfoContent(nfoContent);
  795. file.close();
  796. } else {
  797. nfoContent = initialNfoContent;
  798. }
  799. QDomDocument domDoc;
  800. domDoc.setContent(nfoContent);
  801. if (!domDoc.elementsByTagName("id").isEmpty() )
  802. concert->setId(domDoc.elementsByTagName("id").at(0).toElement().text());
  803. if (!domDoc.elementsByTagName("tmdbid").isEmpty() )
  804. concert->setTmdbId(domDoc.elementsByTagName("tmdbid").at(0).toElement().text());
  805. if (!domDoc.elementsByTagName("title").isEmpty() )
  806. concert->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
  807. if (!domDoc.elementsByTagName("artist").isEmpty() )
  808. concert->setArtist(domDoc.elementsByTagName("artist").at(0).toElement().text());
  809. if (!domDoc.elementsByTagName("album").isEmpty() )
  810. concert->setAlbum(domDoc.elementsByTagName("album").at(0).toElement().text());
  811. if (!domDoc.elementsByTagName("rating").isEmpty())
  812. concert->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
  813. if (!domDoc.elementsByTagName("year").isEmpty())
  814. concert->setReleased(QDate::fromString(domDoc.elementsByTagName("year").at(0).toElement().text(), "yyyy"));
  815. if (!domDoc.elementsByTagName("plot").isEmpty())
  816. concert->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
  817. if (!domDoc.elementsByTagName("tagline").isEmpty())
  818. concert->setTagline(domDoc.elementsByTagName("tagline").at(0).toElement().text());
  819. if (!domDoc.elementsByTagName("runtime").isEmpty())
  820. concert->setRuntime(domDoc.elementsByTagName("runtime").at(0).toElement().text().toInt());
  821. if (!domDoc.elementsByTagName("mpaa").isEmpty())
  822. concert->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
  823. if (!domDoc.elementsByTagName("playcount").isEmpty())
  824. concert->setPlayCount(domDoc.elementsByTagName("playcount").at(0).toElement().text().toInt());
  825. if (!domDoc.elementsByTagName("lastplayed").isEmpty())
  826. concert->setLastPlayed(QDateTime::fromString(domDoc.elementsByTagName("lastplayed").at(0).toElement().text(), "yyyy-MM-dd HH:mm:ss"));
  827. if (!domDoc.elementsByTagName("trailer").isEmpty())
  828. concert->setTrailer(QUrl(domDoc.elementsByTagName("trailer").at(0).toElement().text()));
  829. if (!domDoc.elementsByTagName("watched").isEmpty())
  830. concert->setWatched(domDoc.elementsByTagName("watched").at(0).toElement().text() == "true" ? true : false);
  831. for (int i=0, n=domDoc.elementsByTagName("genre").size() ; i<n ; i++)
  832. concert->addGenre(domDoc.elementsByTagName("genre").at(i).toElement().text());
  833. for (int i=0, n=domDoc.elementsByTagName("tag").size() ; i<n ; i++)
  834. concert->addTag(domDoc.elementsByTagName("tag").at(i).toElement().text());
  835. for (int i=0, n=domDoc.elementsByTagName("thumb").size() ; i<n ; i++) {
  836. QString parentTag = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().tagName();
  837. if (parentTag == "movie") {
  838. Poster p;
  839. p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
  840. p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
  841. concert->addPoster(p);
  842. } else if (parentTag == "fanart") {
  843. Poster p;
  844. p.originalUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().text());
  845. p.thumbUrl = QUrl(domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
  846. concert->addBackdrop(p);
  847. }
  848. }
  849. concert->setStreamDetailsLoaded(loadStreamDetails(concert->streamDetails(), domDoc));
  850. return true;
  851. }
  852. /**
  853. * @brief Get the path to the concert poster
  854. * @param concert Concert object
  855. * @return Path to poster image
  856. */
  857. QString XbmcXml::posterImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
  858. {
  859. if (concert->files().count() == 0)
  860. return QString();
  861. if (!constructName)
  862. dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertPoster);
  863. QFileInfo fi(concert->files().at(0));
  864. QString posterFileName;
  865. foreach (DataFile dataFile, dataFiles) {
  866. QString file = dataFile.saveFileName(fi.fileName());
  867. QFileInfo pFi(fi.absolutePath() + QDir::separator() + file);
  868. if (pFi.isFile() || constructName) {
  869. posterFileName = fi.absolutePath() + QDir::separator() + file;
  870. break;
  871. }
  872. }
  873. fi.setFile(posterFileName);
  874. if (fi.isFile())
  875. return posterFileName;
  876. return QString();
  877. }
  878. /**
  879. * @brief Get the path to the concert backdrop
  880. * @param concert Concert object
  881. * @return Path to backdrop image
  882. */
  883. QString XbmcXml::backdropImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
  884. {
  885. if (concert->files().count() == 0)
  886. return QString();
  887. if (!constructName)
  888. dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertBackdrop);
  889. QFileInfo fi(concert->files().at(0));
  890. QString fanartFileName;
  891. foreach (DataFile dataFile, dataFiles) {
  892. QString file = dataFile.saveFileName(fi.fileName());
  893. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  894. if (bFi.isFile() || constructName) {
  895. fanartFileName = fi.absolutePath() + QDir::separator() + file;
  896. break;
  897. }
  898. }
  899. fi.setFile(fanartFileName);
  900. if (fi.isFile())
  901. return fanartFileName;
  902. return QString();
  903. }
  904. /**
  905. * @brief Get the path to the concert logo
  906. * @param concert Concert object
  907. * @return Path to logo image
  908. */
  909. QString XbmcXml::logoImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
  910. {
  911. QString logoFileName;
  912. if (concert->files().size() == 0) {
  913. qWarning() << "Concert has no files";
  914. return logoFileName;
  915. }
  916. QFileInfo fi(concert->files().at(0));
  917. if (!constructName)
  918. dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertLogo);
  919. foreach (DataFile dataFile, dataFiles) {
  920. QString file = dataFile.saveFileName(fi.fileName());
  921. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  922. if (bFi.isFile() || constructName) {
  923. logoFileName = fi.absolutePath() + QDir::separator() + file;
  924. break;
  925. }
  926. }
  927. return logoFileName;
  928. }
  929. /**
  930. * @brief Get the path to the concert clear art
  931. * @param concert Concert object
  932. * @return Path to clear art image
  933. */
  934. QString XbmcXml::clearArtImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
  935. {
  936. QString clearArtFileName;
  937. if (concert->files().size() == 0) {
  938. qWarning() << "Concert has no files";
  939. return clearArtFileName;
  940. }
  941. QFileInfo fi(concert->files().at(0));
  942. if (!constructName)
  943. dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertClearArt);
  944. foreach (DataFile dataFile, dataFiles) {
  945. QString file = dataFile.saveFileName(fi.fileName());
  946. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  947. if (bFi.isFile() || constructName) {
  948. clearArtFileName = fi.absolutePath() + QDir::separator() + file;
  949. break;
  950. }
  951. }
  952. return clearArtFileName;
  953. }
  954. /**
  955. * @brief Get the path to the concert cd art
  956. * @param concert Concert object
  957. * @return Path to cd art image
  958. */
  959. QString XbmcXml::cdArtImageName(Concert *concert, QList<DataFile> dataFiles, bool constructName)
  960. {
  961. QString cdArtFileName;
  962. if (concert->files().size() == 0) {
  963. qWarning() << "Concert has no files";
  964. return cdArtFileName;
  965. }
  966. QFileInfo fi(concert->files().at(0));
  967. if (!constructName)
  968. dataFiles = Settings::instance()->dataFiles(DataFileType::ConcertCdArt);
  969. foreach (DataFile dataFile, dataFiles) {
  970. QString file = dataFile.saveFileName(fi.fileName());
  971. QFileInfo bFi(fi.absolutePath() + QDir::separator() + file);
  972. if (bFi.isFile() || constructName) {
  973. cdArtFileName = fi.absolutePath() + QDir::separator() + file;
  974. break;
  975. }
  976. }
  977. return cdArtFileName;
  978. }
  979. /**
  980. * @brief Get path to poster image
  981. * @param show
  982. * @return
  983. */
  984. QString XbmcXml::posterImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  985. {
  986. if (show->dir().isEmpty())
  987. return QString();
  988. if (!constructName)
  989. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowPoster);
  990. QString posterFileName;
  991. foreach (DataFile dataFile, dataFiles) {
  992. QString loadFileName = dataFile.saveFileName("");
  993. QFileInfo posterFi(show->dir() + QDir::separator() + loadFileName);
  994. if (posterFi.isFile() || constructName) {
  995. posterFileName = show->dir() + QDir::separator() + loadFileName;
  996. break;
  997. }
  998. }
  999. QFileInfo fi(posterFileName);
  1000. if (fi.isFile())
  1001. return posterFileName;
  1002. return QString();
  1003. }
  1004. /**
  1005. * @brief Gets path to backdrop image
  1006. * @param show
  1007. * @return
  1008. */
  1009. QString XbmcXml::backdropImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  1010. {
  1011. if (show->dir().isEmpty())
  1012. return QString();
  1013. if (!constructName)
  1014. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowBackdrop);
  1015. QString posterFileName;
  1016. foreach (DataFile dataFile, dataFiles) {
  1017. QString loadFileName = dataFile.saveFileName("");
  1018. QFileInfo posterFi(show->dir() + QDir::separator() + loadFileName);
  1019. if (posterFi.isFile() || constructName) {
  1020. posterFileName = show->dir() + QDir::separator() + loadFileName;
  1021. break;
  1022. }
  1023. }
  1024. QFileInfo fi(posterFileName);
  1025. if (fi.isFile())
  1026. return posterFileName;
  1027. return QString();
  1028. }
  1029. /**
  1030. * @brief Get path to banner image
  1031. * @param show
  1032. * @return
  1033. */
  1034. QString XbmcXml::bannerImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  1035. {
  1036. if (show->dir().isEmpty())
  1037. return QString();
  1038. if (!constructName)
  1039. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowBanner);
  1040. QString bannerFileName;
  1041. foreach (DataFile dataFile, dataFiles) {
  1042. QString loadFileName = dataFile.saveFileName("");
  1043. QFileInfo bannerFi(show->dir() + QDir::separator() + loadFileName);
  1044. if (bannerFi.isFile() || constructName) {
  1045. bannerFileName = show->dir() + QDir::separator() + loadFileName;
  1046. break;
  1047. }
  1048. }
  1049. QFileInfo fi(bannerFileName);
  1050. if (fi.isFile())
  1051. return bannerFileName;
  1052. return QString();
  1053. }
  1054. /**
  1055. * @brief Get the path to the tv show logo
  1056. * @param show TV Show object
  1057. * @return Path to logo image
  1058. */
  1059. QString XbmcXml::logoImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  1060. {
  1061. QString logoFileName;
  1062. if (show->dir().isEmpty())
  1063. return logoFileName;
  1064. if (!constructName)
  1065. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowLogo);
  1066. foreach (DataFile dataFile, dataFiles) {
  1067. QString file = dataFile.saveFileName("");
  1068. QFileInfo fi(show->dir() + QDir::separator() + file);
  1069. if (fi.isFile() || constructName) {
  1070. logoFileName = show->dir() + QDir::separator() + file;
  1071. break;
  1072. }
  1073. }
  1074. return logoFileName;
  1075. }
  1076. /**
  1077. * @brief Get the path to the tv show clear art
  1078. * @param show TV Show object
  1079. * @return Path to clear art image
  1080. */
  1081. QString XbmcXml::clearArtImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  1082. {
  1083. QString clearArtFileName;
  1084. if (show->dir().isEmpty())
  1085. return clearArtFileName;
  1086. if (!constructName)
  1087. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowClearArt);
  1088. foreach (DataFile dataFile, dataFiles) {
  1089. QString file = dataFile.saveFileName("");
  1090. QFileInfo fi(show->dir() + QDir::separator() + file);
  1091. if (fi.isFile() || constructName) {
  1092. clearArtFileName = show->dir() + QDir::separator() + file;
  1093. break;
  1094. }
  1095. }
  1096. return clearArtFileName;
  1097. }
  1098. /**
  1099. * @brief Get the path to the tv show character art
  1100. * @param show TV Show object
  1101. * @return Path to character art image
  1102. */
  1103. QString XbmcXml::characterArtImageName(TvShow *show, QList<DataFile> dataFiles, bool constructName)
  1104. {
  1105. QString characterArtFileName;
  1106. if (show->dir().isEmpty())
  1107. return characterArtFileName;
  1108. if (!constructName)
  1109. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowCharacterArt);
  1110. foreach (DataFile dataFile, dataFiles) {
  1111. QString file = dataFile.saveFileName("");
  1112. QFileInfo fi(show->dir() + QDir::separator() + file);
  1113. if (fi.isFile() || constructName) {
  1114. characterArtFileName = show->dir() + QDir::separator() + file;
  1115. break;
  1116. }
  1117. }
  1118. return characterArtFileName;
  1119. }
  1120. /**
  1121. * @brief Get path to season poster
  1122. * @param show
  1123. * @param season
  1124. * @return
  1125. */
  1126. QString XbmcXml::seasonPosterImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
  1127. {
  1128. QString fileName;
  1129. if (show->dir().isEmpty())
  1130. return fileName;
  1131. if (!constructName)
  1132. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonPoster);
  1133. foreach (DataFile dataFile, dataFiles) {
  1134. QString file = dataFile.saveFileName("", season);
  1135. QFileInfo fi(show->dir() + QDir::separator() + file);
  1136. if (fi.isFile() || constructName) {
  1137. fileName = show->dir() + QDir::separator() + file;
  1138. break;
  1139. }
  1140. }
  1141. return fileName;
  1142. }
  1143. QString XbmcXml::seasonBackdropImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
  1144. {
  1145. QString fileName;
  1146. if (show->dir().isEmpty())
  1147. return fileName;
  1148. if (!constructName)
  1149. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonBackdrop);
  1150. foreach (DataFile dataFile, dataFiles) {
  1151. QString file = dataFile.saveFileName("", season);
  1152. QFileInfo fi(show->dir() + QDir::separator() + file);
  1153. if (fi.isFile() || constructName) {
  1154. fileName = show->dir() + QDir::separator() + file;
  1155. break;
  1156. }
  1157. }
  1158. return fileName;
  1159. }
  1160. QString XbmcXml::seasonBannerImageName(TvShow *show, int season, QList<DataFile> dataFiles, bool constructName)
  1161. {
  1162. QString fileName;
  1163. if (show->dir().isEmpty())
  1164. return fileName;
  1165. if (!constructName)
  1166. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowSeasonBanner);
  1167. foreach (DataFile dataFile, dataFiles) {
  1168. QString file = dataFile.saveFileName("", season);
  1169. QFileInfo fi(show->dir() + QDir::separator() + file);
  1170. if (fi.isFile() || constructName) {
  1171. fileName = show->dir() + QDir::separator() + file;
  1172. break;
  1173. }
  1174. }
  1175. return fileName;
  1176. }
  1177. /**
  1178. * @brief Get path to actor image
  1179. * @param show
  1180. * @param actor
  1181. * @return Path to actor image
  1182. */
  1183. QString XbmcXml::actorImageName(TvShow *show, Actor actor)
  1184. {
  1185. if (show->dir().isEmpty())
  1186. return QString();
  1187. QString actorName = actor.name;
  1188. actorName = actorName.replace(" ", "_");
  1189. QString fileName = show->dir() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn";
  1190. QFileInfo fi(fileName);
  1191. if (fi.isFile())
  1192. return fileName;
  1193. return QString();
  1194. }
  1195. /**
  1196. * @brief Gets path to thumbnail image
  1197. * @param episode
  1198. * @return
  1199. */
  1200. QString XbmcXml::thumbnailImageName(TvShowEpisode *episode, QList<DataFile> dataFiles, bool constructName)
  1201. {
  1202. QString fileName;
  1203. if (episode->files().size() == 0)
  1204. return fileName;
  1205. QFileInfo fi(episode->files().at(0));
  1206. if (!constructName)
  1207. dataFiles = Settings::instance()->dataFiles(DataFileType::TvShowEpisodeThumb);
  1208. foreach (DataFile dataFile, dataFiles) {
  1209. QString file = dataFile.saveFileName(fi.fileName());
  1210. QFileInfo pFi(fi.absolutePath() + QDir::separator() + file);
  1211. if (pFi.isFile() || constructName) {
  1212. fileName = fi.absolutePath() + QDir::separator() + file;
  1213. break;
  1214. }
  1215. }
  1216. return fileName;
  1217. }
  1218. /**
  1219. * @brief Loads tv show information
  1220. * @param show Show to load
  1221. * @return Loading success
  1222. */
  1223. bool XbmcXml::loadTvShow(TvShow *show, QString initialNfoContent)
  1224. {
  1225. show->clear();
  1226. show->setChanged(false);
  1227. QString nfoContent;
  1228. if (initialNfoContent.isEmpty()) {
  1229. if (show->dir().isEmpty())
  1230. return false;
  1231. QString nfoFile;
  1232. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowNfo)) {
  1233. QString file = dataFile.saveFileName("");
  1234. QFileInfo nfoFi(show->dir() + QDir::separator() + file);
  1235. if (nfoFi.exists()) {
  1236. nfoFile = show->dir() + QDir::separator() + file;
  1237. break;
  1238. }
  1239. }
  1240. QFile file(nfoFile);
  1241. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  1242. qWarning() << "Nfo file could not be opened for reading" << nfoFile;
  1243. return false;
  1244. }
  1245. nfoContent = QString::fromUtf8(file.readAll());
  1246. show->setNfoContent(nfoContent);
  1247. file.close();
  1248. } else {
  1249. nfoContent = initialNfoContent;
  1250. }
  1251. QDomDocument domDoc;
  1252. domDoc.setContent(nfoContent);
  1253. if (!domDoc.elementsByTagName("tvdbid").isEmpty() )
  1254. show->setTvdbId(domDoc.elementsByTagName("tvdbid").at(0).toElement().text());
  1255. if (!domDoc.elementsByTagName("title").isEmpty() )
  1256. show->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
  1257. if (!domDoc.elementsByTagName("showtitle").isEmpty() )
  1258. show->setShowTitle(domDoc.elementsByTagName("showtitle").at(0).toElement().text());
  1259. if (!domDoc.elementsByTagName("rating").isEmpty())
  1260. show->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
  1261. if (!domDoc.elementsByTagName("plot").isEmpty())
  1262. show->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
  1263. if (!domDoc.elementsByTagName("mpaa").isEmpty())
  1264. show->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
  1265. if (!domDoc.elementsByTagName("premiered").isEmpty())
  1266. show->setFirstAired(QDate::fromString(domDoc.elementsByTagName("premiered").at(0).toElement().text(), "yyyy-MM-dd"));
  1267. if (!domDoc.elementsByTagName("studio").isEmpty())
  1268. show->setNetwork(domDoc.elementsByTagName("studio").at(0).toElement().text());
  1269. if (!domDoc.elementsByTagName("episodeguide").isEmpty() && !domDoc.elementsByTagName("episodeguide").at(0).toElement().elementsByTagName("url").isEmpty())
  1270. show->setEpisodeGuideUrl(domDoc.elementsByTagName("episodeguide").at(0).toElement().elementsByTagName("url").at(0).toElement().text());
  1271. for (int i=0, n=domDoc.elementsByTagName("genre").size() ; i<n ; i++)
  1272. show->addGenre(domDoc.elementsByTagName("genre").at(i).toElement().text());
  1273. for (int i=0, n=domDoc.elementsByTagName("tag").size() ; i<n ; i++)
  1274. show->addTag(domDoc.elementsByTagName("tag").at(i).toElement().text());
  1275. for (int i=0, n=domDoc.elementsByTagName("actor").size() ; i<n ; i++) {
  1276. Actor a;
  1277. a.imageHasChanged = false;
  1278. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").isEmpty())
  1279. a.name = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("name").at(0).toElement().text();
  1280. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").isEmpty())
  1281. a.role = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("role").at(0).toElement().text();
  1282. if (!domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").isEmpty())
  1283. a.thumb = domDoc.elementsByTagName("actor").at(i).toElement().elementsByTagName("thumb").at(0).toElement().text();
  1284. show->addActor(a);
  1285. }
  1286. for (int i=0, n=domDoc.elementsByTagName("thumb").size() ; i<n ; i++) {
  1287. QString parentTag = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().tagName();
  1288. if (parentTag == "tvshow") {
  1289. QDomElement elem = domDoc.elementsByTagName("thumb").at(i).toElement();
  1290. Poster p;
  1291. p.originalUrl = QUrl(elem.text());
  1292. p.thumbUrl = QUrl(elem.text());
  1293. if (elem.hasAttribute("type") && elem.attribute("type") == "season") {
  1294. int season = elem.attribute("season").toInt();
  1295. if (season >= 0)
  1296. show->addSeasonPoster(season, p);
  1297. } else {
  1298. show->addPoster(p);
  1299. }
  1300. } else if (parentTag == "fanart") {
  1301. QString url = domDoc.elementsByTagName("thumb").at(i).parentNode().toElement().attribute("url");
  1302. Poster p;
  1303. p.originalUrl = QUrl(url + domDoc.elementsByTagName("thumb").at(i).toElement().text());
  1304. p.thumbUrl = QUrl(url + domDoc.elementsByTagName("thumb").at(i).toElement().attribute("preview"));
  1305. show->addBackdrop(p);
  1306. }
  1307. }
  1308. return true;
  1309. }
  1310. /**
  1311. * @brief Loads tv show episode information
  1312. * @param episode Episode to load infos for
  1313. * @return Loading success
  1314. */
  1315. bool XbmcXml::loadTvShowEpisode(TvShowEpisode *episode, QString initialNfoContent)
  1316. {
  1317. episode->clear();
  1318. episode->setChanged(false);
  1319. QString nfoContent;
  1320. if (initialNfoContent.isEmpty()) {
  1321. QString nfoFile = nfoFilePath(episode);
  1322. if (nfoFile.isEmpty())
  1323. return false;
  1324. QFile file(nfoFile);
  1325. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  1326. qWarning() << "File" << nfoFile << "could not be opened for reading";
  1327. return false;
  1328. }
  1329. nfoContent = QString::fromUtf8(file.readAll());
  1330. episode->setNfoContent(nfoContent);
  1331. file.close();
  1332. } else {
  1333. nfoContent = initialNfoContent;
  1334. }
  1335. QDomDocument domDoc;
  1336. domDoc.setContent(nfoContent);
  1337. if (!domDoc.elementsByTagName("title").isEmpty() )
  1338. episode->setName(domDoc.elementsByTagName("title").at(0).toElement().text());
  1339. if (!domDoc.elementsByTagName("showtitle").isEmpty() )
  1340. episode->setShowTitle(domDoc.elementsByTagName("showtitle").at(0).toElement().text());
  1341. if (!domDoc.elementsByTagName("season").isEmpty())
  1342. episode->setSeason(domDoc.elementsByTagName("season").at(0).toElement().text().toInt());
  1343. if (!domDoc.elementsByTagName("episode").isEmpty())
  1344. episode->setEpisode(domDoc.elementsByTagName("episode").at(0).toElement().text().toInt());
  1345. if (!domDoc.elementsByTagName("displayseason").isEmpty())
  1346. episode->setDisplaySeason(domDoc.elementsByTagName("displayseason").at(0).toElement().text().toInt());
  1347. if (!domDoc.elementsByTagName("displayepisode").isEmpty())
  1348. episode->setDisplayEpisode(domDoc.elementsByTagName("displayepisode").at(0).toElement().text().toInt());
  1349. if (!domDoc.elementsByTagName("rating").isEmpty())
  1350. episode->setRating(domDoc.elementsByTagName("rating").at(0).toElement().text().toFloat());
  1351. if (!domDoc.elementsByTagName("plot").isEmpty())
  1352. episode->setOverview(domDoc.elementsByTagName("plot").at(0).toElement().text());
  1353. if (!domDoc.elementsByTagName("mpaa").isEmpty())
  1354. episode->setCertification(domDoc.elementsByTagName("mpaa").at(0).toElement().text());
  1355. if (!domDoc.elementsByTagName("aired").isEmpty())
  1356. episode->setFirstAired(QDate::fromString(domDoc.elementsByTagName("aired").at(0).toElement().text(), "yyyy-MM-dd"));
  1357. if (!domDoc.elementsByTagName("playcount").isEmpty())
  1358. episode->setPlayCount(domDoc.elementsByTagName("playcount").at(0).toElement().text().toInt());
  1359. if (!domDoc.elementsByTagName("lastplayed").isEmpty())
  1360. episode->setLastPlayed(QDateTime::fromString(domDoc.elementsByTagName("lastplayed").at(0).toElement().text(), "yyyy-MM-dd HH:mm:ss"));
  1361. if (!domDoc.elementsByTagName("studio").isEmpty())
  1362. episode->setNetwork(domDoc.elementsByTagName("studio").at(0).toElement().text());
  1363. if (!domDoc.elementsByTagName("thumb").isEmpty())
  1364. episode->setThumbnail(QUrl(domDoc.elementsByTagName("thumb").at(0).toElement().text()));
  1365. for (int i=0, n=domDoc.elementsByTagName("credits").size() ; i<n ; i++)
  1366. episode->addWriter(domDoc.elementsByTagName("credits").at(i).toElement().text());
  1367. for (int i=0, n=domDoc.elementsByTagName("director").size() ; i<n ; i++)
  1368. episode->addDirector(domDoc.elementsByTagName("director").at(i).toElement().text());
  1369. episode->setStreamDetailsLoaded(loadStreamDetails(episode->streamDetails(), domDoc));
  1370. return true;
  1371. }
  1372. /**
  1373. * @brief Saves a tv show
  1374. * @param show Show to save
  1375. * @return Saving success
  1376. * @see XbmcXml::writeTvShowXml
  1377. */
  1378. bool XbmcXml::saveTvShow(TvShow *show)
  1379. {
  1380. QByteArray xmlContent;
  1381. QXmlStreamWriter xml(&xmlContent);
  1382. xml.setAutoFormatting(true);
  1383. xml.writeStartDocument("1.0", true);
  1384. writeTvShowXml(xml, show);
  1385. xml.writeEndDocument();
  1386. if (show->dir().isEmpty())
  1387. return false;
  1388. show->setNfoContent(xmlContent);
  1389. Manager::instance()->database()->update(show);
  1390. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowNfo)) {
  1391. QFile file(show->dir() + QDir::separator() + dataFile.saveFileName(""));
  1392. if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
  1393. qWarning() << "Nfo file could not be openend for writing" << file.fileName();
  1394. return false;
  1395. }
  1396. file.write(xmlContent);
  1397. file.close();
  1398. }
  1399. if (show->posterImageChanged() && !show->posterImage()->isNull()) {
  1400. qDebug() << "Poster image has changed";
  1401. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowPoster)) {
  1402. QString saveFileName = dataFile.saveFileName("");
  1403. qDebug() << "Saving poster to" << show->dir() + QDir::separator() + saveFileName;
  1404. show->posterImage()->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1405. }
  1406. }
  1407. if (show->backdropImageChanged() && !show->backdropImage()->isNull()) {
  1408. qDebug() << "Backdrop image has changed";
  1409. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowBackdrop)) {
  1410. QString saveFileName = dataFile.saveFileName("");
  1411. qDebug() << "Saving poster to" << show->dir() + QDir::separator() + saveFileName;
  1412. show->backdropImage()->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1413. }
  1414. }
  1415. if (show->bannerImageChanged() && !show->bannerImage()->isNull()) {
  1416. qDebug() << "Banner image has changed";
  1417. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowBanner)) {
  1418. QString saveFileName = dataFile.saveFileName("");
  1419. qDebug() << "Saving banner to" << show->dir() + QDir::separator() + saveFileName;
  1420. show->bannerImage()->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1421. }
  1422. }
  1423. foreach (const Actor &actor, show->actors()) {
  1424. if (!actor.image.isNull()) {
  1425. QDir dir;
  1426. dir.mkdir(show->dir() + QDir::separator() + ".actors");
  1427. QString actorName = actor.name;
  1428. actorName = actorName.replace(" ", "_");
  1429. actor.image.save(show->dir() + QDir::separator() + ".actors" + QDir::separator() + actorName + ".tbn", "jpg", 100);
  1430. }
  1431. }
  1432. foreach (int season, show->seasons()) {
  1433. if (show->seasonPosterImageChanged(season) && !show->seasonPosterImage(season)->isNull()) {
  1434. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowSeasonPoster)) {
  1435. QString saveFileName = dataFile.saveFileName("", season);
  1436. qDebug() << "Saving season poster for season" << season << "to" << show->dir() + QDir::separator() + saveFileName;
  1437. show->seasonPosterImage(season)->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1438. }
  1439. }
  1440. if (show->seasonBackdropImageChanged(season) && !show->seasonBackdropImage(season)->isNull()) {
  1441. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowSeasonBackdrop)) {
  1442. QString saveFileName = dataFile.saveFileName("", season);
  1443. qDebug() << "Saving season backdrop for season" << season << "to" << show->dir() + QDir::separator() + saveFileName;
  1444. show->seasonBackdropImage(season)->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1445. }
  1446. }
  1447. if (show->seasonBannerImageChanged(season) && !show->seasonBannerImage(season)->isNull()) {
  1448. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowSeasonBanner)) {
  1449. QString saveFileName = dataFile.saveFileName("", season);
  1450. qDebug() << "Saving season banner for season" << season << "to" << show->dir() + QDir::separator() + saveFileName;
  1451. show->seasonBannerImage(season)->save(show->dir() + QDir::separator() + saveFileName, "jpg", 100);
  1452. }
  1453. }
  1454. }
  1455. saveAdditionalImages(show);
  1456. return true;
  1457. }
  1458. /**
  1459. * @brief Saves additional tv show images (logo, clear art,)
  1460. * @param show TV Show object
  1461. */
  1462. void XbmcXml::saveAdditionalImages(TvShow *show)
  1463. {
  1464. if (show->logoImageChanged() && !show->logoImage()->isNull()) {
  1465. qDebug() << "Logo image has changed";
  1466. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowLogo)) {
  1467. QString saveFileName = dataFile.saveFileName("");
  1468. qDebug() << "Saving logo to" << show->dir() + QDir::separator() + saveFileName;
  1469. show->logoImage()->save(show->dir() + QDir::separator() + saveFileName, "png", 100);
  1470. }
  1471. }
  1472. if (show->clearArtImageChanged() && !show->clearArtImage()->isNull()) {
  1473. qDebug() << "Clear art image has changed";
  1474. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowClearArt)) {
  1475. QString saveFileName = dataFile.saveFileName("");
  1476. qDebug() << "Saving clear art to" << show->dir() + QDir::separator() + saveFileName;
  1477. show->clearArtImage()->save(show->dir() + QDir::separator() + saveFileName, "png", 100);
  1478. }
  1479. }
  1480. if (show->characterArtImageChanged() && !show->characterArtImage()->isNull()) {
  1481. qDebug() << "Character art image has changed";
  1482. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowCharacterArt)) {
  1483. QString saveFileName = dataFile.saveFileName("");
  1484. qDebug() << "Saving character art to" << show->dir() + QDir::separator() + saveFileName;
  1485. show->characterArtImage()->save(show->dir() + QDir::separator() + saveFileName, "png", 100);
  1486. }
  1487. }
  1488. if (!show->dir().isEmpty()) {
  1489. foreach (const QString &file, show->extraFanartsToRemove())
  1490. QFile::remove(file);
  1491. QDir dir(show->dir() + "/extrafanart");
  1492. if (!dir.exists() && !show->extraFanartImagesToAdd().isEmpty())
  1493. QDir(show->dir()).mkdir("extrafanart");
  1494. foreach (QImage img, show->extraFanartImagesToAdd()) {
  1495. int num = 1;
  1496. while (QFileInfo(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num)).exists())
  1497. ++num;
  1498. img.save(dir.absolutePath() + "/" + QString("fanart%1.jpg").arg(num), "jpg", 100);
  1499. }
  1500. }
  1501. }
  1502. /**
  1503. * @brief Saves a tv show episode
  1504. * @param episode Episode to save
  1505. * @return Saving success
  1506. * @see XbmcXml::writeTvShowEpisodeXml
  1507. */
  1508. bool XbmcXml::saveTvShowEpisode(TvShowEpisode *episode)
  1509. {
  1510. qDebug() << "Entered, episode=" << episode->name();
  1511. QByteArray xmlContent;
  1512. QXmlStreamWriter xml(&xmlContent);
  1513. xml.setAutoFormatting(true);
  1514. xml.writeStartDocument("1.0", true);
  1515. writeTvShowEpisodeXml(xml, episode);
  1516. xml.writeEndDocument();
  1517. if (episode->files().isEmpty()) {
  1518. qWarning() << "Episode has no files";
  1519. return false;
  1520. }
  1521. episode->setNfoContent(xmlContent);
  1522. Manager::instance()->database()->update(episode);
  1523. QFileInfo fi(episode->files().at(0));
  1524. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowEpisodeNfo)) {
  1525. QString saveFileName = dataFile.saveFileName(fi.fileName());
  1526. QFile file(fi.absolutePath() + QDir::separator() + saveFileName);
  1527. if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
  1528. qWarning() << "Nfo file could not be opened for writing" << saveFileName;
  1529. return false;
  1530. }
  1531. file.write(xmlContent);
  1532. file.close();
  1533. }
  1534. if (episode->thumbnailImageChanged() && !episode->thumbnailImage()->isNull()) {
  1535. foreach (DataFile dataFile, Settings::instance()->dataFiles(DataFileType::TvShowEpisodeThumb)) {
  1536. QString saveFileName = dataFile.saveFileName(fi.fileName());
  1537. qDebug() << "Thumbnail image has changed, saving to" << fi.absolutePath() + QDir::separator() + saveFileName;
  1538. episode->thumbnailImage()->save(fi.absolutePath() + QDir::separator() + saveFileName, "jpg", 100);
  1539. }
  1540. }
  1541. return true;
  1542. }
  1543. /**
  1544. * @brief Writes tv show elements to an xml stream
  1545. * @param xml XML stream
  1546. * @param show Tv show to save
  1547. */
  1548. void XbmcXml::writeTvShowXml(QXmlStreamWriter &xml, TvShow *show)
  1549. {
  1550. qDebug() << "Entered, show=" << show->name();
  1551. xml.writeStartElement("tvshow");
  1552. xml.writeTextElement("title", show->name());
  1553. xml.writeTextElement("showtitle", show->showTitle());
  1554. xml.writeTextElement("rating", QString("%1").arg(show->rating()));
  1555. xml.writeTextElement("episode", QString("%1").arg(show->episodes().count()));
  1556. xml.writeTextElement("plot", show->overview());
  1557. xml.writeTextElement("outline", show->overview());
  1558. xml.writeTextElement("mpaa", QString("%1").arg(show->certification()));
  1559. xml.writeTextElement("premiered", show->firstAired().toString("yyyy-MM-dd"));
  1560. xml.writeTextElement("studio", show->network());
  1561. xml.writeTextElement("tvdbid", show->tvdbId());
  1562. xml.writeTextElement("id", show->tvdbId());
  1563. if (!show->episodeGuideUrl().isEmpty()) {
  1564. xml.writeStartElement("episodeguide");
  1565. xml.writeTextElement("url", show->episodeGuideUrl());
  1566. xml.writeEndElement();
  1567. }
  1568. foreach (const QString &genre, show->genres())
  1569. xml.writeTextElement("genre", genre);
  1570. foreach (const QString &tag, show->tags())
  1571. xml.writeTextElement("tag", tag);
  1572. foreach (const Actor &actor, show->actors()) {
  1573. xml.writeStartElement("actor");
  1574. xml.writeTextElement("name", actor.name);
  1575. xml.writeTextElement("role", actor.role);
  1576. xml.writeTextElement("thumb", actor.thumb);
  1577. xml.writeEndElement();
  1578. }
  1579. foreach (const Poster &poster, show->posters()) {
  1580. xml.writeStartElement("thumb");
  1581. xml.writeCharacters(poster.originalUrl.toString());
  1582. xml.writeEndElement();
  1583. xml.writeStartElement("thumb");
  1584. xml.writeAttribute("type", "season");
  1585. xml.writeAttribute("season", "-1");
  1586. xml.writeCharacters(poster.originalUrl.toString());
  1587. xml.writeEndElement();
  1588. }
  1589. xml.writeStartElement("fanart");
  1590. foreach (const Poster &poster, show->backdrops()) {
  1591. xml.writeStartElement("thumb");
  1592. xml.writeAttribute("preview", poster.thumbUrl.toString());
  1593. xml.writeCharacters(poster.originalUrl.toString());
  1594. xml.writeEndElement();
  1595. }
  1596. xml.writeEndElement();
  1597. foreach (int season, show->seasons()) {
  1598. foreach (const Poster &poster, show->seasonPosters(season)) {
  1599. xml.writeStartElement("thumb");
  1600. xml.writeAttribute("type", "season");
  1601. xml.writeAttribute("season", QString("%1").arg(season));
  1602. xml.writeCharacters(poster.originalUrl.toString());
  1603. xml.writeEndElement();
  1604. }
  1605. }
  1606. xml.writeEndElement();
  1607. }
  1608. /**
  1609. * @brief Writes tv show episode elements to an xml stream
  1610. * @param xml XML stream
  1611. * @param episode Episode to save
  1612. */
  1613. void XbmcXml::writeTvShowEpisodeXml(QXmlStreamWriter &xml, TvShowEpisode *episode)
  1614. {
  1615. qDebug() << "Entered, episode=" << episode->name();
  1616. xml.writeStartElement("episodedetails");
  1617. xml.writeTextElement("title", episode->name());
  1618. xml.writeTextElement("showtitle", episode->showTitle());
  1619. xml.writeTextElement("rating", QString("%1").arg(episode->rating()));
  1620. xml.writeTextElement("season", QString("%1").arg(episode->season()));
  1621. xml.writeTextElement("episode", QString("%1").arg(episode->episode()));
  1622. if (episode->displaySeason() > -1)
  1623. xml.writeTextElement("displayseason", QString("%1").arg(episode->displaySeason()));
  1624. if (episode->displayEpisode() > -1)
  1625. xml.writeTextElement("displayepisode", QString("%1").arg(episode->displayEpisode()));
  1626. xml.writeTextElement("plot", episode->overview());
  1627. xml.writeTextElement("outline", episode->overview());
  1628. xml.writeTextElement("mpaa", episode->certification());
  1629. xml.writeTextElement("playcount", QString("%1").arg(episode->playCount()));
  1630. xml.writeTextElement("lastplayed", episode->lastPlayed().toString("yyyy-MM-dd HH:mm:ss"));
  1631. xml.writeTextElement("aired", episode->firstAired().toString("yyyy-MM-dd"));
  1632. xml.writeTextElement("studio", episode->network());
  1633. foreach (const QString &writer, episode->writers())
  1634. xml.writeTextElement("credits", writer);
  1635. foreach (const QString &director, episode->directors())
  1636. xml.writeTextElement("director", director);
  1637. if (!episode->thumbnail().isEmpty())
  1638. xml.writeTextElement("thumb", episode->thumbnail().toString());
  1639. if (episode->tvShow() != 0) {
  1640. foreach (const Actor &actor, episode->tvShow()->actors()) {
  1641. xml.writeStartElement("actor");
  1642. xml.writeTextElement("name", actor.name);
  1643. xml.writeTextElement("role", actor.role);
  1644. xml.writeTextElement("thumb", actor.thumb);
  1645. xml.writeEndElement();
  1646. }
  1647. }
  1648. xml.writeStartElement("fileinfo");
  1649. writeStreamDetails(xml, episode->streamDetails());
  1650. xml.writeEndElement();
  1651. xml.writeEndElement();
  1652. }
  1653. QStringList XbmcXml::extraFanartNames(Movie *movie)
  1654. {
  1655. if (movie->files().isEmpty() || !movie->inSeparateFolder())
  1656. return QStringList();
  1657. QFileInfo fi(movie->files().first());
  1658. QDir dir(fi.absolutePath() + "/extrafanart");
  1659. QStringList files;
  1660. QStringList filters = QStringList() << "*.jpg" << "*.jpeg" << "*.JPEG" << "*.Jpeg" << "*.JPeg";
  1661. foreach (const QString &file, dir.entryList(filters, QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
  1662. files << QDir::toNativeSeparators(dir.path() + "/" + file);
  1663. return files;
  1664. }
  1665. QStringList XbmcXml::extraFanartNames(Concert *concert)
  1666. {
  1667. if (concert->files().isEmpty() || !concert->inSeparateFolder())
  1668. return QStringList();
  1669. QFileInfo fi(concert->files().first());
  1670. QDir dir(fi.absolutePath() + "/extrafanart");
  1671. QStringList files;
  1672. QStringList filters = QStringList() << "*.jpg" << "*.jpeg" << "*.JPEG" << "*.Jpeg" << "*.JPeg";
  1673. foreach (const QString &file, dir.entryList(filters, QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
  1674. files << QDir::toNativeSeparators(dir.path() + "/" + file);
  1675. return files;
  1676. }
  1677. QStringList XbmcXml::extraFanartNames(TvShow *show)
  1678. {
  1679. if (show->dir().isEmpty())
  1680. return QStringList();
  1681. QDir dir(show->dir() + "/extrafanart");
  1682. QStringList files;
  1683. QStringList filters = QStringList() << "*.jpg" << "*.jpeg" << "*.JPEG" << "*.Jpeg" << "*.JPeg";
  1684. foreach (const QString &file, dir.entryList(filters, QDir::Files | QDir::NoDotAndDotDot, QDir::Name))
  1685. files << QDir::toNativeSeparators(dir.path() + "/" + file);
  1686. return files;
  1687. }
  1688. /**
  1689. * @brief Loading of movie set posters is not possible with nfos
  1690. * @param setName
  1691. * @return
  1692. * @todo: Find a solution for loading movie set poster
  1693. */
  1694. QImage XbmcXml::movieSetPoster(QString setName)
  1695. {
  1696. return QImage();
  1697. foreach (Movie *movie, Manager::instance()->movieModel()->movies()) {
  1698. if (movie->set() == setName) {
  1699. if (movie->files().isEmpty())
  1700. continue;
  1701. QFileInfo fi(movie->files().first());
  1702. return QImage(fi.absolutePath() + "/movieset-poster.jpg");
  1703. }
  1704. }
  1705. return QImage();
  1706. }
  1707. /**
  1708. * @brief Loading of movie set backdrops is not possible with nfos
  1709. * @param setName
  1710. * @return
  1711. * @todo: Find a solution for loading movie set fanart
  1712. */
  1713. QImage XbmcXml::movieSetBackdrop(QString setName)
  1714. {
  1715. return QImage();
  1716. foreach (Movie *movie, Manager::instance()->movieModel()->movies()) {
  1717. if (movie->set() == setName) {
  1718. if (movie->files().isEmpty())
  1719. continue;
  1720. QFileInfo fi(movie->files().first());
  1721. return QImage(fi.absolutePath() + "/movieset-fanart.jpg");
  1722. }
  1723. }
  1724. return QImage();
  1725. }
  1726. /**
  1727. * @brief Save movie set poster
  1728. * @param setName
  1729. * @param poster
  1730. * @todo: Find a solution for storing movie set poster
  1731. */
  1732. void XbmcXml::saveMovieSetPoster(QString setName, QImage poster)
  1733. {
  1734. return;
  1735. foreach (Movie *movie, Manager::instance()->movieModel()->movies()) {
  1736. if (movie->set() == setName) {
  1737. if (movie->files().isEmpty())
  1738. continue;
  1739. QFileInfo fi(movie->files().first());
  1740. poster.save(fi.absolutePath() + "/movieset-poster.jpg", "jpg", 100);
  1741. }
  1742. }
  1743. }
  1744. /**
  1745. * @brief Save movie set backdrop
  1746. * @param setName
  1747. * @param backdrop
  1748. * @todo: Find a solution for storing movie set fanart
  1749. */
  1750. void XbmcXml::saveMovieSetBackdrop(QString setName, QImage backdrop)
  1751. {
  1752. return;
  1753. foreach (Movie *movie, Manager::instance()->movieModel()->movies()) {
  1754. if (movie->set() == setName) {
  1755. if (movie->files().isEmpty())
  1756. continue;
  1757. QFileInfo fi(movie->files().first());
  1758. backdrop.save(fi.absolutePath() + "/movieset-fanart.jpg", "jpg", 100);
  1759. }
  1760. }
  1761. }