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