00001 #include <QApplication>
00002 #include <QList>
00003 #include <QUrl>
00004
00005 #include "mythcontext.h"
00006 #include "videoutils.h"
00007 #include "mythlogging.h"
00008 #include "compat.h"
00009 #include "remoteutil.h"
00010
00011
00012 #include "metadatadownload.h"
00013 #include "metadataimagedownload.h"
00014
00015
00016 #include "videometadatalistmanager.h"
00017 #include "globals.h"
00018
00019
00020 #include "videometadata.h"
00021 #include "programinfo.h"
00022 #include "recordingrule.h"
00023
00024 #include "metadatafactory.h"
00025
00026 QEvent::Type MetadataFactoryNoResult::kEventType =
00027 (QEvent::Type) QEvent::registerEventType();
00028
00029 QEvent::Type MetadataFactorySingleResult::kEventType =
00030 (QEvent::Type) QEvent::registerEventType();
00031
00032 QEvent::Type MetadataFactoryMultiResult::kEventType =
00033 (QEvent::Type) QEvent::registerEventType();
00034
00035 QEvent::Type MetadataFactoryVideoChanges::kEventType =
00036 (QEvent::Type) QEvent::registerEventType();
00037
00038 MetadataFactory::MetadataFactory(QObject *parent) :
00039 m_parent(parent), m_scanning(false),
00040 m_returnList(), m_sync(false)
00041 {
00042 m_lookupthread = new MetadataDownload(this);
00043 m_imagedownload = new MetadataImageDownload(this);
00044 m_videoscanner = new VideoScannerThread(this);
00045
00046 m_mlm = new VideoMetadataListManager();
00047 }
00048
00049 MetadataFactory::~MetadataFactory()
00050 {
00051 if (m_lookupthread)
00052 {
00053 m_lookupthread->cancel();
00054 delete m_lookupthread;
00055 m_lookupthread = NULL;
00056 }
00057
00058 if (m_imagedownload)
00059 {
00060 m_imagedownload->cancel();
00061 delete m_imagedownload;
00062 m_imagedownload = NULL;
00063 }
00064
00065 if (m_videoscanner && m_videoscanner->wait())
00066 delete m_videoscanner;
00067 }
00068
00069 void MetadataFactory::Lookup(RecordingRule *recrule, bool automatic,
00070 bool getimages, bool allowgeneric)
00071 {
00072 if (!recrule)
00073 return;
00074
00075 MetadataLookup *lookup = new MetadataLookup();
00076 lookup->SetStep(kLookupSearch);
00077 lookup->SetType(kMetadataRecording);
00078 lookup->SetSubtype(GuessLookupType(recrule));
00079 lookup->SetData(qVariantFromValue(recrule));
00080 lookup->SetAutomatic(automatic);
00081 lookup->SetHandleImages(getimages);
00082 lookup->SetAllowGeneric(allowgeneric);
00083 lookup->SetHost(gCoreContext->GetMasterHostName());
00084 lookup->SetTitle(recrule->m_title);
00085 lookup->SetSubtitle(recrule->m_subtitle);
00086 lookup->SetInetref(recrule->m_inetref);
00087 lookup->SetSeason(recrule->m_season);
00088 lookup->SetEpisode(recrule->m_episode);
00089
00090 if (m_lookupthread->isRunning())
00091 m_lookupthread->prependLookup(lookup);
00092 else
00093 m_lookupthread->addLookup(lookup);
00094 }
00095
00096 void MetadataFactory::Lookup(ProgramInfo *pginfo, bool automatic,
00097 bool getimages, bool allowgeneric)
00098 {
00099 if (!pginfo)
00100 return;
00101
00102 MetadataLookup *lookup = new MetadataLookup();
00103 lookup->SetStep(kLookupSearch);
00104 lookup->SetType(kMetadataRecording);
00105 lookup->SetSubtype(GuessLookupType(pginfo));
00106 lookup->SetData(qVariantFromValue(pginfo));
00107 lookup->SetAutomatic(automatic);
00108 lookup->SetHandleImages(getimages);
00109 lookup->SetAllowGeneric(allowgeneric);
00110 lookup->SetHost(gCoreContext->GetMasterHostName());
00111 lookup->SetTitle(pginfo->GetTitle());
00112 lookup->SetSubtitle(pginfo->GetSubtitle());
00113 lookup->SetSeason(pginfo->GetSeason());
00114 lookup->SetEpisode(pginfo->GetEpisode());
00115 lookup->SetInetref(pginfo->GetInetRef());
00116
00117 if (m_lookupthread->isRunning())
00118 m_lookupthread->prependLookup(lookup);
00119 else
00120 m_lookupthread->addLookup(lookup);
00121 }
00122
00123 void MetadataFactory::Lookup(VideoMetadata *metadata, bool automatic,
00124 bool getimages, bool allowgeneric)
00125 {
00126 if (!metadata)
00127 return;
00128
00129 MetadataLookup *lookup = new MetadataLookup();
00130 lookup->SetStep(kLookupSearch);
00131 lookup->SetType(kMetadataVideo);
00132 if (metadata->GetSeason() > 0 || metadata->GetEpisode() > 0)
00133 lookup->SetSubtype(kProbableTelevision);
00134 else if (metadata->GetSubtitle().isEmpty())
00135 lookup->SetSubtype(kProbableMovie);
00136 else
00137 lookup->SetSubtype(kUnknownVideo);
00138 lookup->SetData(qVariantFromValue(metadata));
00139 lookup->SetAutomatic(automatic);
00140 lookup->SetHandleImages(getimages);
00141 lookup->SetAllowGeneric(allowgeneric);
00142 lookup->SetHost(metadata->GetHost());
00143 lookup->SetTitle(metadata->GetTitle());
00144 lookup->SetSubtitle(metadata->GetSubtitle());
00145 lookup->SetSeason(metadata->GetSeason());
00146 lookup->SetEpisode(metadata->GetEpisode());
00147 lookup->SetInetref(metadata->GetInetRef());
00148 QString fntmp;
00149 if (metadata->GetHost().isEmpty())
00150 fntmp = metadata->GetFilename();
00151 else
00152 fntmp = generate_file_url("Videos", metadata->GetHost(),
00153 metadata->GetFilename());
00154 lookup->SetFilename(fntmp);
00155
00156 if (m_lookupthread->isRunning())
00157 m_lookupthread->prependLookup(lookup);
00158 else
00159 m_lookupthread->addLookup(lookup);
00160 }
00161
00162 void MetadataFactory::Lookup(MetadataLookup *lookup)
00163 {
00164 if (!lookup)
00165 return;
00166
00167 if (m_lookupthread->isRunning())
00168 m_lookupthread->prependLookup(lookup);
00169 else
00170 m_lookupthread->addLookup(lookup);
00171 }
00172
00173 META_PUBLIC MetadataLookupList MetadataFactory::SynchronousLookup(QString title,
00174 QString subtitle,
00175 QString inetref,
00176 int season,
00177 int episode,
00178 QString grabber,
00179 bool allowgeneric)
00180 {
00181 MetadataLookup *lookup = new MetadataLookup();
00182 lookup->SetStep(kLookupSearch);
00183 lookup->SetType(kMetadataRecording);
00184 lookup->SetAutomatic(false);
00185 lookup->SetHandleImages(false);
00186 lookup->SetAllowGeneric(allowgeneric);
00187 lookup->SetTitle(title);
00188 lookup->SetSubtitle(subtitle);
00189 lookup->SetSeason(season);
00190 lookup->SetEpisode(episode);
00191 lookup->SetInetref(inetref);
00192 if (grabber.toLower() == "movie")
00193 lookup->SetSubtype(kProbableMovie);
00194 else if (grabber.toLower() == "tv" ||
00195 grabber.toLower() == "television")
00196 lookup->SetSubtype(kProbableTelevision);
00197 else
00198 lookup->SetSubtype(GuessLookupType(lookup));
00199
00200 return SynchronousLookup(lookup);
00201 }
00202
00203 MetadataLookupList MetadataFactory::SynchronousLookup(MetadataLookup *lookup)
00204 {
00205 if (!lookup)
00206 return MetadataLookupList();
00207
00208 m_sync = true;
00209
00210 if (m_lookupthread->isRunning())
00211 m_lookupthread->prependLookup(lookup);
00212 else
00213 m_lookupthread->addLookup(lookup);
00214
00215 while (m_returnList.isEmpty() && m_sync)
00216 {
00217 sleep(1);
00218 qApp->processEvents();
00219 }
00220
00221 m_sync = false;
00222
00223 delete lookup;
00224
00225 return m_returnList;
00226 }
00227
00228 bool MetadataFactory::VideoGrabbersFunctional()
00229 {
00230 return (m_lookupthread->MovieGrabberWorks() &&
00231 m_lookupthread->TelevisionGrabberWorks());
00232 }
00233
00234 void MetadataFactory::VideoScan()
00235 {
00236 if (IsRunning())
00237 return;
00238
00239 QStringList hosts;
00240 if (!RemoteGetActiveBackends(&hosts))
00241 {
00242 LOG(VB_GENERAL, LOG_WARNING, "Could not retrieve list of "
00243 "available backends.");
00244 return;
00245 }
00246
00247 VideoScan(hosts);
00248 }
00249
00250 void MetadataFactory::VideoScan(QStringList hosts)
00251 {
00252 if (IsRunning())
00253 return;
00254
00255 m_scanning = true;
00256
00257 m_videoscanner->SetHosts(hosts);
00258 m_videoscanner->SetDirs(GetVideoDirs());
00259 m_videoscanner->start();
00260 }
00261
00262 void MetadataFactory::OnMultiResult(MetadataLookupList list)
00263 {
00264 if (!list.size())
00265 return;
00266
00267 if (m_parent)
00268 QCoreApplication::postEvent(m_parent,
00269 new MetadataFactoryMultiResult(list));
00270 }
00271
00272 void MetadataFactory::OnSingleResult(MetadataLookup *lookup)
00273 {
00274 if (!lookup)
00275 return;
00276
00277 if (lookup->GetHandleImages())
00278 {
00279 DownloadMap map;
00280
00281 ArtworkList coverartlist = lookup->GetArtwork(kArtworkCoverart);
00282 if (coverartlist.size())
00283 {
00284 ArtworkInfo info;
00285 info.url = coverartlist.takeLast().url;
00286 map.insert(kArtworkCoverart, info);
00287 }
00288
00289 ArtworkList fanartlist = lookup->GetArtwork(kArtworkFanart);
00290 if (fanartlist.size())
00291 {
00292 ArtworkInfo info;
00293 int index = 0;
00294 int season = (int)lookup->GetSeason();
00295 if (season > 0 && season <= fanartlist.count())
00296 index = season - 1;
00297 info.url = fanartlist.takeAt(index).url;
00298 map.insert(kArtworkFanart, info);
00299 }
00300
00301 ArtworkList bannerlist = lookup->GetArtwork(kArtworkBanner);
00302 if (bannerlist.size())
00303 {
00304 ArtworkInfo info;
00305 info.url = bannerlist.takeLast().url;
00306 map.insert(kArtworkBanner, info);
00307 }
00308
00309 if (!lookup->GetType() == kMetadataRecording)
00310 {
00311 ArtworkList screenshotlist = lookup->GetArtwork(kArtworkScreenshot);
00312 if (screenshotlist.size())
00313 {
00314 ArtworkInfo info;
00315 info.url = screenshotlist.takeLast().url;
00316 map.insert(kArtworkScreenshot, info);
00317 }
00318 }
00319 lookup->SetDownloads(map);
00320 m_imagedownload->addDownloads(lookup);
00321 }
00322 else
00323 {
00324 if (m_scanning)
00325 OnVideoResult(lookup);
00326 else if (m_parent)
00327 QCoreApplication::postEvent(m_parent,
00328 new MetadataFactorySingleResult(lookup));
00329 }
00330 }
00331
00332 void MetadataFactory::OnNoResult(MetadataLookup *lookup)
00333 {
00334 if (!lookup)
00335 return;
00336
00337 if (m_parent)
00338 QCoreApplication::postEvent(m_parent,
00339 new MetadataFactoryNoResult(lookup));
00340 }
00341
00342 void MetadataFactory::OnImageResult(MetadataLookup *lookup)
00343 {
00344 if (!lookup)
00345 return;
00346
00347 if (m_parent)
00348 QCoreApplication::postEvent(m_parent,
00349 new MetadataFactorySingleResult(lookup));
00350 }
00351
00352 void MetadataFactory::OnVideoResult(MetadataLookup *lookup)
00353 {
00354 if (!lookup)
00355 return;
00356
00357 VideoMetadata *metadata = qVariantValue<VideoMetadata *>(lookup->GetData());
00358
00359 if (!metadata)
00360 return;
00361
00362 metadata->SetTitle(lookup->GetTitle());
00363 metadata->SetSubtitle(lookup->GetSubtitle());
00364
00365 if (metadata->GetTagline().isEmpty())
00366 metadata->SetTagline(lookup->GetTagline());
00367 if (metadata->GetYear() == 1895 || metadata->GetYear() == 0)
00368 metadata->SetYear(lookup->GetYear());
00369 if (metadata->GetReleaseDate() == QDate())
00370 metadata->SetReleaseDate(lookup->GetReleaseDate());
00371 if (metadata->GetDirector() == VIDEO_DIRECTOR_UNKNOWN ||
00372 metadata->GetDirector().isEmpty())
00373 {
00374 QList<PersonInfo> director = lookup->GetPeople(kPersonDirector);
00375 if (director.count() > 0)
00376 metadata->SetDirector(director.takeFirst().name);
00377 }
00378 if (metadata->GetStudio().isEmpty())
00379 {
00380 QStringList studios = lookup->GetStudios();
00381 if (studios.count() > 0)
00382 metadata->SetStudio(studios.takeFirst());
00383 }
00384 if (metadata->GetPlot() == VIDEO_PLOT_DEFAULT ||
00385 metadata->GetPlot().isEmpty())
00386 metadata->SetPlot(lookup->GetDescription());
00387 if (metadata->GetUserRating() == 0)
00388 metadata->SetUserRating(lookup->GetUserRating());
00389 if (metadata->GetRating() == VIDEO_RATING_DEFAULT)
00390 metadata->SetRating(lookup->GetCertification());
00391 if (metadata->GetLength() == 0)
00392 metadata->SetLength(lookup->GetRuntime());
00393 if (metadata->GetSeason() == 0)
00394 metadata->SetSeason(lookup->GetSeason());
00395 if (metadata->GetEpisode() == 0)
00396 metadata->SetEpisode(lookup->GetEpisode());
00397 if (metadata->GetHomepage().isEmpty())
00398 metadata->SetHomepage(lookup->GetHomepage());
00399
00400 metadata->SetInetRef(lookup->GetInetref());
00401
00402
00403
00404
00405 QList<PersonInfo> actors = lookup->GetPeople(kPersonActor);
00406 QList<PersonInfo> gueststars = lookup->GetPeople(kPersonGuestStar);
00407
00408 for (QList<PersonInfo>::const_iterator p = gueststars.begin();
00409 p != gueststars.end(); ++p)
00410 {
00411 actors.append(*p);
00412 }
00413
00414 VideoMetadata::cast_list cast;
00415 QStringList cl;
00416
00417 for (QList<PersonInfo>::const_iterator p = actors.begin();
00418 p != actors.end(); ++p)
00419 {
00420 cl.append((*p).name);
00421 }
00422
00423 for (QStringList::const_iterator p = cl.begin();
00424 p != cl.end(); ++p)
00425 {
00426 QString cn = (*p).trimmed();
00427 if (cn.length())
00428 {
00429 cast.push_back(VideoMetadata::cast_list::
00430 value_type(-1, cn));
00431 }
00432 }
00433
00434 metadata->SetCast(cast);
00435
00436
00437 VideoMetadata::genre_list video_genres;
00438 QStringList genres = lookup->GetCategories();
00439
00440 for (QStringList::const_iterator p = genres.begin();
00441 p != genres.end(); ++p)
00442 {
00443 QString genre_name = (*p).trimmed();
00444 if (genre_name.length())
00445 {
00446 video_genres.push_back(
00447 VideoMetadata::genre_list::value_type(-1, genre_name));
00448 }
00449 }
00450
00451 metadata->SetGenres(video_genres);
00452
00453
00454 VideoMetadata::country_list video_countries;
00455 QStringList countries = lookup->GetCountries();
00456
00457 for (QStringList::const_iterator p = countries.begin();
00458 p != countries.end(); ++p)
00459 {
00460 QString country_name = (*p).trimmed();
00461 if (country_name.length())
00462 {
00463 video_countries.push_back(
00464 VideoMetadata::country_list::value_type(-1,
00465 country_name));
00466 }
00467 }
00468
00469 metadata->SetCountries(video_countries);
00470
00471 ArtworkMap map = lookup->GetDownloads();
00472
00473 QUrl coverurl(map.value(kArtworkCoverart).url);
00474 if (!coverurl.path().isEmpty())
00475 metadata->SetCoverFile(coverurl.path().remove(0,1));
00476
00477 QUrl fanarturl(map.value(kArtworkFanart).url);
00478 if (!fanarturl.path().isEmpty())
00479 metadata->SetFanart(fanarturl.path().remove(0,1));
00480
00481 QUrl bannerurl(map.value(kArtworkBanner).url);
00482 if (!bannerurl.path().isEmpty())
00483 metadata->SetBanner(bannerurl.path().remove(0,1));
00484
00485 QUrl sshoturl(map.value(kArtworkScreenshot).url);
00486 if (!sshoturl.path().isEmpty())
00487 metadata->SetScreenshot(sshoturl.path().remove(0,1));
00488
00489 metadata->SetProcessed(true);
00490 metadata->UpdateDatabase();
00491
00492 if (gCoreContext->HasGUI() && m_parent)
00493 {
00494 QCoreApplication::postEvent(m_parent,
00495 new MetadataFactorySingleResult(lookup));
00496 }
00497 else
00498 {
00499 lookup->deleteLater();
00500 }
00501 }
00502
00503 void MetadataFactory::customEvent(QEvent *levent)
00504 {
00505 if (levent->type() == MetadataLookupEvent::kEventType)
00506 {
00507 MetadataLookupEvent *lue = (MetadataLookupEvent *)levent;
00508
00509 MetadataLookupList lul = lue->lookupList;
00510
00511 if (lul.isEmpty())
00512 return;
00513
00514 if (m_sync)
00515 {
00516 m_returnList = lul;
00517 }
00518 else if (lul.count() == 1)
00519 {
00520 OnSingleResult(lul.takeFirst());
00521 }
00522 else
00523 {
00524 OnMultiResult(lul);
00525 }
00526 }
00527 else if (levent->type() == MetadataLookupFailure::kEventType)
00528 {
00529 MetadataLookupFailure *luf = (MetadataLookupFailure *)levent;
00530
00531 MetadataLookupList lul = luf->lookupList;
00532
00533 if (lul.isEmpty())
00534 return;
00535
00536 if (m_sync)
00537 {
00538 m_returnList = MetadataLookupList();
00539 m_sync = false;
00540 }
00541 if (lul.size())
00542 {
00543 OnNoResult(lul.takeFirst());
00544 }
00545 }
00546 else if (levent->type() == ImageDLEvent::kEventType)
00547 {
00548 ImageDLEvent *ide = (ImageDLEvent *)levent;
00549
00550 MetadataLookup *lookup = ide->item;
00551
00552 if (!lookup)
00553 return;
00554
00555 if (m_scanning)
00556 OnVideoResult(lookup);
00557 else
00558 OnImageResult(lookup);
00559 }
00560 else if (levent->type() == VideoScanChanges::kEventType)
00561 {
00562 VideoScanChanges *vsc = (VideoScanChanges *)levent;
00563
00564 if (!vsc)
00565 return;
00566
00567 QList<int> additions = vsc->additions;
00568 QList<int> moves = vsc->moved;
00569 QList<int> deletions = vsc->deleted;
00570
00571 if (!m_scanning)
00572 {
00573 LOG(VB_GENERAL, LOG_INFO,
00574 QString("Video Scan Complete: a(%1) m(%2) d(%3)")
00575 .arg(additions.count()).arg(moves.count())
00576 .arg(deletions.count()));
00577
00578 if (m_parent)
00579 QCoreApplication::postEvent(m_parent,
00580 new MetadataFactoryVideoChanges(additions, moves,
00581 deletions));
00582 }
00583 else
00584 {
00585 LOG(VB_GENERAL, LOG_INFO,
00586 QString("Video Scan Complete: a(%1) m(%2) d(%3)")
00587 .arg(additions.count()).arg(moves.count())
00588 .arg(deletions.count()));
00589
00590 VideoMetadataListManager::metadata_list ml;
00591 VideoMetadataListManager::loadAllFromDatabase(ml);
00592 m_mlm->setList(ml);
00593
00594 for (QList<int>::const_iterator it = additions.begin();
00595 it != additions.end(); ++it)
00596 {
00597 VideoMetadata *metadata = m_mlm->byID(*it).get();
00598
00599 if (metadata)
00600 Lookup(metadata, true, true);
00601 }
00602 }
00603 m_videoscanner->ResetCounts();
00604 }
00605 }
00606
00607
00608
00609
00610 LookupType GuessLookupType(ProgramInfo *pginfo)
00611 {
00612 LookupType ret = kUnknownVideo;
00613
00614 QString catType = pginfo->GetCategoryType();
00615 if (catType.isEmpty())
00616 catType = pginfo->QueryCategoryType();
00617
00618 if ((!pginfo->GetSubtitle().isEmpty() || pginfo->GetEpisode() > 0) &&
00619 (catType == "series" || catType == "tvshow" ||
00620 catType == "show"))
00621 ret = kProbableTelevision;
00622 else if (catType == "movie" || catType == "film")
00623 ret = kProbableMovie;
00624 else if (pginfo->GetSeason() > 0 || pginfo->GetEpisode() > 0 ||
00625 !pginfo->GetSubtitle().isEmpty())
00626 ret = kProbableTelevision;
00627 else
00628 {
00629
00630
00631
00632
00633
00634
00635
00636
00637 RecordingRule *rule = new RecordingRule();
00638 rule->m_recordID = pginfo->GetRecordingRuleID();
00639 rule->Load();
00640 int ruleepisode = rule->m_episode;
00641 delete rule;
00642
00643 if (ruleepisode == 0 && pginfo->GetEpisode() == 0 &&
00644 pginfo->GetSubtitle().isEmpty())
00645 ret = kProbableMovie;
00646 else if (ruleepisode > 0 && pginfo->GetSubtitle().isEmpty())
00647 ret = kProbableGenericTelevision;
00648 else
00649 ret = kUnknownVideo;
00650 }
00651
00652 return ret;
00653 }
00654
00655 LookupType GuessLookupType(MetadataLookup *lookup)
00656 {
00657 LookupType ret = kUnknownVideo;
00658
00659 if (lookup->GetSeason() > 0 || lookup->GetEpisode() > 0 ||
00660 !lookup->GetSubtitle().isEmpty())
00661 ret = kProbableTelevision;
00662 else
00663 ret = kProbableMovie;
00664
00665 return ret;
00666 }
00667
00668
00669 LookupType GuessLookupType(RecordingRule *recrule)
00670 {
00671 LookupType ret = kUnknownVideo;
00672
00673 if (recrule->m_season > 0 || recrule->m_episode > 0 ||
00674 !recrule->m_subtitle.isEmpty())
00675 ret = kProbableTelevision;
00676 else
00677 ret = kProbableMovie;
00678
00679 return ret;
00680 }
00681