Go to the documentation of this file.
6 #include <QRegularExpression>
26 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
27 #define capturedView capturedRef
39 QString hash, QString trailer,
40 QString coverfile, QString screenshot, QString banner,
41 QString fanart,
const QString &title, QString sortTitle,
42 const QString &subtitle, QString sortSubtitle,
43 QString tagline,
int year,
const QDate releasedate,
44 QString inetref,
int collectionref, QString homepage,
45 QString director, QString studio,
46 QString plot,
float userrating,
47 QString
rating,
int length,
int playcount,
48 int season,
int episode,
const QDate insertdate,
50 int childID,
bool browse,
bool watched,
51 QString playcommand, QString category,
56 bool processed =
false,
78 if (title.isEmpty() and subtitle.isEmpty()
79 and season == 0 and episode == 0)
177 void SetTitle(
const QString& title,
const QString& sortTitle =
"")
186 void SetSubtitle(
const QString &subtitle,
const QString &sortSubtitle =
"") {
336 QString
GetImage(
const QString &name)
const;
418 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
419 QFileInfoList contents =
d.entryInfoList();
420 if (contents.empty())
422 return d.rmdir(dirName);
425 for (
const auto& entry : qAsConst(contents))
429 QString fileName = entry.fileName();
435 if (!QFile(entry.fileName()).remove())
439 return d.rmdir(dirName);
445 bool isremoved =
false;
467 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Could not delete file: %1")
506 for (
long value : genres.
values)
524 for (
long value : countries.
values)
542 for (
long value : cast.
values)
547 m_cast.emplace_back(value, name);
631 QString coverfile = metadataMap[
"coverfile"];
632 QString screenshot = metadataMap[
"screenshotfile"];
633 QString bannerfile = metadataMap[
"bannerfile"];
634 QString fanartfile = metadataMap[
"fanartfile"];
650 if (m_userrating < -10.0F || m_userrating > 10.0F)
662 bool inserting =
m_id == 0;
672 query.
prepare(
"INSERT INTO videometadata (title,subtitle,tagline,director,studio,plot,"
673 "rating,year,userrating,length,season,episode,filename,hash,"
674 "showlevel,coverfile,inetref,homepage,browse,watched,trailer,"
675 "screenshot,banner,fanart,host,processed,contenttype) VALUES (:TITLE, :SUBTITLE, "
676 ":TAGLINE, :DIRECTOR, :STUDIO, :PLOT, :RATING, :YEAR, :USERRATING, "
677 ":LENGTH, :SEASON, :EPISODE, :FILENAME, :HASH, :SHOWLEVEL, "
678 ":COVERFILE, :INETREF, :HOMEPAGE, :BROWSE, :WATCHED, "
679 ":TRAILER, :SCREENSHOT, :BANNER, :FANART, :HOST, :PROCESSED, :CONTENTTYPE)");
683 query.
prepare(
"UPDATE videometadata SET title = :TITLE, subtitle = :SUBTITLE, "
684 "tagline = :TAGLINE, director = :DIRECTOR, studio = :STUDIO, "
685 "plot = :PLOT, rating= :RATING, year = :YEAR, "
686 "releasedate = :RELEASEDATE, userrating = :USERRATING, "
687 "length = :LENGTH, playcount = :PLAYCOUNT, season = :SEASON, "
688 "episode = :EPISODE, filename = :FILENAME, hash = :HASH, trailer = :TRAILER, "
689 "showlevel = :SHOWLEVEL, coverfile = :COVERFILE, "
690 "screenshot = :SCREENSHOT, banner = :BANNER, fanart = :FANART, "
691 "inetref = :INETREF, collectionref = :COLLECTION, homepage = :HOMEPAGE, "
692 "browse = :BROWSE, watched = :WATCHED, host = :HOST, playcommand = :PLAYCOMMAND, "
693 "childid = :CHILDID, category = :CATEGORY, processed = :PROCESSED, "
694 "contenttype = :CONTENTTYPE WHERE intid = :INTID");
744 if (!query.
exec(
"SELECT LAST_INSERT_ID()") || !query.
next())
754 LOG(VB_GENERAL, LOG_ERR,
755 QString(
"%1: The id of the last inserted row to "
756 "videometadata seems to be 0. This is odd.")
784 query.
prepare(
"DELETE FROM videometadata WHERE intid = :ID");
791 query.
prepare(
"DELETE FROM filemarkup WHERE filename = :FILENAME");
820 LOG(VB_GENERAL, LOG_ERR,
"Unknown category id");
834 if (!genre->second.trimmed().isEmpty())
855 if (!country->second.trimmed().isEmpty())
873 auto cast =
m_cast.begin();
874 while (cast !=
m_cast.end())
876 if (!cast->second.trimmed().isEmpty())
884 cast =
m_cast.erase(cast);
905 imageMap[QStringLiteral(u
"coverfile")] = coverfile;
906 imageMap[QStringLiteral(u
"coverart")] = coverfile;
908 QString screenshotfile;
920 imageMap[QStringLiteral(u
"screenshotfile")] = screenshotfile;
921 imageMap[QStringLiteral(u
"screenshot")] = screenshotfile;
935 imageMap[QStringLiteral(u
"bannerfile")] = bannerfile;
936 imageMap[QStringLiteral(u
"banner")] = bannerfile;
950 imageMap[QStringLiteral(u
"fanartfile")] = fanartfile;
951 imageMap[QStringLiteral(u
"fanart")] = fanartfile;
953 QString smartimage = coverfile;
955 smartimage = screenshotfile;
956 imageMap[QStringLiteral(u
"smartimage")] = smartimage;
963 if ((name == QStringLiteral(u
"coverfile")) ||
964 (name == QStringLiteral(u
"coverart")))
968 && !coverfile.startsWith(u
'/')
969 && !coverfile.isEmpty()
976 if ((name == QStringLiteral(u
"screenshotfile")) ||
977 (name == QStringLiteral(u
"screenshot")))
980 if (
IsHostSet() && !screenshot.startsWith(u
'/')
981 && !screenshot.isEmpty())
987 if ((name == QStringLiteral(u
"bannerfile")) ||
988 (name == QStringLiteral(u
"banner")))
991 if (
IsHostSet() && !bannerfile.startsWith(u
'/')
992 && !bannerfile.isEmpty())
998 if ((name == QStringLiteral(u
"fanartfile")) ||
999 (name == QStringLiteral(u
"fanart")))
1002 if (
IsHostSet() && !fanartfile.startsWith(
'/')
1003 && !fanartfile.isEmpty())
1009 if ((name == QStringLiteral(u
"smartimage")) ||
1010 (name == QStringLiteral(u
"buttonimage")))
1014 QString screenshotfile =
GetImage(
"screenshot");
1015 if (!screenshotfile.isEmpty())
1016 return screenshotfile;
1029 QString eatBraces(
const QString &title,
const QString &left_brace,
1030 const QString &right_brace)
1033 bool keep_checking =
true;
1035 while (keep_checking)
1037 int left_position = ret.indexOf(left_brace);
1038 int right_position = ret.indexOf(right_brace);
1039 if (left_position == -1 || right_position == -1)
1045 keep_checking =
false;
1049 if (left_position < right_position)
1055 ret = ret.left(left_position) +
1056 ret.right(ret.length() - right_position - 1);
1058 else if (left_position > right_position)
1064 ret = ret.left(right_position) +
1065 ret.right(ret.length() - left_position - 1);
1075 const QString &file_name,
1076 const QString &host)
1080 query.
prepare(
"SELECT intid,filename FROM videometadata WHERE "
1093 int intid = query.
value(0).toInt();
1094 QString oldfilename = query.
value(1).toString();
1096 query.
prepare(
"UPDATE videometadata SET filename = :FILENAME, "
1097 "host = :HOST WHERE intid = :INTID");
1098 query.
bindValue(
":FILENAME", file_name);
1104 MythDB::DBError(
"Video hashed metadata update (videometadata)", query);
1108 query.
prepare(
"UPDATE filemarkup SET filename = :FILENAME "
1109 "WHERE filename = :OLDFILENAME");
1110 query.
bindValue(
":FILENAME", file_name);
1111 query.
bindValue(
":OLDFILENAME", oldfilename);
1123 const QString &host)
1131 QString fullname = sgroup.
FindFile(file_name);
1146 QString cleanFilename = file_name.left(file_name.lastIndexOf(
'.'));
1147 static const QRegularExpression kSpaceRE {
"%20" };
1148 static const QRegularExpression kUnderscoreRE {
"_" };
1149 static const QRegularExpression kDotRE {
"\\." };
1150 cleanFilename.replace(kSpaceRE,
" ");
1151 cleanFilename.replace(kUnderscoreRE,
" ");
1152 cleanFilename.replace(kDotRE,
" ");
1157 QString season_translation = tr(
"Season",
"Metadata file name parsing");
1162 QString episode_translation = tr(
"Episode",
"Metadata file name parsing");
1165 QString separator =
"(?:\\s?(?:-|/)?\\s?)?";
1166 QString regexp = QString(
1169 "(?:s|(?:Season|%2))?"
1173 "(?:[ex/]|Episode|%3)"
1178 ).arg(separator, season_translation, episode_translation);
1179 static const QRegularExpression filename_parse { regexp,
1180 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1183 QString regexp2 = QString(
"(%1(?:(?:Season|%2)%1\\d*%1)*%1)$")
1184 .arg(separator, season_translation);
1185 static const QRegularExpression title_parse {regexp2,
1186 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1188 auto match = filename_parse.match(cleanFilename);
1189 if (match.hasMatch())
1192 if (position == 1 && !match.capturedView(1).isEmpty())
1195 QString title = match.captured(1);
1196 match = title_parse.match(title);
1197 if (match.hasMatch())
1198 title = title.left(match.capturedStart());
1199 title = title.right(title.length() - title.lastIndexOf(
'/') -1);
1200 return title.trimmed();
1203 return match.captured(2).trimmed();
1205 return match.captured(3).trimmed();
1207 return match.captured(4).trimmed();
1209 else if (position == 1)
1211 QString title = cleanFilename;
1214 title = title.right(title.length() -
1215 title.lastIndexOf(
'/') -1);
1217 title = eatBraces(title,
"[",
"]");
1218 title = eatBraces(title,
"(",
")");
1219 title = eatBraces(title,
"{",
"}");
1220 return title.trimmed();
1222 else if (position == 2 || position == 3)
1229 const QString &hash,
const QString &trailer,
const QString &coverfile,
1230 const QString &screenshot,
const QString &banner,
const QString &fanart,
1231 const QString &title,
const QString &sortTitle,
1232 const QString &subtitle,
const QString &sortSubtitle,
const QString &tagline,
1233 int year,
const QDate releasedate,
const QString &inetref,
int collectionref,
1234 const QString &homepage,
const QString &director,
const QString &studio,
1235 const QString &plot,
float userrating,
const QString &
rating,
1236 int length,
int playcount,
int season,
int episode,
const QDate insertdate,
1238 int childID,
bool browse,
bool watched,
1239 const QString &playcommand,
const QString &category,
1243 const QString &host,
bool processed,
1247 screenshot, banner, fanart, title, sortTitle, subtitle,
1248 sortSubtitle, tagline, year, releasedate, inetref,
1249 collectionref, homepage, director, studio, plot, userrating,
rating,
1250 length, playcount, season, episode, insertdate,
id, showlevel, categoryID,
1251 childID, browse, watched, playcommand, category, genres, countries,
1252 cast, host, processed, contenttype);
1284 metadataMap[QStringLiteral(u
"filename")] =
GetFilename();
1286 metadataMap[QStringLiteral(u
"title")] =
GetTitle();
1287 metadataMap[QStringLiteral(u
"sorttitle")] =
GetSortTitle();
1288 metadataMap[QStringLiteral(u
"subtitle")] =
GetSubtitle();
1290 metadataMap[QStringLiteral(u
"tagline")] =
GetTagline();
1291 metadataMap[QStringLiteral(u
"director")] =
GetDirector();
1292 metadataMap[QStringLiteral(u
"studio")] =
GetStudio();
1293 metadataMap[QStringLiteral(u
"description0")] = metadataMap[QStringLiteral(u
"description")] =
GetPlot();
1296 metadataMap[QStringLiteral(u
"cast")] =
GetDisplayCast(*this).join(
", ");
1299 metadataMap[QStringLiteral(u
"playcount")] = QString::number(
GetPlayCount());
1311 QString usingSE = QStringLiteral(u
"s%1e%2")
1314 metadataMap[QStringLiteral(u
"s##e##")] = metadataMap[QStringLiteral(u
"s00e00")] = usingSE;
1315 QString usingX = QStringLiteral(u
"%1x%2")
1318 metadataMap[QStringLiteral(u
"##x##")] = metadataMap[QStringLiteral(u
"00x00")] = usingX;
1322 metadataMap[QStringLiteral(u
"s##e##")] = metadataMap[QStringLiteral(u
"##x##")] = QString();
1323 metadataMap[QStringLiteral(u
"s00e00")] = metadataMap[QStringLiteral(u
"00x00")] = QString();
1324 metadataMap[QStringLiteral(u
"season")] = metadataMap[QStringLiteral(u
"episode")] = QString();
1331 metadataMap[QStringLiteral(u
"inetref")] =
GetInetRef();
1332 metadataMap[QStringLiteral(u
"homepage")] =
GetHomepage();
1333 metadataMap[QStringLiteral(u
"child_id")] = QString::number(
GetChildID());
1337 metadataMap[QStringLiteral(u
"category")] =
GetCategory();
1344 if (name == QStringLiteral(u
"filename"))
1346 if (name == QStringLiteral(u
"sortfilename"))
1348 if (name == QStringLiteral(u
"title"))
1350 if (name == QStringLiteral(u
"sorttitle"))
1352 if (name == QStringLiteral(u
"subtitle"))
1354 if (name == QStringLiteral(u
"sortsubtitle"))
1356 if (name == QStringLiteral(u
"tagline"))
1358 if (name == QStringLiteral(u
"director"))
1360 if (name == QStringLiteral(u
"studio"))
1362 if ((name == QStringLiteral(u
"description")) ||
1363 (name == QStringLiteral(u
"description0")))
1365 if (name == QStringLiteral(u
"genres"))
1367 if (name == QStringLiteral(u
"countries"))
1369 if (name == QStringLiteral(u
"cast"))
1371 if (name == QStringLiteral(u
"rating"))
1373 if (name == QStringLiteral(u
"length"))
1375 if (name == QStringLiteral(u
"playcount"))
1377 if (name == QStringLiteral(u
"year"))
1380 if (name == QStringLiteral(u
"releasedate"))
1383 if (name == QStringLiteral(u
"userrating"))
1388 if (name == QStringLiteral(u
"season"))
1390 if (name == QStringLiteral(u
"episode"))
1392 if ((name == QStringLiteral(u
"s##e##")) ||
1393 (name == QStringLiteral(u
"s00e00")))
1395 return QStringLiteral(u
"s%1e%2")
1399 if ((name == QStringLiteral(u
"##x##")) ||
1400 (name == QStringLiteral(u
"00x00")))
1402 return QStringLiteral(u
"%1x%2")
1408 if (name == QStringLiteral(u
"insertdate"))
1411 if (name == QStringLiteral(u
"inetref"))
1413 if (name == QStringLiteral(u
"homepage"))
1415 if (name == QStringLiteral(u
"child_id"))
1417 if (name == QStringLiteral(u
"browseable"))
1419 if (name == QStringLiteral(u
"watched"))
1421 if (name == QStringLiteral(u
"processed"))
1423 if (name == QStringLiteral(u
"category"))
1431 stateMap[QStringLiteral(u
"userratingstate")] =
1441 if (name == QStringLiteral(u
"trailerstate"))
1443 if (name == QStringLiteral(u
"userratingstate"))
1445 if (name == QStringLiteral(u
"watchedstate"))
1447 if (name == QStringLiteral(u
"videolevel"))
1464 metadataMap[
"coverfile"] =
"";
1465 metadataMap[
"screenshotfile"] =
"";
1466 metadataMap[
"bannerfile"] =
"";
1467 metadataMap[
"fanartfile"] =
"";
1468 metadataMap[
"filename"] =
"";
1469 metadataMap[
"sortfilename"] =
"";
1470 metadataMap[
"title"] =
"";
1471 metadataMap[
"sorttitle"] =
"";
1472 metadataMap[
"subtitle"] =
"";
1473 metadataMap[
"sortsubtitle"] =
"";
1474 metadataMap[
"tagline"] =
"";
1475 metadataMap[
"director"] =
"";
1476 metadataMap[
"studio"] =
"";
1477 metadataMap[
"description"] =
"";
1478 metadataMap[
"description0"] =
"";
1479 metadataMap[
"genres"] =
"";
1480 metadataMap[
"countries"] =
"";
1481 metadataMap[
"cast"] =
"";
1482 metadataMap[
"rating"] =
"";
1483 metadataMap[
"length"] =
"";
1484 metadataMap[
"playcount"] =
"";
1485 metadataMap[
"year"] =
"";
1486 metadataMap[
"releasedate"] =
"";
1487 metadataMap[
"userrating"] =
"";
1488 metadataMap[
"season"] =
"";
1489 metadataMap[
"episode"] =
"";
1490 metadataMap[
"s##e##"] =
"";
1491 metadataMap[
"##x##"] =
"";
1492 metadataMap[
"trailerstate"] =
"";
1493 metadataMap[
"userratingstate"] =
"";
1494 metadataMap[
"watchedstate"] =
"";
1495 metadataMap[
"videolevel"] =
"";
1496 metadataMap[
"insertdate"] =
"";
1497 metadataMap[
"inetref"] =
"";
1498 metadataMap[
"homepage"] =
"";
1499 metadataMap[
"child_id"] =
"";
1500 metadataMap[
"browseable"] =
"";
1501 metadataMap[
"watched"] =
"";
1502 metadataMap[
"category"] =
"";
1503 metadataMap[
"processed"] =
"";
1508 if (data ==
nullptr)
1511 QString result = metadata->
GetText(name);
1512 if (!result.isEmpty())
1514 result = metadata->GetImage(name);
1515 if (!result.isEmpty())
1517 return metadata->GetState(name);
1522 if (data ==
nullptr)
1530 if (data ==
nullptr)
1969 if (
m_imp->getID() == 0)
bool isActive(void) const
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
QString WatchedToState(bool watched)
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
int add(const QString &name)
static bool Exists(const QString &url, struct stat *fileinfo)
QString FindFile(const QString &filename)
QVariant value(int i) const
QString GetDisplayLength(std::chrono::minutes length)
QString GetDisplayProcessed(bool processed)
VideoContentType ContentTypeFromString(const QString &type)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
QString ContentTypeToString(VideoContentType type)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QHash< QString, QString > InfoMap
static VideoGenreMap & getGenreMap()
bool IsMasterBackend(void)
is this the actual MBE process
QString GetDisplayRating(const QString &rating)
static VideoGenre & getGenre()
QString GetDisplayUserRating(float userrating)
int add(int id, int value)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString GetDisplayCountries(const VideoMetadata &item)
static void DBError(const QString &where, const MSqlQuery &query)
def rating(profile, smoonURL, gate)
MBASE_PUBLIC int naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity=Qt::CaseSensitive)
This method chops the input a and b into pieces of digits and non-digits (a1.05 becomes a | 1 | .
const QString VIDEO_COVERFILE_DEFAULT
const QString VIDEO_PLOT_DEFAULT
static QString GetFileHash(const QString &url)
QString GetDisplayBrowse(bool browse)
static VideoCountry & getCountry()
int ternary_compare(const QDateTime &a, const QDateTime &b)
balanced ternary (three way) comparison This is equivalent to C++20's operator <=>.
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
static bool DeleteFile(const QString &url)
static VideoCategory & GetCategory()
const QString VIDEO_TRAILER_DEFAULT
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
const QString VIDEO_INETREF_DEFAULT
const QString VIDEO_FANART_DEFAULT
QString ParentalLevelToState(const ParentalLevel &level)
bool IsDefaultCoverFile(const QString &coverfile)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
static VideoCountryMap & getCountryMap()
@ kAddYear
Add year to string if not included.
static VideoCast & GetCast()
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
const QString VIDEO_RATING_DEFAULT
bool get(int id, entry &values)
void remove(int id, int value)
VideoMetadata(const QString &filename=QString(), const QString &sortFilename=QString(), const QString &hash=QString(), const QString &trailer=QString(), const QString &coverfile=QString(), const QString &screenshot=QString(), const QString &banner=QString(), const QString &fanart=QString(), const QString &title=QString(), const QString &sortTitle=QString(), const QString &subtitle=QString(), const QString &sortSubtitle=QString(), const QString &tagline=QString(), int year=VIDEO_YEAR_DEFAULT, QDate releasedate=QDate(), const QString &inetref=QString(), int collectionref=0, const QString &homepage=QString(), const QString &director=QString(), const QString &studio=QString(), const QString &plot=QString(), float userrating=0.0, const QString &rating=QString(), int length=0, int playcount=0, int season=0, int episode=0, QDate insertdate=QDate(), int id=0, ParentalLevel::Level showlevel=ParentalLevel::plLowest, int categoryID=0, int childID=-1, bool browse=true, bool watched=false, const QString &playcommand=QString(), const QString &category=QString(), const genre_list &genres=genre_list(), const country_list &countries=country_list(), const cast_list &cast=cast_list(), const QString &host="", bool processed=false, VideoContentType contenttype=kContentUnknown)
@ kDateFull
Default local time.
QString GetDisplayGenres(const VideoMetadata &item)
bool IsThisHost(const QString &addr)
is this address mapped to this host
static VideoCastMap & getCastMap()
QString TrailerToState(const QString &trailerFile)
const QString VIDEO_BANNER_DEFAULT
static const iso6937table * d
QString FileHash(const QString &filename)
QString GetDisplayWatched(bool watched)
QStringList GetDisplayCast(const VideoMetadata &item)
bool get(int id, QString &category)
const QString VIDEO_DIRECTOR_DEFAULT
const QString VIDEO_SCREENSHOT_DEFAULT
QString GetDisplayYear(int year)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
const QString VIDEO_DIRECTOR_UNKNOWN