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;
404 cmp = m_id <=> rhs->
m_id;
412 d.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
413 QFileInfoList contents =
d.entryInfoList();
414 if (contents.empty())
416 return d.rmdir(dirName);
419 for (
const auto& entry : std::as_const(contents))
423 QString fileName = entry.fileName();
429 if (!QFile(entry.fileName()).remove())
433 return d.rmdir(dirName);
439 bool isremoved =
false;
461 LOG(VB_GENERAL, LOG_DEBUG, QString(
"Could not delete file: %1")
500 for (
long value : genres.
values)
518 for (
long value : countries.
values)
536 for (
long value : cast.
values)
541 m_cast.emplace_back(value, name);
624 QString coverfile = metadataMap[
"coverfile"];
625 QString screenshot = metadataMap[
"screenshotfile"];
626 QString bannerfile = metadataMap[
"bannerfile"];
627 QString fanartfile = metadataMap[
"fanartfile"];
643 if (m_userrating < -10.0F || m_userrating > 10.0F)
655 bool inserting =
m_id == 0;
665 query.
prepare(
"INSERT INTO videometadata (title,subtitle,tagline,director,studio,plot,"
666 "rating,year,userrating,length,season,episode,filename,hash,"
667 "showlevel,coverfile,inetref,homepage,browse,watched,trailer,"
668 "screenshot,banner,fanart,host,processed,contenttype) VALUES (:TITLE, :SUBTITLE, "
669 ":TAGLINE, :DIRECTOR, :STUDIO, :PLOT, :RATING, :YEAR, :USERRATING, "
670 ":LENGTH, :SEASON, :EPISODE, :FILENAME, :HASH, :SHOWLEVEL, "
671 ":COVERFILE, :INETREF, :HOMEPAGE, :BROWSE, :WATCHED, "
672 ":TRAILER, :SCREENSHOT, :BANNER, :FANART, :HOST, :PROCESSED, :CONTENTTYPE)");
676 query.
prepare(
"UPDATE videometadata SET title = :TITLE, subtitle = :SUBTITLE, "
677 "tagline = :TAGLINE, director = :DIRECTOR, studio = :STUDIO, "
678 "plot = :PLOT, rating= :RATING, year = :YEAR, "
679 "releasedate = :RELEASEDATE, userrating = :USERRATING, "
680 "length = :LENGTH, playcount = :PLAYCOUNT, season = :SEASON, "
681 "episode = :EPISODE, filename = :FILENAME, hash = :HASH, trailer = :TRAILER, "
682 "showlevel = :SHOWLEVEL, coverfile = :COVERFILE, "
683 "screenshot = :SCREENSHOT, banner = :BANNER, fanart = :FANART, "
684 "inetref = :INETREF, collectionref = :COLLECTION, homepage = :HOMEPAGE, "
685 "browse = :BROWSE, watched = :WATCHED, host = :HOST, playcommand = :PLAYCOMMAND, "
686 "childid = :CHILDID, category = :CATEGORY, processed = :PROCESSED, "
687 "contenttype = :CONTENTTYPE WHERE intid = :INTID");
737 if (!query.
exec(
"SELECT LAST_INSERT_ID()") || !query.
next())
747 LOG(VB_GENERAL, LOG_ERR,
748 QString(
"%1: The id of the last inserted row to "
749 "videometadata seems to be 0. This is odd.")
777 query.
prepare(
"DELETE FROM videometadata WHERE intid = :ID");
784 query.
prepare(
"DELETE FROM filemarkup WHERE filename = :FILENAME");
813 LOG(VB_GENERAL, LOG_ERR,
"Unknown category id");
827 if (!genre->second.trimmed().isEmpty())
848 if (!country->second.trimmed().isEmpty())
866 auto cast =
m_cast.begin();
867 while (cast !=
m_cast.end())
869 if (!cast->second.trimmed().isEmpty())
877 cast =
m_cast.erase(cast);
898 imageMap[QStringLiteral(u
"coverfile")] = coverfile;
899 imageMap[QStringLiteral(u
"coverart")] = coverfile;
901 QString screenshotfile;
913 imageMap[QStringLiteral(u
"screenshotfile")] = screenshotfile;
914 imageMap[QStringLiteral(u
"screenshot")] = screenshotfile;
928 imageMap[QStringLiteral(u
"bannerfile")] = bannerfile;
929 imageMap[QStringLiteral(u
"banner")] = bannerfile;
943 imageMap[QStringLiteral(u
"fanartfile")] = fanartfile;
944 imageMap[QStringLiteral(u
"fanart")] = fanartfile;
946 QString smartimage = coverfile;
948 smartimage = screenshotfile;
949 imageMap[QStringLiteral(u
"smartimage")] = smartimage;
956 if ((name == QStringLiteral(u
"coverfile")) ||
957 (name == QStringLiteral(u
"coverart")))
961 && !coverfile.startsWith(u
'/')
962 && !coverfile.isEmpty()
969 if ((name == QStringLiteral(u
"screenshotfile")) ||
970 (name == QStringLiteral(u
"screenshot")))
973 if (
IsHostSet() && !screenshot.startsWith(u
'/')
974 && !screenshot.isEmpty())
980 if ((name == QStringLiteral(u
"bannerfile")) ||
981 (name == QStringLiteral(u
"banner")))
984 if (
IsHostSet() && !bannerfile.startsWith(u
'/')
985 && !bannerfile.isEmpty())
991 if ((name == QStringLiteral(u
"fanartfile")) ||
992 (name == QStringLiteral(u
"fanart")))
995 if (
IsHostSet() && !fanartfile.startsWith(
'/')
996 && !fanartfile.isEmpty())
1002 if ((name == QStringLiteral(u
"smartimage")) ||
1003 (name == QStringLiteral(u
"buttonimage")))
1007 QString screenshotfile =
GetImage(
"screenshot");
1008 if (!screenshotfile.isEmpty())
1009 return screenshotfile;
1022 QString
eatBraces(
const QString &title,
const QString &left_brace,
1023 const QString &right_brace)
1026 bool keep_checking =
true;
1028 while (keep_checking)
1030 int left_position = ret.indexOf(left_brace);
1031 int right_position = ret.indexOf(right_brace);
1032 if (left_position == -1 || right_position == -1)
1038 keep_checking =
false;
1042 if (left_position < right_position)
1048 ret = ret.left(left_position) +
1049 ret.right(ret.length() - right_position - 1);
1051 else if (left_position > right_position)
1057 ret = ret.left(right_position) +
1058 ret.right(ret.length() - left_position - 1);
1068 const QString &file_name,
1069 const QString &host)
1073 query.
prepare(
"SELECT intid,filename FROM videometadata WHERE "
1086 int intid = query.
value(0).toInt();
1087 QString oldfilename = query.
value(1).toString();
1089 query.
prepare(
"UPDATE videometadata SET filename = :FILENAME, "
1090 "host = :HOST WHERE intid = :INTID");
1091 query.
bindValue(
":FILENAME", file_name);
1097 MythDB::DBError(
"Video hashed metadata update (videometadata)", query);
1101 query.
prepare(
"UPDATE filemarkup SET filename = :FILENAME "
1102 "WHERE filename = :OLDFILENAME");
1103 query.
bindValue(
":FILENAME", file_name);
1104 query.
bindValue(
":OLDFILENAME", oldfilename);
1116 const QString &host)
1124 QString fullname = sgroup.
FindFile(file_name);
1139 QString cleanFilename = file_name.left(file_name.lastIndexOf(
'.'));
1140 static const QRegularExpression kSpaceRE {
"%20" };
1141 static const QRegularExpression kUnderscoreRE {
"_" };
1142 static const QRegularExpression kDotRE {
"\\." };
1143 cleanFilename.replace(kSpaceRE,
" ");
1144 cleanFilename.replace(kUnderscoreRE,
" ");
1145 cleanFilename.replace(kDotRE,
" ");
1150 QString season_translation = tr(
"Season",
"Metadata file name parsing");
1155 QString episode_translation = tr(
"Episode",
"Metadata file name parsing");
1158 QString separator =
"(?:\\s?(?:-|/)?\\s?)?";
1159 QString regexp = QString(
1162 "(?:s|(?:Season|%2))?"
1166 "(?:[ex/]|Episode|%3)"
1171 ).arg(separator, season_translation, episode_translation);
1172 static const QRegularExpression filename_parse { regexp,
1173 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1176 QString regexp2 = QString(
"(%1(?:(?:Season|%2)%1\\d*%1)*%1)$")
1177 .arg(separator, season_translation);
1178 static const QRegularExpression title_parse {regexp2,
1179 QRegularExpression::CaseInsensitiveOption|QRegularExpression::UseUnicodePropertiesOption };
1181 auto match = filename_parse.match(cleanFilename);
1182 if (match.hasMatch())
1185 if (position == 1 && !match.capturedView(1).isEmpty())
1188 QString title = match.captured(1);
1189 match = title_parse.match(title);
1190 if (match.hasMatch())
1191 title = title.left(match.capturedStart());
1192 title = title.right(title.length() - title.lastIndexOf(
'/') -1);
1193 return title.trimmed();
1196 return match.captured(2).trimmed();
1198 return match.captured(3).trimmed();
1200 return match.captured(4).trimmed();
1202 else if (position == 1)
1204 QString title = cleanFilename;
1207 title = title.right(title.length() -
1208 title.lastIndexOf(
'/') -1);
1213 return title.trimmed();
1215 else if (position == 2 || position == 3)
1224 const QString &hash,
const QString &trailer,
const QString &coverfile,
1225 const QString &screenshot,
const QString &banner,
const QString &fanart,
1226 const QString &title,
const QString &sortTitle,
1227 const QString &subtitle,
const QString &sortSubtitle,
const QString &tagline,
1228 int year,
const QDate releasedate,
const QString &inetref,
int collectionref,
1229 const QString &homepage,
const QString &director,
const QString &studio,
1230 const QString &plot,
float userrating,
const QString &
rating,
1231 int length,
int playcount,
int season,
int episode,
const QDate insertdate,
1233 int childID,
bool browse,
bool watched,
1234 const QString &playcommand,
const QString &category,
1238 const QString &host,
bool processed,
1242 screenshot, banner, fanart, title, sortTitle, subtitle,
1243 sortSubtitle, tagline, year, releasedate, inetref,
1244 collectionref, homepage, director, studio, plot, userrating,
rating,
1245 length, playcount, season, episode, insertdate,
id, showlevel, categoryID,
1246 childID, browse, watched, playcommand, category, genres, countries,
1247 cast, host, processed, contenttype);
1279 metadataMap[QStringLiteral(u
"filename")] =
GetFilename();
1281 metadataMap[QStringLiteral(u
"title")] =
GetTitle();
1282 metadataMap[QStringLiteral(u
"sorttitle")] =
GetSortTitle();
1283 metadataMap[QStringLiteral(u
"subtitle")] =
GetSubtitle();
1285 metadataMap[QStringLiteral(u
"tagline")] =
GetTagline();
1286 metadataMap[QStringLiteral(u
"director")] =
GetDirector();
1287 metadataMap[QStringLiteral(u
"studio")] =
GetStudio();
1288 metadataMap[QStringLiteral(u
"description0")] = metadataMap[QStringLiteral(u
"description")] =
GetPlot();
1291 metadataMap[QStringLiteral(u
"cast")] =
GetDisplayCast(*this).join(
", ");
1294 metadataMap[QStringLiteral(u
"playcount")] = QString::number(
GetPlayCount());
1306 QString usingSE = QStringLiteral(u
"s%1e%2")
1309 metadataMap[QStringLiteral(u
"s##e##")] = metadataMap[QStringLiteral(u
"s00e00")] = usingSE;
1310 QString usingX = QStringLiteral(u
"%1x%2")
1313 metadataMap[QStringLiteral(u
"##x##")] = metadataMap[QStringLiteral(u
"00x00")] = usingX;
1317 metadataMap[QStringLiteral(u
"s##e##")] = metadataMap[QStringLiteral(u
"##x##")] = QString();
1318 metadataMap[QStringLiteral(u
"s00e00")] = metadataMap[QStringLiteral(u
"00x00")] = QString();
1319 metadataMap[QStringLiteral(u
"season")] = metadataMap[QStringLiteral(u
"episode")] = QString();
1326 metadataMap[QStringLiteral(u
"inetref")] =
GetInetRef();
1327 metadataMap[QStringLiteral(u
"homepage")] =
GetHomepage();
1328 metadataMap[QStringLiteral(u
"child_id")] = QString::number(
GetChildID());
1332 metadataMap[QStringLiteral(u
"category")] =
GetCategory();
1339 if (name == QStringLiteral(u
"filename"))
1341 if (name == QStringLiteral(u
"sortfilename"))
1343 if (name == QStringLiteral(u
"title"))
1345 if (name == QStringLiteral(u
"sorttitle"))
1347 if (name == QStringLiteral(u
"subtitle"))
1349 if (name == QStringLiteral(u
"sortsubtitle"))
1351 if (name == QStringLiteral(u
"tagline"))
1353 if (name == QStringLiteral(u
"director"))
1355 if (name == QStringLiteral(u
"studio"))
1357 if ((name == QStringLiteral(u
"description")) ||
1358 (name == QStringLiteral(u
"description0")))
1360 if (name == QStringLiteral(u
"genres"))
1362 if (name == QStringLiteral(u
"countries"))
1364 if (name == QStringLiteral(u
"cast"))
1366 if (name == QStringLiteral(u
"rating"))
1368 if (name == QStringLiteral(u
"length"))
1370 if (name == QStringLiteral(u
"playcount"))
1372 if (name == QStringLiteral(u
"year"))
1375 if (name == QStringLiteral(u
"releasedate"))
1378 if (name == QStringLiteral(u
"userrating"))
1383 if (name == QStringLiteral(u
"season"))
1385 if (name == QStringLiteral(u
"episode"))
1387 if ((name == QStringLiteral(u
"s##e##")) ||
1388 (name == QStringLiteral(u
"s00e00")))
1390 return QStringLiteral(u
"s%1e%2")
1394 if ((name == QStringLiteral(u
"##x##")) ||
1395 (name == QStringLiteral(u
"00x00")))
1397 return QStringLiteral(u
"%1x%2")
1403 if (name == QStringLiteral(u
"insertdate"))
1406 if (name == QStringLiteral(u
"inetref"))
1408 if (name == QStringLiteral(u
"homepage"))
1410 if (name == QStringLiteral(u
"child_id"))
1412 if (name == QStringLiteral(u
"browseable"))
1414 if (name == QStringLiteral(u
"watched"))
1416 if (name == QStringLiteral(u
"processed"))
1418 if (name == QStringLiteral(u
"category"))
1426 stateMap[QStringLiteral(u
"userratingstate")] =
1436 if (name == QStringLiteral(u
"trailerstate"))
1438 if (name == QStringLiteral(u
"userratingstate"))
1440 if (name == QStringLiteral(u
"watchedstate"))
1442 if (name == QStringLiteral(u
"videolevel"))
1459 metadataMap[
"coverfile"] =
"";
1460 metadataMap[
"screenshotfile"] =
"";
1461 metadataMap[
"bannerfile"] =
"";
1462 metadataMap[
"fanartfile"] =
"";
1463 metadataMap[
"filename"] =
"";
1464 metadataMap[
"sortfilename"] =
"";
1465 metadataMap[
"title"] =
"";
1466 metadataMap[
"sorttitle"] =
"";
1467 metadataMap[
"subtitle"] =
"";
1468 metadataMap[
"sortsubtitle"] =
"";
1469 metadataMap[
"tagline"] =
"";
1470 metadataMap[
"director"] =
"";
1471 metadataMap[
"studio"] =
"";
1472 metadataMap[
"description"] =
"";
1473 metadataMap[
"description0"] =
"";
1474 metadataMap[
"genres"] =
"";
1475 metadataMap[
"countries"] =
"";
1476 metadataMap[
"cast"] =
"";
1477 metadataMap[
"rating"] =
"";
1478 metadataMap[
"length"] =
"";
1479 metadataMap[
"playcount"] =
"";
1480 metadataMap[
"year"] =
"";
1481 metadataMap[
"releasedate"] =
"";
1482 metadataMap[
"userrating"] =
"";
1483 metadataMap[
"season"] =
"";
1484 metadataMap[
"episode"] =
"";
1485 metadataMap[
"s##e##"] =
"";
1486 metadataMap[
"##x##"] =
"";
1487 metadataMap[
"trailerstate"] =
"";
1488 metadataMap[
"userratingstate"] =
"";
1489 metadataMap[
"watchedstate"] =
"";
1490 metadataMap[
"videolevel"] =
"";
1491 metadataMap[
"insertdate"] =
"";
1492 metadataMap[
"inetref"] =
"";
1493 metadataMap[
"homepage"] =
"";
1494 metadataMap[
"child_id"] =
"";
1495 metadataMap[
"browseable"] =
"";
1496 metadataMap[
"watched"] =
"";
1497 metadataMap[
"category"] =
"";
1498 metadataMap[
"processed"] =
"";
1503 if (data ==
nullptr)
1506 QString result = metadata->
GetText(name);
1507 if (!result.isEmpty())
1509 result = metadata->GetImage(name);
1510 if (!result.isEmpty())
1512 return metadata->GetState(name);
1517 if (data ==
nullptr)
1525 if (data ==
nullptr)
1964 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)