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