7#include <QRegularExpression>
33 QString hash, QString trailer,
34 QString coverfile, QString screenshot, QString banner,
35 QString fanart,
const QString &title, QString sortTitle,
36 const QString &subtitle, QString sortSubtitle,
37 QString tagline,
int year,
const QDate releasedate,
38 QString inetref,
int collectionref, QString homepage,
39 QString director, QString studio,
40 QString plot,
float userrating,
41 QString
rating,
int length,
int playcount,
42 int season,
int episode,
const QDate insertdate,
44 int childID,
bool browse,
bool watched,
45 QString playcommand, QString category,
50 bool processed =
false,
72 if (title.isEmpty() and subtitle.isEmpty()
73 and season == 0 and episode == 0)
171 void SetTitle(
const QString& title,
const QString& sortTitle =
"")
180 void SetSubtitle(
const QString &subtitle,
const QString &sortSubtitle =
"") {
330 QString
GetImage(
const QString &name)
const;
408 cmp = m_id <=> rhs->
m_id;
417 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
418 QFileInfoList contents =
d.entryInfoList();
419 if (contents.empty())
421 return d.rmdir(dirName);
424 for (
const auto& entry : std::as_const(contents))
428 QString fileName = entry.fileName();
434 if (!QFile(entry.fileName()).remove())
438 return d.rmdir(dirName);
444 bool isremoved =
false;
466 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Could not delete file: %1")
505 for (
long value : genres.
values)
523 for (
long value : countries.
values)
541 for (
long value : cast.
values)
546 m_cast.emplace_back(value, name);
629 QString coverfile = metadataMap[
"coverfile"];
630 QString screenshot = metadataMap[
"screenshotfile"];
631 QString bannerfile = metadataMap[
"bannerfile"];
632 QString fanartfile = metadataMap[
"fanartfile"];
648 if (m_userrating < -10.0F || m_userrating > 10.0F)
660 bool inserting =
m_id == 0;
670 query.
prepare(
"INSERT INTO videometadata (title,subtitle,tagline,director,studio,plot,"
671 "rating,year,userrating,length,season,episode,filename,hash,"
672 "showlevel,coverfile,inetref,homepage,browse,watched,trailer,"
673 "screenshot,banner,fanart,host,processed,contenttype) VALUES (:TITLE, :SUBTITLE, "
674 ":TAGLINE, :DIRECTOR, :STUDIO, :PLOT, :RATING, :YEAR, :USERRATING, "
675 ":LENGTH, :SEASON, :EPISODE, :FILENAME, :HASH, :SHOWLEVEL, "
676 ":COVERFILE, :INETREF, :HOMEPAGE, :BROWSE, :WATCHED, "
677 ":TRAILER, :SCREENSHOT, :BANNER, :FANART, :HOST, :PROCESSED, :CONTENTTYPE)");
681 query.
prepare(
"UPDATE videometadata SET title = :TITLE, subtitle = :SUBTITLE, "
682 "tagline = :TAGLINE, director = :DIRECTOR, studio = :STUDIO, "
683 "plot = :PLOT, rating= :RATING, year = :YEAR, "
684 "releasedate = :RELEASEDATE, userrating = :USERRATING, "
685 "length = :LENGTH, playcount = :PLAYCOUNT, season = :SEASON, "
686 "episode = :EPISODE, filename = :FILENAME, hash = :HASH, trailer = :TRAILER, "
687 "showlevel = :SHOWLEVEL, coverfile = :COVERFILE, "
688 "screenshot = :SCREENSHOT, banner = :BANNER, fanart = :FANART, "
689 "inetref = :INETREF, collectionref = :COLLECTION, homepage = :HOMEPAGE, "
690 "browse = :BROWSE, watched = :WATCHED, host = :HOST, playcommand = :PLAYCOMMAND, "
691 "childid = :CHILDID, category = :CATEGORY, processed = :PROCESSED, "
692 "contenttype = :CONTENTTYPE WHERE intid = :INTID");
742 if (!query.
exec(
"SELECT LAST_INSERT_ID()") || !query.
next())
752 LOG(VB_GENERAL, LOG_ERR,
753 QString(
"%1: The id of the last inserted row to "
754 "videometadata seems to be 0. This is odd.")
782 query.
prepare(
"DELETE FROM videometadata WHERE intid = :ID");
789 query.
prepare(
"DELETE FROM filemarkup WHERE filename = :FILENAME");
818 LOG(VB_GENERAL, LOG_ERR,
"Unknown category id");
832 if (!genre->second.trimmed().isEmpty())
853 if (!country->second.trimmed().isEmpty())
871 auto cast =
m_cast.begin();
872 while (cast !=
m_cast.end())
874 if (!cast->second.trimmed().isEmpty())
882 cast =
m_cast.erase(cast);
903 imageMap[QStringLiteral(u
"coverfile")] = coverfile;
904 imageMap[QStringLiteral(u
"coverart")] = coverfile;
906 QString screenshotfile;
918 imageMap[QStringLiteral(u
"screenshotfile")] = screenshotfile;
919 imageMap[QStringLiteral(u
"screenshot")] = screenshotfile;
933 imageMap[QStringLiteral(u
"bannerfile")] = bannerfile;
934 imageMap[QStringLiteral(u
"banner")] = bannerfile;
948 imageMap[QStringLiteral(u
"fanartfile")] = fanartfile;
949 imageMap[QStringLiteral(u
"fanart")] = fanartfile;
951 QString smartimage = coverfile;
953 smartimage = screenshotfile;
954 imageMap[QStringLiteral(u
"smartimage")] = smartimage;
961 if ((name == QStringLiteral(u
"coverfile")) ||
962 (name == QStringLiteral(u
"coverart")))
966 && !coverfile.startsWith(u
'/')
967 && !coverfile.isEmpty()
974 if ((name == QStringLiteral(u
"screenshotfile")) ||
975 (name == QStringLiteral(u
"screenshot")))
978 if (
IsHostSet() && !screenshot.startsWith(u
'/')
979 && !screenshot.isEmpty())
985 if ((name == QStringLiteral(u
"bannerfile")) ||
986 (name == QStringLiteral(u
"banner")))
989 if (
IsHostSet() && !bannerfile.startsWith(u
'/')
990 && !bannerfile.isEmpty())
996 if ((name == QStringLiteral(u
"fanartfile")) ||
997 (name == QStringLiteral(u
"fanart")))
1000 if (
IsHostSet() && !fanartfile.startsWith(
'/')
1001 && !fanartfile.isEmpty())
1007 if ((name == QStringLiteral(u
"smartimage")) ||
1008 (name == QStringLiteral(u
"buttonimage")))
1012 QString screenshotfile =
GetImage(
"screenshot");
1013 if (!screenshotfile.isEmpty())
1014 return screenshotfile;
1027 QString
eatBraces(
const QString &title,
const QString &left_brace,
1028 const QString &right_brace)
1031 bool keep_checking =
true;
1033 while (keep_checking)
1035 int left_position = ret.indexOf(left_brace);
1036 int right_position = ret.indexOf(right_brace);
1037 if (left_position == -1 || right_position == -1)
1043 keep_checking =
false;
1047 if (left_position < right_position)
1053 ret = ret.left(left_position) +
1054 ret.right(ret.length() - right_position - 1);
1056 else if (left_position > right_position)
1062 ret = ret.left(right_position) +
1063 ret.right(ret.length() - left_position - 1);
1073 const QString &file_name,
1074 const QString &host)
1078 query.
prepare(
"SELECT intid,filename FROM videometadata WHERE "
1091 int intid = query.
value(0).toInt();
1092 QString oldfilename = query.
value(1).toString();
1094 query.
prepare(
"UPDATE videometadata SET filename = :FILENAME, "
1095 "host = :HOST WHERE intid = :INTID");
1096 query.
bindValue(
":FILENAME", file_name);
1102 MythDB::DBError(
"Video hashed metadata update (videometadata)", query);
1106 query.
prepare(
"UPDATE filemarkup SET filename = :FILENAME "
1107 "WHERE filename = :OLDFILENAME");
1108 query.
bindValue(
":FILENAME", file_name);
1109 query.
bindValue(
":OLDFILENAME", oldfilename);
1121 const QString &host)
1129 QString fullname = sgroup.
FindFile(file_name);
1144 QString cleanFilename = file_name.left(file_name.lastIndexOf(
'.'));
1145 static const QRegularExpression kSpaceRE {
"%20" };
1146 static const QRegularExpression kUnderscoreRE {
"_" };
1147 static const QRegularExpression kDotRE {
"\\." };
1148 cleanFilename.replace(kSpaceRE,
" ");
1149 cleanFilename.replace(kUnderscoreRE,
" ");
1150 cleanFilename.replace(kDotRE,
" ");
1155 QString season_translation = tr(
"Season",
"Metadata file name parsing");
1160 QString episode_translation = tr(
"Episode",
"Metadata file name parsing");
1163 QString separator =
"(?:\\s?(?:-|/)?\\s?)?";
1164 QString regexp = QString(
1167 "(?:s|(?:Season|%2))?"
1171 "(?:[ex/]|Episode|%3)"
1176 ).arg(separator, season_translation, episode_translation);
1177 static const QRegularExpression filename_parse { regexp,
1178 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1181 QString regexp2 = QString(
"(%1(?:(?:Season|%2)%1\\d*%1)*%1)$")
1182 .arg(separator, season_translation);
1183 static const QRegularExpression title_parse {regexp2,
1184 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1186 auto match = filename_parse.match(cleanFilename);
1187 if (match.hasMatch())
1190 if (position == 1 && !match.capturedView(1).isEmpty())
1193 QString title = match.captured(1);
1194 match = title_parse.match(title);
1195 if (match.hasMatch())
1196 title = title.left(match.capturedStart());
1197 title = title.right(title.length() - title.lastIndexOf(
'/') -1);
1198 return title.trimmed();
1201 return match.captured(2).trimmed();
1203 return match.captured(3).trimmed();
1205 return match.captured(4).trimmed();
1207 else if (position == 1)
1209 QString title = cleanFilename;
1212 title = title.right(title.length() -
1213 title.lastIndexOf(
'/') -1);
1218 return title.trimmed();
1220 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)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool isActive(void) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
int add(int id, int value)
bool get(int id, entry &values)
void remove(int id, int value)
bool IsThisHost(const QString &addr)
is this address mapped to this host
bool IsMasterBackend(void)
is this the actual MBE process
static void DBError(const QString &where, const MSqlQuery &query)
static QString GetFileHash(const QString &url)
static bool DeleteFile(const QString &url)
static bool Exists(const QString &url, struct stat *fileinfo)
bool get(int id, QString &category)
int add(const QString &name)
QString FindFile(const QString &filename)
static QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
static VideoCastMap & getCastMap()
static VideoCast & GetCast()
static VideoCategory & GetCategory()
static VideoCountryMap & getCountryMap()
static VideoCountry & getCountry()
static VideoGenreMap & getGenreMap()
static VideoGenre & getGenre()
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)
const QString VIDEO_INETREF_DEFAULT
const QString VIDEO_PLOT_DEFAULT
const QString VIDEO_TRAILER_DEFAULT
const QString VIDEO_BANNER_DEFAULT
const QString VIDEO_SCREENSHOT_DEFAULT
const QString VIDEO_FANART_DEFAULT
const QString VIDEO_RATING_DEFAULT
const QString VIDEO_DIRECTOR_UNKNOWN
const QString VIDEO_DIRECTOR_DEFAULT
const QString VIDEO_COVERFILE_DEFAULT
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString FileHash(const QString &filename)
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QHash< QString, QString > InfoMap
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kDateFull
Default local time.
@ kAddYear
Add year to string if not included.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
MBASE_PUBLIC std::strong_ordering 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 | .
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
def rating(profile, smoonURL, gate)
QString ParentalLevelToState(const ParentalLevel &level)
QString GetDisplayUserRating(float userrating)
QString WatchedToState(bool watched)
QString GetDisplayProcessed(bool processed)
QString TrailerToState(const QString &trailerFile)
QString GetDisplayRating(const QString &rating)
QString GetDisplayWatched(bool watched)
VideoContentType ContentTypeFromString(const QString &type)
QString ContentTypeToString(VideoContentType type)
bool IsDefaultCoverFile(const QString &coverfile)
QString GetDisplayCountries(const VideoMetadata &item)
QStringList GetDisplayCast(const VideoMetadata &item)
QString GetDisplayLength(std::chrono::minutes length)
QString GetDisplayYear(int year)
QString GetDisplayGenres(const VideoMetadata &item)
QString GetDisplayBrowse(bool browse)