MythTV master
v2video.cpp
Go to the documentation of this file.
1// MythTV
9#include "libmythbase/mythversion.h"
17
18// MythBackend
19#include "v2artworkInfoList.h"
20#include "v2castMemberList.h"
21#include "v2genreList.h"
22#include "v2serviceUtil.h"
23#include "v2video.h"
24
25
26// This will be initialised in a thread safe manner on first use
28 (VIDEO_HANDLE, V2Video::staticMetaObject, &V2Video::RegisterCustomTypes))
29
31{
32 qRegisterMetaType<V2VideoMetadataInfo*>("V2VideoMetadataInfo");
33 qRegisterMetaType<V2VideoMetadataInfoList*>("V2VideoMetadataInfoList");
34 qRegisterMetaType<V2VideoLookupList*>("V2VideoLookupList");
35 qRegisterMetaType<V2BlurayInfo*>("V2BlurayInfo");
36 qRegisterMetaType<V2VideoStreamInfoList*>("V2VideoStreamInfoList");
37 qRegisterMetaType<V2VideoStreamInfo*>("V2VideoStreamInfo");
38 qRegisterMetaType<V2ArtworkInfoList*>("V2ArtworkInfoList");
39 qRegisterMetaType<V2ArtworkInfo*>("V2ArtworkInfo");
40 qRegisterMetaType<V2CastMemberList*>("V2CastMemberList");
41 qRegisterMetaType<V2CastMember*>("V2CastMember");
42 qRegisterMetaType<V2GenreList*>("V2GenreList");
43 qRegisterMetaType<V2Genre*>("V2Genre");
44 qRegisterMetaType<V2VideoLookupList*>("V2VideoLookupList");
45 qRegisterMetaType<V2VideoLookup*>("V2VideoLookup");
46 qRegisterMetaType<V2ArtworkItem*>("V2ArtworkItem");
47 qRegisterMetaType<V2CutList*>("V2CutList");
48 qRegisterMetaType<V2Cutting*>("V2Cutting");
49 qRegisterMetaType<V2VideoCategory*>("V2VideoCategory");
50 qRegisterMetaType<V2VideoCategoryList*>("V2VideoCategoryList");
51}
52
54 : MythHTTPService(s_service)
55{
56}
57
59{
62
63 if ( !metadata )
64 throw( QString( "No metadata found for selected ID!." ));
65
66 auto *pVideoMetadataInfo = new V2VideoMetadataInfo();
67
68 V2FillVideoMetadataInfo ( pVideoMetadataInfo, metadata, true );
69
70 return pVideoMetadataInfo;
71}
72
74{
77 QScopedPointer<VideoMetadataListManager> mlm(new VideoMetadataListManager());
78 mlm->setList(videolist);
79 VideoMetadataListManager::VideoMetadataPtr metadata = mlm->byFilename(FileName);
80
81 if ( !metadata )
82 throw( QString( "No metadata found for selected filename!." ));
83
84 auto *pVideoMetadataInfo = new V2VideoMetadataInfo();
85
86 V2FillVideoMetadataInfo ( pVideoMetadataInfo, metadata, true );
87
88 return pVideoMetadataInfo;
89}
90
91
93// Get bookmark of a video as a frame number.
95
97{
99
100 query.prepare("SELECT filename "
101 "FROM videometadata "
102 "WHERE intid = :ID ");
103 query.bindValue(":ID", Id);
104
105 if (!query.exec())
106 {
107 MythDB::DBError("V2Video::GetSavedBookmark", query);
108 return 0;
109 }
110
111 QString fileName;
112
113 if (query.next())
114 fileName = query.value(0).toString();
115 else
116 {
117 LOG(VB_GENERAL, LOG_ERR, QString("V2Video/GetSavedBookmark Video id %1 Not found.").arg(Id));
118 return -1;
119 }
120
121 ProgramInfo pi(fileName,
122 nullptr, // _plot,
123 nullptr, // _title,
124 nullptr, // const QString &_sortTitle,
125 nullptr, // const QString &_subtitle,
126 nullptr, // const QString &_sortSubtitle,
127 nullptr, // const QString &_director,
128 0, // int _season,
129 0, // int _episode,
130 nullptr, // const QString &_inetref,
131 0min, // uint _length_in_minutes,
132 0, // uint _year,
133 nullptr); //const QString &_programid);
134
135 long ret = pi.QueryBookmark();
136 return ret;
137}
138
140// Get last play pos of a video as a frame number.
142
144{
146
147 query.prepare("SELECT filename "
148 "FROM videometadata "
149 "WHERE intid = :ID ");
150 query.bindValue(":ID", Id);
151
152 if (!query.exec())
153 {
154 MythDB::DBError("V2Video::GetLastPlayPos", query);
155 return 0;
156 }
157
158 QString fileName;
159
160 if (query.next())
161 fileName = query.value(0).toString();
162 else
163 {
164 LOG(VB_GENERAL, LOG_ERR, QString("V2Video/GetLastPlayPos Video id %1 Not found.").arg(Id));
165 return -1;
166 }
167
168 ProgramInfo pi(fileName,
169 nullptr, // _plot,
170 nullptr, // _title,
171 nullptr, // const QString &_sortTitle,
172 nullptr, // const QString &_subtitle,
173 nullptr, // const QString &_sortSubtitle,
174 nullptr, // const QString &_director,
175 0, // int _season,
176 0, // int _episode,
177 nullptr, // const QString &_inetref,
178 0min, // uint _length_in_minutes,
179 0, // uint _year,
180 nullptr); //const QString &_programid);
181
182 long ret = pi.QueryLastPlayPos();
183 return ret;
184}
185
186
187// If CollapseSubDirs is true, then files in subdirectories are not returned.
188// Instead, one row is returned for each subdirectory, with the full
189// directory name in FileName and the lowest part of the directory name
190// in Title. These directories are returned at the beginning of the list.
191//
192// Example: If the database has these files:
193// one.mkv
194// dir1/two.mkv
195// dir1/dir2/two.mkv
196//
197// With no Folder name and CollapseSubDirs=true, you get
198// one.mkv
199// dir1 Title=dir1
200//
201// With Folder=dir1 and CollapseSubDirs=true, you get
202// dir1/two.mkv
203// dir1/dir2 Title=dir2
204
206 const QString &Sort,
207 const QString &TitleRegEx,
208 int Category,
209 bool bDescending,
210 int nStartIndex,
211 int nCount,
212 bool CollapseSubDirs )
213{
214 QString fields = "title,director,studio,plot,rating,year,releasedate,"
215 "userrating,length,playcount,filename,hash,showlevel,"
216 "coverfile,inetref,collectionref,homepage,childid,browse,watched,"
217 "playcommand,category,intid,trailer,screenshot,banner,fanart,"
218 "subtitle,tagline,season,episode,host,insertdate,processed,contenttype";
219
220 QStringList sortFields = fields.split(',');
221
223
224 QString sql = "";
225 QString folder;
226 QStringList bindValues;
227
228 if (!Folder.isEmpty())
229 {
230 if (Folder.endsWith('/'))
231 folder = Folder;
232 else
233 folder = Folder + "/";
234 sql.append(" WHERE filename LIKE :BIND0 ");
235 bindValues.append(folder + "%");
236 }
237 if (!TitleRegEx.isEmpty())
238 {
239 if (bindValues.empty())
240 {
241 sql.append(" WHERE ");
242 }
243 else
244 {
245 sql.append(" AND ");
246 }
247 sql.append(" title REGEXP :BIND" + QString::number(bindValues.size()) + " ");
248 bindValues.append(TitleRegEx);
249 }
250 if (HAS_PARAMv2("Category") && Category != -1)
251 {
252 if (bindValues.empty())
253 {
254 sql.append(" WHERE ");
255 }
256 else
257 {
258 sql.append(" AND ");
259 }
260 sql.append(" category = :BIND" + QString::number(bindValues.size()) + " ");
261 bindValues.append( QString::number(Category) );
262 }
263 sql.append(" ORDER BY ");
264 QString defSeq = " ASC";
265 if (bDescending)
266 defSeq = " DESC";
267
268 QStringList sortList = Sort.toLower().split(',',Qt::SkipEmptyParts);
269 bool next = false;
270 for (const auto & item : std::as_const(sortList))
271 {
272 QStringList partList = item.split(' ',Qt::SkipEmptyParts);
273 if (partList.empty())
274 continue;
275 QString sort = partList[0];
276 if (sort == "added")
277 sort = "insertdate";
278 else if (sort == "released")
279 sort = "releasedate";
280 if (sortFields.contains(sort))
281 {
282 if (next)
283 sql.append(",");
284 if (sort == "title")
285 {
286 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
287 QString prefixes = sh->getPrefixes();
288 sort = "REGEXP_REPLACE(title,'" + prefixes + "','')";
289 }
290 else if (sort == "subtitle")
291 {
292 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
293 QString prefixes = sh->getPrefixes();
294 sort = "REGEXP_REPLACE(subtitle,'" + prefixes + "','')";
295 }
296 sql.append(sort);
297 if (partList.length() > 1 && partList[1].compare("DESC",Qt::CaseInsensitive) == 0)
298 sql.append(" DESC");
299 else if (partList.length() > 1 && partList[1].compare("ASC",Qt::CaseInsensitive) == 0)
300 sql.append(" ASC");
301 else
302 sql.append(defSeq);
303 next = true;
304 }
305 }
306 if (!next)
307 {
308 sql.append("intid");
309 sql.append(defSeq);
310 }
311
312 VideoMetadataListManager::loadAllFromDatabase(videolist, sql, bindValues);
313 std::vector<VideoMetadataListManager::VideoMetadataPtr> videos(videolist.begin(), videolist.end());
314
315 // ----------------------------------------------------------------------
316 // Build Response
317 // ----------------------------------------------------------------------
318
319 auto *pVideoMetadataInfos = new V2VideoMetadataInfoList();
320 QMap<QString, QString> map;
321 int folderlen = folder.length();
322
323 nStartIndex = (nStartIndex > 0) ? std::min( nStartIndex, (int)videos.size() ) : 0;
324 int selectedCount = 0;
325 int totalCount = 0;
326 QString dir;
327
328 // Make directory list
329 if (CollapseSubDirs)
330 {
331 for( const auto& metadata : videos )
332 {
333 if (!metadata)
334 break;
335 QString fnPart = metadata->GetFilename().mid(folderlen);
336 int slashPos = fnPart.indexOf('/',1);
337 if (slashPos >= 0)
338 {
339 dir = fnPart.mid(0, slashPos);
340 QString key = dir.toLower();
341 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
342 key = sh->doTitle(key);
343 if (!map.contains(key))
344 map.insert(key, dir);
345 }
346 }
347 // Insert directory entries at the front of the list, ordered ascending
348 // or descending, depending on the value of bDescending
349 auto addMetadata = [&](const auto it)
350 {
351 if (totalCount >= nStartIndex && (nCount == 0 || selectedCount < nCount)) {
352 V2VideoMetadataInfo *pVideoMetadataInfo =
353 pVideoMetadataInfos->AddNewVideoMetadataInfo();
354 pVideoMetadataInfo->setContentType("DIRECTORY");
355 pVideoMetadataInfo->setFileName(folder + it.value());
356 pVideoMetadataInfo->setTitle(it.value());
357 selectedCount++;
358 }
359 totalCount++;
360 };
361 if (bDescending)
362 {
363 if (!map.empty())
364 {
365 for (auto it = map.cend(); it != map.cbegin(); )
366 {
367 --it;
368 addMetadata(it);
369 }
370 }
371 }
372 else
373 {
374 for (auto it = map.cbegin(); it != map.cend(); it++) {
375 addMetadata(it);
376 }
377 }
378 }
379
380 for( const auto& metadata : videos )
381 {
382 if (!metadata)
383 break;
384
385 if (CollapseSubDirs)
386 {
387 QString fnPart = metadata->GetFilename().mid(folderlen);
388 int slashPos = fnPart.indexOf('/',1);
389 if (slashPos >= 0)
390 continue;
391 }
392
393 if (totalCount >= nStartIndex && (nCount == 0 || selectedCount < nCount)) {
394 V2VideoMetadataInfo *pVideoMetadataInfo = pVideoMetadataInfos->AddNewVideoMetadataInfo();
395 V2FillVideoMetadataInfo ( pVideoMetadataInfo, metadata, true );
396 selectedCount++;
397 }
398 totalCount++;
399 }
400
401 int curPage = 0;
402 int totalPages = 0;
403 if (nCount == 0)
404 totalPages = 1;
405 else
406 totalPages = (int)ceil((float)totalCount / nCount);
407
408 if (totalPages == 1)
409 curPage = 1;
410 else
411 {
412 curPage = (int)ceil((float)nStartIndex / nCount) + 1;
413 }
414
415 pVideoMetadataInfos->setStartIndex ( nStartIndex );
416 pVideoMetadataInfos->setCount ( selectedCount );
417 pVideoMetadataInfos->setCurrentPage ( curPage );
418 pVideoMetadataInfos->setTotalPages ( totalPages );
419 pVideoMetadataInfos->setTotalAvailable( totalCount );
420 pVideoMetadataInfos->setAsOf ( MythDate::current() );
421 pVideoMetadataInfos->setVersion ( MYTH_BINARY_VERSION );
422 pVideoMetadataInfos->setProtoVer ( MYTH_PROTO_VERSION );
423
424 return pVideoMetadataInfos;
425}
426
427
429 const QString &Subtitle,
430 const QString &Inetref,
431 int Season,
432 int Episode,
433 const QString &GrabberType,
434 bool AllowGeneric )
435{
436 auto *pVideoLookups = new V2VideoLookupList();
437
439
440 auto *factory = new MetadataFactory(nullptr);
441
442 if (factory)
443 {
444 list = factory->SynchronousLookup(Title, Subtitle,
445 Inetref, Season, Episode,
446 GrabberType, AllowGeneric);
447 }
448
449 if ( list.empty() )
450 return pVideoLookups;
451
452 //MetadataLookupList is a reference counted list.
453 //it will delete all its content at its end of life
454 for(const auto & lookup : std::as_const(list))
455 {
456 V2VideoLookup *pVideoLookup = pVideoLookups->AddNewVideoLookup();
457
458 if (lookup)
459 {
460 pVideoLookup->setTitle(lookup->GetTitle());
461 pVideoLookup->setSubTitle(lookup->GetSubtitle());
462 pVideoLookup->setSeason(lookup->GetSeason());
463 pVideoLookup->setEpisode(lookup->GetEpisode());
464 pVideoLookup->setYear(lookup->GetYear());
465 pVideoLookup->setTagline(lookup->GetTagline());
466 pVideoLookup->setDescription(lookup->GetDescription());
467 pVideoLookup->setCertification(lookup->GetCertification());
468 pVideoLookup->setInetref(lookup->GetInetref());
469 pVideoLookup->setCollectionref(lookup->GetCollectionref());
470 pVideoLookup->setHomePage(lookup->GetHomepage());
471#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
472 pVideoLookup->setReleaseDate(
473 QDateTime(lookup->GetReleaseDate(),
474 QTime(0,0),Qt::LocalTime).toUTC());
475#else
476 pVideoLookup->setReleaseDate(
477 QDateTime(lookup->GetReleaseDate(),
478 QTime(0,0),
479 QTimeZone(QTimeZone::LocalTime)).toUTC());
480#endif
481 pVideoLookup->setUserRating(lookup->GetUserRating());
482 pVideoLookup->setLength(lookup->GetRuntime().count());
483 pVideoLookup->setLanguage(lookup->GetLanguage());
484 pVideoLookup->setCountries(lookup->GetCountries());
485 pVideoLookup->setPopularity(lookup->GetPopularity());
486 pVideoLookup->setBudget(lookup->GetBudget());
487 pVideoLookup->setRevenue(lookup->GetRevenue());
488 pVideoLookup->setIMDB(lookup->GetIMDB());
489 pVideoLookup->setTMSRef(lookup->GetTMSref());
490
491 ArtworkList coverartlist = lookup->GetArtwork(kArtworkCoverart);
492 ArtworkList::iterator c;
493 for (c = coverartlist.begin(); c != coverartlist.end(); ++c)
494 {
495 V2ArtworkItem *art = pVideoLookup->AddNewArtwork();
496 art->setType("coverart");
497 art->setUrl((*c).url);
498 art->setThumbnail((*c).thumbnail);
499 art->setWidth((*c).width);
500 art->setHeight((*c).height);
501 }
502 ArtworkList fanartlist = lookup->GetArtwork(kArtworkFanart);
503 ArtworkList::iterator f;
504 for (f = fanartlist.begin(); f != fanartlist.end(); ++f)
505 {
506 V2ArtworkItem *art = pVideoLookup->AddNewArtwork();
507 art->setType("fanart");
508 art->setUrl((*f).url);
509 art->setThumbnail((*f).thumbnail);
510 art->setWidth((*f).width);
511 art->setHeight((*f).height);
512 }
513 ArtworkList bannerlist = lookup->GetArtwork(kArtworkBanner);
514 ArtworkList::iterator b;
515 for (b = bannerlist.begin(); b != bannerlist.end(); ++b)
516 {
517 V2ArtworkItem *art = pVideoLookup->AddNewArtwork();
518 art->setType("banner");
519 art->setUrl((*b).url);
520 art->setThumbnail((*b).thumbnail);
521 art->setWidth((*b).width);
522 art->setHeight((*b).height);
523 }
524 ArtworkList screenshotlist = lookup->GetArtwork(kArtworkScreenshot);
525 ArtworkList::iterator s;
526 for (s = screenshotlist.begin(); s != screenshotlist.end(); ++s)
527 {
528 V2ArtworkItem *art = pVideoLookup->AddNewArtwork();
529 art->setType("screenshot");
530 art->setUrl((*s).url);
531 art->setThumbnail((*s).thumbnail);
532 art->setWidth((*s).width);
533 art->setHeight((*s).height);
534 }
535 }
536 }
537
538 pVideoLookups->setCount ( list.count() );
539 pVideoLookups->setAsOf ( MythDate::current() );
540 pVideoLookups->setVersion ( MYTH_BINARY_VERSION );
541 pVideoLookups->setProtoVer ( MYTH_PROTO_VERSION );
542
543 delete factory;
544
545 return pVideoLookups;
546}
547
548
550{
551 bool bResult = false;
552
555 QScopedPointer<VideoMetadataListManager> mlm(new VideoMetadataListManager());
556 mlm->setList(videolist);
557 VideoMetadataListManager::VideoMetadataPtr metadata = mlm->byID(Id);
558
559 if (metadata)
560 bResult = metadata->DeleteFromDatabase();
561
562 return bResult;
563}
564
565bool V2Video::AddVideo( const QString &sFileName,
566 const QString &sHostName )
567{
568 if ( sHostName.isEmpty() )
569 throw( QString( "Host not provided! Local storage is deprecated and "
570 "is not supported by the API." ));
571
572 if ( sFileName.isEmpty() ||
573 (sFileName.contains("/../")) ||
574 (sFileName.startsWith("../")) )
575 {
576 throw( QString( "Filename not provided, or fails sanity checks!" ));
577 }
578
579 StorageGroup sgroup("Videos", sHostName);
580
581 QString fullname = sgroup.FindFile(sFileName);
582
583 if ( !QFile::exists(fullname) )
584 throw( QString( "Provided filename does not exist!" ));
585
586 QString hash = FileHash(fullname);
587
588 if (hash == "NULL")
589 {
590 LOG(VB_GENERAL, LOG_ERR, "Video Hash Failed. Unless this is a DVD or "
591 "Blu-ray, something has probably gone wrong.");
592 hash = "";
593 }
594
595 VideoMetadata newFile(sFileName, QString(), hash,
601 QString(), QString(), QString(), QString(),
602 QString(), VIDEO_YEAR_DEFAULT,
603 QDate::fromString("0000-00-00","YYYY-MM-DD"),
604 VIDEO_INETREF_DEFAULT, 0, QString(),
606 0.0, VIDEO_RATING_DEFAULT, 0, 0,
607 0, 0,
608 MythDate::current().date(), 0,
610
611 newFile.SetHost(sHostName);
612 newFile.SaveToDatabase();
613
614 return true;
615}
616
618//
620
622 bool bWatched )
623{
626 QScopedPointer<VideoMetadataListManager> mlm(new VideoMetadataListManager());
627 mlm->setList(videolist);
628 VideoMetadataListManager::VideoMetadataPtr metadata = mlm->byID(nId);
629
630 if ( !metadata )
631 return false;
632
633 metadata->SetWatched(bWatched);
634 metadata->UpdateDatabase();
635
636 return true;
637}
638
640 const QString &sTitle,
641 const QString &sSubTitle,
642 const QString &sTagLine,
643 const QString &sDirector,
644 const QString &sStudio,
645 const QString &sPlot,
646 const QString &sRating,
647 const QString &sInetref,
648 int nCollectionRef,
649 const QString &sHomePage,
650 int nYear,
651 QDate sReleasedate,
652 float fUserRating,
653 int nLength,
654 int nPlayCount,
655 int nSeason,
656 int nEpisode,
657 int nShowLevel,
658 const QString &sFileName,
659 const QString &sHash,
660 const QString &sCoverFile,
661 int nChildID,
662 bool bBrowse,
663 bool bWatched,
664 bool bProcessed,
665 const QString &sPlayCommand,
666 int nCategory,
667 const QString &sTrailer,
668 const QString &sHost,
669 const QString &sScreenshot,
670 const QString &sBanner,
671 const QString &sFanart,
672 QDate sInsertDate,
673 const QString &sContentType,
674 const QString &sGenres,
675 const QString &sCast,
676 const QString &sCountries)
677{
678 bool update_required = false;
681 QScopedPointer<VideoMetadataListManager> mlm(new VideoMetadataListManager());
682 mlm->setList(videolist);
683 VideoMetadataListManager::VideoMetadataPtr metadata = mlm->byID(nId);
684
685 if (!metadata)
686 {
687 LOG(VB_GENERAL, LOG_ERR, QString("UpdateVideoMetadata: Id=%1 not found")
688 .arg(nId));
689 return false;
690 }
691
692 if (HAS_PARAMv2("Title"))
693 {
694 metadata->SetTitle(sTitle);
695 update_required = true;
696 }
697
698 if (HAS_PARAMv2("SubTitle"))
699 {
700 metadata->SetSubtitle(sSubTitle);
701 update_required = true;
702 }
703
704 if (HAS_PARAMv2("TagLine"))
705 {
706 metadata->SetTagline(sTagLine);
707 update_required = true;
708 }
709
710 if (HAS_PARAMv2("Director"))
711 {
712 metadata->SetDirector(sDirector);
713 update_required = true;
714 }
715
716 if (HAS_PARAMv2("Studio"))
717 {
718 metadata->SetStudio(sStudio);
719 update_required = true;
720 }
721
722 if (HAS_PARAMv2("Plot"))
723 {
724 metadata->SetPlot(sPlot);
725 update_required = true;
726 }
727
728 if (HAS_PARAMv2("UserRating"))
729 {
730 metadata->SetUserRating(fUserRating);
731 update_required = true;
732 }
733
734 if (HAS_PARAMv2("Inetref"))
735 {
736 metadata->SetInetRef(sInetref);
737 update_required = true;
738 }
739
740 if (HAS_PARAMv2("CollectionRef"))
741 {
742 metadata->SetCollectionRef(nCollectionRef);
743 update_required = true;
744 }
745
746 if (HAS_PARAMv2("HomePage"))
747 {
748 metadata->SetHomepage(sHomePage);
749 update_required = true;
750 }
751
752 if (HAS_PARAMv2("Year"))
753 {
754 metadata->SetYear(nYear);
755 update_required = true;
756 }
757
758 if (HAS_PARAMv2("ReleaseDate"))
759 {
760 metadata->SetReleaseDate(sReleasedate);
761 update_required = true;
762 }
763
764 if (HAS_PARAMv2("Rating"))
765 {
766 metadata->SetRating(sRating);
767 update_required = true;
768 }
769
770 if (HAS_PARAMv2("Length"))
771 {
772 metadata->SetLength(std::chrono::minutes(nLength));
773 update_required = true;
774 }
775
776 if (HAS_PARAMv2("PlayCount"))
777 {
778 metadata->SetPlayCount(nPlayCount);
779 update_required = true;
780 }
781
782 if (HAS_PARAMv2("Season"))
783 {
784 metadata->SetSeason(nSeason);
785 update_required = true;
786 }
787
788 if (HAS_PARAMv2("Episode"))
789 {
790 metadata->SetEpisode(nEpisode);
791 update_required = true;
792 }
793
794 if (HAS_PARAMv2("ShowLevel"))
795 {
796 metadata->SetShowLevel(ParentalLevel::Level(nShowLevel));
797 update_required = true;
798 }
799
800 if (HAS_PARAMv2("FileName"))
801 {
802 metadata->SetFilename(sFileName);
803 update_required = true;
804 }
805
806 if (HAS_PARAMv2("Hash"))
807 {
808 metadata->SetHash(sHash);
809 update_required = true;
810 }
811
812 if (HAS_PARAMv2("CoverFile"))
813 {
814 metadata->SetCoverFile(sCoverFile);
815 update_required = true;
816 }
817
818 if (HAS_PARAMv2("ChildID"))
819 {
820 metadata->SetChildID(nChildID);
821 update_required = true;
822 }
823
824 if (HAS_PARAMv2("Browse"))
825 {
826 metadata->SetBrowse(bBrowse);
827 update_required = true;
828 }
829
830 if (HAS_PARAMv2("Watched"))
831 {
832 metadata->SetWatched(bWatched);
833 update_required = true;
834 }
835
836 if (HAS_PARAMv2("Processed"))
837 {
838 metadata->SetProcessed(bProcessed);
839 update_required = true;
840 }
841
842 if (HAS_PARAMv2("PlayCommand"))
843 {
844 metadata->SetPlayCommand(sPlayCommand);
845 update_required = true;
846 }
847
848 if (HAS_PARAMv2("Category"))
849 {
850 metadata->SetCategoryID(nCategory);
851 update_required = true;
852 }
853
854 if (HAS_PARAMv2("Trailer"))
855 {
856 metadata->SetTrailer(sTrailer);
857 update_required = true;
858 }
859
860 if (HAS_PARAMv2("Host"))
861 {
862 metadata->SetHost(sHost);
863 update_required = true;
864 }
865
866 if (HAS_PARAMv2("Screenshot"))
867 {
868 metadata->SetScreenshot(sScreenshot);
869 update_required = true;
870 }
871
872 if (HAS_PARAMv2("Banner"))
873 {
874 metadata->SetBanner(sBanner);
875 update_required = true;
876 }
877
878 if (HAS_PARAMv2("Fanart"))
879 {
880 metadata->SetFanart(sFanart);
881 update_required = true;
882 }
883
884 if (HAS_PARAMv2("InsertDate"))
885 {
886 metadata->SetInsertdate(sInsertDate);
887 update_required = true;
888 }
889
890 if (HAS_PARAMv2("ContentType"))
891 {
892 // valid values for ContentType are 'MOVIE','TELEVISION','ADULT','MUSICVIDEO','HOMEVIDEO'
893 VideoContentType contentType = kContentUnknown;
894 if (sContentType == "MOVIE")
895 contentType = kContentMovie;
896
897 if (sContentType == "TELEVISION")
898 contentType = kContentTelevision;
899
900 if (sContentType == "ADULT")
901 contentType = kContentAdult;
902
903 if (sContentType == "MUSICVIDEO")
904 contentType = kContentMusicVideo;
905
906 if (sContentType == "HOMEVIDEO")
907 contentType = kContentHomeMovie;
908
909 if (contentType != kContentUnknown)
910 {
911 metadata->SetContentType(contentType);
912 update_required = true;
913 }
914 else
915 {
916 LOG(VB_GENERAL, LOG_ERR, QString("UpdateVideoMetadata: Ignoring unknown ContentType: %1").arg(sContentType));
917 }
918 }
919
920 if (HAS_PARAMv2("Genres"))
921 {
923 QStringList genresList = sGenres.split(',', Qt::SkipEmptyParts);
924 std::transform(genresList.cbegin(), genresList.cend(), std::back_inserter(genres),
925 [](const QString& name)
926 {return VideoMetadata::genre_list::value_type(-1, name.simplified());} );
927
928 metadata->SetGenres(genres);
929 update_required = true;
930 }
931
932 if (HAS_PARAMv2("Cast"))
933 {
935 QStringList castList = sCast.split(',', Qt::SkipEmptyParts);
936 std::transform(castList.cbegin(), castList.cend(), std::back_inserter(cast),
937 [](const QString& name)
938 {return VideoMetadata::cast_list::value_type(-1, name.simplified());} );
939
940 metadata->SetCast(cast);
941 update_required = true;
942 }
943
944 if (HAS_PARAMv2("Countries"))
945 {
947 QStringList countryList = sCountries.split(',', Qt::SkipEmptyParts);
948 std::transform(countryList.cbegin(), countryList.cend(), std::back_inserter(countries),
949 [](const QString& name)
950 {return VideoMetadata::country_list::value_type(-1, name.simplified());} );
951
952 metadata->SetCountries(countries);
953 update_required = true;
954 }
955
956 if (update_required)
957 metadata->UpdateDatabase();
958
959 return true;
960}
961
963// Set bookmark of a video as a frame number.
965
966bool V2Video::SetSavedBookmark( int Id, long Offset )
967{
969
970 query.prepare("SELECT filename "
971 "FROM videometadata "
972 "WHERE intid = :ID ");
973 query.bindValue(":ID", Id);
974
975 if (!query.exec())
976 {
977 MythDB::DBError("Video::SetSavedBookmark", query);
978 return false;
979 }
980
981 QString fileName;
982
983 if (query.next())
984 fileName = query.value(0).toString();
985 else
986 {
987 LOG(VB_GENERAL, LOG_ERR, QString("Video/SetSavedBookmark Video id %1 Not found.").arg(Id));
988 return false;
989 }
990
991 ProgramInfo pi(fileName,
992 nullptr, // _plot,
993 nullptr, // _title,
994 nullptr, // const QString &_sortTitle,
995 nullptr, // const QString &_subtitle,
996 nullptr, // const QString &_sortSubtitle,
997 nullptr, // const QString &_director,
998 0, // int _season,
999 0, // int _episode,
1000 nullptr, // const QString &_inetref,
1001 0min, // uint _length_in_minutes,
1002 0, // uint _year,
1003 nullptr); //const QString &_programid);
1004
1005 pi.SaveBookmark(Offset);
1006 return true;
1007}
1008
1010// Set bookmark of a video as a frame number.
1012
1013bool V2Video::SetLastPlayPos( int Id, long Offset )
1014{
1016
1017 query.prepare("SELECT filename "
1018 "FROM videometadata "
1019 "WHERE intid = :ID ");
1020 query.bindValue(":ID", Id);
1021
1022 if (!query.exec())
1023 {
1024 MythDB::DBError("Video::SetLastPlayPos", query);
1025 return false;
1026 }
1027
1028 QString fileName;
1029
1030 if (query.next())
1031 fileName = query.value(0).toString();
1032 else
1033 {
1034 LOG(VB_GENERAL, LOG_ERR, QString("Video/SetLastPlayPos Video id %1 Not found.").arg(Id));
1035 return false;
1036 }
1037
1038 ProgramInfo pi(fileName,
1039 nullptr, // _plot,
1040 nullptr, // _title,
1041 nullptr, // const QString &_sortTitle,
1042 nullptr, // const QString &_subtitle,
1043 nullptr, // const QString &_sortSubtitle,
1044 nullptr, // const QString &_director,
1045 0, // int _season,
1046 0, // int _episode,
1047 nullptr, // const QString &_inetref,
1048 0min, // uint _length_in_minutes,
1049 0, // uint _year,
1050 nullptr); //const QString &_programid);
1051
1052 pi.SaveLastPlayPos(Offset);
1053 return true;
1054}
1055
1056
1057V2BlurayInfo* V2Video::GetBluray( const QString &sPath )
1058{
1059 QString path = sPath;
1060
1061 if (sPath.isEmpty())
1062 path = gCoreContext->GetSetting( "BluRayMountpoint", "/media/cdrom");
1063
1064 LOG(VB_GENERAL, LOG_NOTICE,
1065 QString("Parsing Blu-ray at path: %1 ").arg(path));
1066
1067 auto *bdmeta = new BlurayMetadata(path);
1068
1069 if ( !bdmeta )
1070 throw( QString( "Unable to open Blu-ray Metadata Parser!" ));
1071
1072 if ( !bdmeta->OpenDisc() )
1073 throw( QString( "Unable to open Blu-ray Disc/Path!" ));
1074
1075 if ( !bdmeta->ParseDisc() )
1076 throw( QString( "Unable to parse metadata from Blu-ray Disc/Path!" ));
1077
1078 auto *pBlurayInfo = new V2BlurayInfo();
1079
1080 pBlurayInfo->setPath(path);
1081 pBlurayInfo->setTitle(bdmeta->GetTitle());
1082 pBlurayInfo->setAltTitle(bdmeta->GetAlternateTitle());
1083 pBlurayInfo->setDiscLang(bdmeta->GetDiscLanguage());
1084 pBlurayInfo->setDiscNum(bdmeta->GetCurrentDiscNumber());
1085 pBlurayInfo->setTotalDiscNum(bdmeta->GetTotalDiscNumber());
1086 pBlurayInfo->setTitleCount(bdmeta->GetTitleCount());
1087 pBlurayInfo->setThumbCount(bdmeta->GetThumbnailCount());
1088 pBlurayInfo->setTopMenuSupported(bdmeta->GetTopMenuSupported());
1089 pBlurayInfo->setFirstPlaySupported(bdmeta->GetFirstPlaySupported());
1090 pBlurayInfo->setNumHDMVTitles(bdmeta->GetNumHDMVTitles());
1091 pBlurayInfo->setNumBDJTitles(bdmeta->GetNumBDJTitles());
1092 pBlurayInfo->setNumUnsupportedTitles(bdmeta->GetNumUnsupportedTitles());
1093 pBlurayInfo->setAACSDetected(bdmeta->GetAACSDetected());
1094 pBlurayInfo->setLibAACSDetected(bdmeta->GetLibAACSDetected());
1095 pBlurayInfo->setAACSHandled(bdmeta->GetAACSHandled());
1096 pBlurayInfo->setBDPlusDetected(bdmeta->GetBDPlusDetected());
1097 pBlurayInfo->setLibBDPlusDetected(bdmeta->GetLibBDPlusDetected());
1098 pBlurayInfo->setBDPlusHandled(bdmeta->GetBDPlusHandled());
1099
1100 QStringList thumbs = bdmeta->GetThumbnails();
1101 if (!thumbs.empty())
1102 pBlurayInfo->setThumbPath(thumbs.at(0));
1103
1104 delete bdmeta;
1105
1106 return pBlurayInfo;
1107}
1108
1109
1111// Jun 3, 2020
1112// Service to get stream info for all streams in a media file.
1113// This gets some basic info. If anything more is needed it can be added,
1114// depending on whether it is available from ffmpeg avformat apis.
1115// See the MythStreamInfoList class for the code that uses avformat to
1116// extract the information.
1118
1120 ( const QString &storageGroup,
1121 const QString &FileName )
1122{
1123
1124 // Search for the filename
1125
1126 StorageGroup storage( storageGroup );
1127 QString sFullFileName = storage.FindFile( FileName );
1128 MythStreamInfoList infos(sFullFileName);
1129
1130 // The constructor of this class reads the file and gets the needed
1131 // information.
1132 auto *pVideoStreamInfos = new V2VideoStreamInfoList();
1133
1134 pVideoStreamInfos->setCount ( infos.m_streamInfoList.size() );
1135 pVideoStreamInfos->setAsOf ( MythDate::current() );
1136 pVideoStreamInfos->setVersion ( MYTH_BINARY_VERSION );
1137 pVideoStreamInfos->setProtoVer ( MYTH_PROTO_VERSION );
1138 pVideoStreamInfos->setErrorCode ( infos.m_errorCode );
1139 pVideoStreamInfos->setErrorMsg ( infos.m_errorMsg );
1140
1141 for (const auto & info : std::as_const(infos.m_streamInfoList))
1142 {
1143 V2VideoStreamInfo *pVideoStreamInfo = pVideoStreamInfos->AddNewVideoStreamInfo();
1144 pVideoStreamInfo->setCodecType ( QString(QChar(info.m_codecType)) );
1145 pVideoStreamInfo->setCodecName ( info.m_codecName );
1146 pVideoStreamInfo->setWidth ( info.m_width );
1147 pVideoStreamInfo->setHeight ( info.m_height );
1148 pVideoStreamInfo->setAspectRatio ( info.m_SampleAspectRatio );
1149 pVideoStreamInfo->setFieldOrder ( info.m_fieldOrder );
1150 pVideoStreamInfo->setFrameRate ( info.m_frameRate );
1151 pVideoStreamInfo->setAvgFrameRate ( info.m_avgFrameRate );
1152 pVideoStreamInfo->setChannels ( info.m_channels );
1153 pVideoStreamInfo->setDuration ( info.m_duration );
1154
1155 }
1156 return pVideoStreamInfos;
1157}
1158
1160// October 26,2022
1161// Support for Cut List for Videos
1163
1165 const QString &offsettype,
1166 bool includeFps )
1167{
1169
1170 query.prepare("SELECT filename "
1171 "FROM videometadata "
1172 "WHERE intid = :ID ");
1173 query.bindValue(":ID", Id);
1174
1175 if (!query.exec())
1176 {
1177 MythDB::DBError("V2Video::GetVideoCommBreak", query);
1178 throw QString("Database Error.");
1179 }
1180
1181 QString fileName;
1182
1183 if (query.next())
1184 fileName = query.value(0).toString();
1185 else
1186 {
1187 LOG(VB_GENERAL, LOG_ERR, QString("V2Video/GetVideoCommBreak Video id %1 Not found.").arg(Id));
1188 throw QString("Video Not Found.");
1189 }
1190
1191 ProgramInfo pi(fileName,
1192 nullptr, // _plot,
1193 nullptr, // _title,
1194 nullptr, // const QString &_sortTitle,
1195 nullptr, // const QString &_subtitle,
1196 nullptr, // const QString &_sortSubtitle,
1197 nullptr, // const QString &_director,
1198 0, // int _season,
1199 0, // int _episode,
1200 nullptr, // const QString &_inetref,
1201 0min, // uint _length_in_minutes,
1202 0, // uint _year,
1203 nullptr); //const QString &_programid);
1204
1205
1206 int marktype = 0;
1207
1208 auto* pCutList = new V2CutList();
1209 if (offsettype.toLower() == "position")
1210 marktype = 1;
1211 else if (offsettype.toLower() == "duration")
1212 marktype = 2;
1213 else
1214 marktype = 0;
1215
1216 V2FillCutList(pCutList, &pi, marktype, includeFps);
1217
1218 return pCutList;
1219}
1220
1222// October 26,2022
1223// Support for Commercial Break List for Videos
1225
1227 const QString &offsettype,
1228 bool includeFps )
1229{
1231
1232 query.prepare("SELECT filename "
1233 "FROM videometadata "
1234 "WHERE intid = :ID ");
1235 query.bindValue(":ID", Id);
1236
1237 if (!query.exec())
1238 {
1239 MythDB::DBError("V2Video::GetVideoCommBreak", query);
1240 throw QString("Database Error.");
1241 }
1242
1243 QString fileName;
1244
1245 if (query.next())
1246 fileName = query.value(0).toString();
1247 else
1248 {
1249 LOG(VB_GENERAL, LOG_ERR, QString("V2Video/GetVideoCommBreak Video id %1 Not found.").arg(Id));
1250 throw QString("Video Not Found.");
1251 }
1252
1253 ProgramInfo pi(fileName,
1254 nullptr, // _plot,
1255 nullptr, // _title,
1256 nullptr, // const QString &_sortTitle,
1257 nullptr, // const QString &_subtitle,
1258 nullptr, // const QString &_sortSubtitle,
1259 nullptr, // const QString &_director,
1260 0, // int _season,
1261 0, // int _episode,
1262 nullptr, // const QString &_inetref,
1263 0min, // uint _length_in_minutes,
1264 0, // uint _year,
1265 nullptr); //const QString &_programid);
1266
1267
1268 int marktype = 0;
1269
1270 auto* pCutList = new V2CutList();
1271 if (offsettype.toLower() == "position")
1272 marktype = 1;
1273 else if (offsettype.toLower() == "duration")
1274 marktype = 2;
1275 else
1276 marktype = 0;
1277
1278 V2FillCommBreak(pCutList, &pi, marktype, includeFps);
1279
1280 return pCutList;
1281}
1282
1284{
1285 auto* pCatList = new V2VideoCategoryList();
1287
1288 query.prepare("SELECT intid,category "
1289 "FROM videocategory ");
1290
1291 if (!query.exec())
1292 {
1293 MythDB::DBError("V2Video::GetCategoryList", query);
1294 throw QString("Database Error.");
1295 }
1296
1297 while (query.next())
1298 {
1299 auto *cat = pCatList->AddNewCategory();
1300 cat->setId(query.value(0).toInt());
1301 cat->setName(query.value(1).toString());
1302 }
1303
1304 return pCatList;
1305}
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
bool HAS_PARAMv2(const QString &p)
QVector< MythStreamInfo > m_streamInfoList
Definition: mythavutil.h:104
Holds information on recordings and videos.
Definition: programinfo.h:74
void SaveLastPlayPos(uint64_t frame)
TODO Move to RecordingInfo.
uint64_t QueryLastPlayPos(void) const
Gets any lastplaypos position in database, unless the ignore lastplaypos flag is set.
uint64_t QueryBookmark(void) const
Gets any bookmark position in database, unless the ignore bookmark flag is set.
void SaveBookmark(uint64_t frame)
Clears any existing bookmark in DB and if frame is greater than 0 sets a new bookmark.
QString FindFile(const QString &filename)
V2ArtworkItem * AddNewArtwork()
bool UpdateVideoMetadata(int Id, const QString &Title, const QString &SubTitle, const QString &TagLine, const QString &Director, const QString &Studio, const QString &Plot, const QString &Rating, const QString &Inetref, int CollectionRef, const QString &HomePage, int Year, QDate ReleaseDate, float UserRating, int Length, int PlayCount, int Season, int Episode, int ShowLevel, const QString &FileName, const QString &Hash, const QString &CoverFile, int ChildID, bool Browse, bool Watched, bool Processed, const QString &PlayCommand, int Category, const QString &Trailer, const QString &Host, const QString &Screenshot, const QString &Banner, const QString &Fanart, QDate InsertDate, const QString &ContentType, const QString &Genres, const QString &Cast, const QString &Countries)
Definition: v2video.cpp:639
static V2CutList * GetVideoCutList(int Id, const QString &OffsetType, bool IncludeFps)
Definition: v2video.cpp:1164
static bool AddVideo(const QString &FileName, const QString &HostName)
Definition: v2video.cpp:565
static V2VideoMetadataInfo * GetVideoByFileName(const QString &FileName)
Definition: v2video.cpp:73
V2Video()
Definition: v2video.cpp:53
static bool SetSavedBookmark(int Id, long Offset)
Definition: v2video.cpp:966
static void RegisterCustomTypes()
static bool UpdateVideoWatchedStatus(int Id, bool Watched)
Definition: v2video.cpp:621
static V2CutList * GetVideoCommBreak(int Id, const QString &OffsetType, bool IncludeFps)
Definition: v2video.cpp:1226
static bool RemoveVideoFromDB(int Id)
Definition: v2video.cpp:549
static bool SetLastPlayPos(int Id, long Offset)
Definition: v2video.cpp:1013
static long GetLastPlayPos(int Id)
Definition: v2video.cpp:143
static long GetSavedBookmark(int Id)
Definition: v2video.cpp:96
static V2VideoLookupList * LookupVideo(const QString &Title, const QString &Subtitle, const QString &Inetref, int Season, int Episode, const QString &GrabberType, bool AllowGeneric)
Definition: v2video.cpp:428
static V2BlurayInfo * GetBluray(const QString &Path)
Definition: v2video.cpp:1057
V2VideoMetadataInfoList * GetVideoList(const QString &Folder, const QString &Sort, const QString &TitleRegEx, int Category, bool Descending, int StartIndex, int Count, bool CollapseSubDirs)
Definition: v2video.cpp:205
static V2VideoStreamInfoList * GetStreamInfo(const QString &StorageGroup, const QString &FileName)
Definition: v2video.cpp:1120
static V2VideoMetadataInfo * GetVideo(int Id)
Definition: v2video.cpp:58
static V2VideoCategoryList * GetCategoryList()
Definition: v2video.cpp:1283
std::list< VideoMetadataPtr > metadata_list
static VideoMetadataPtr loadOneFromDatabase(uint id)
static void loadAllFromDatabase(metadata_list &items, const QString &sql="", const QStringList &bindValues=QStringList())
Load videometadata database into memory.
std::vector< cast_entry > cast_list
Definition: videometadata.h:34
std::vector< genre_entry > genre_list
Definition: videometadata.h:32
std::vector< country_entry > country_list
Definition: videometadata.h:33
void SetHost(const QString &host)
const QString VIDEO_INETREF_DEFAULT
Definition: globals.cpp:24
const QString VIDEO_PLOT_DEFAULT
Definition: globals.cpp:32
const QString VIDEO_TRAILER_DEFAULT
Definition: globals.cpp:26
const QString VIDEO_BANNER_DEFAULT
Definition: globals.cpp:28
const QString VIDEO_SCREENSHOT_DEFAULT
Definition: globals.cpp:27
const QString VIDEO_FANART_DEFAULT
Definition: globals.cpp:29
const QString VIDEO_RATING_DEFAULT
Definition: globals.cpp:30
const QString VIDEO_DIRECTOR_DEFAULT
Definition: globals.cpp:23
const QString VIDEO_COVERFILE_DEFAULT
Definition: globals.cpp:25
VideoContentType
@ kContentMusicVideo
@ kContentTelevision
@ kContentAdult
@ kContentUnknown
@ kContentHomeMovie
@ kContentMovie
GrabberType
QList< ArtworkInfo > ArtworkList
@ kArtworkScreenshot
@ kArtworkFanart
@ kArtworkBanner
@ kArtworkCoverart
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QString FileHash(const QString &filename)
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
bool exists(str path)
Definition: xbmcvfs.py:51
void V2FillCommBreak(V2CutList *pCutList, ProgramInfo *rInfo, int marktype, bool includeFps)
void V2FillVideoMetadataInfo(V2VideoMetadataInfo *pVideoMetadataInfo, const VideoMetadataListManager::VideoMetadataPtr &pMetadata, bool bDetails)
void V2FillCutList(V2CutList *pCutList, ProgramInfo *rInfo, int marktype, bool includeFps)
Q_GLOBAL_STATIC_WITH_ARGS(MythHTTPMetaService, s_service,(VIDEO_HANDLE, V2Video::staticMetaObject, &V2Video::RegisterCustomTypes)) void V2Video
Definition: v2video.cpp:27
#define VIDEO_HANDLE
Definition: v2video.h:14
static constexpr uint16_t VIDEO_YEAR_DEFAULT
Definition: videometadata.h:18