Repository: xbmc/xbmc
File: xbmc/Util.cpp (C++)

Code before:
  std::string id;
  return CleanString(fileName, title, titleAndYear, year, db, id, removeExtension, cleanChars);
}

void CUtil::CleanString(const std::string& strFileName,
                        std::string& strTitle,
                        std::string& strTitleAndYear,
                        std::string& strYear,
                        std::string& database,
                        std::string& identifier,
                        bool bRemoveExtension /* = false */,
                        bool bCleanChars /* = true */)
{
  strTitleAndYear = strFileName;

  if (strFileName == "..")
   return;

  const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
  const std::vector<std::string> &regexps = advancedSettings->m_videoCleanStringRegExps;

  CRegExp reTags(true, CRegExp::autoUtf8);
  CRegExp reYear(false, CRegExp::autoUtf8);
  CRegExp reIdentifier(true, CRegExp::autoUtf8);

  if (reIdentifier.RegComp(R"((\{|\[)\w+(-|=)\w+(\}|\]))"))
  {
   int match = reIdentifier.RegFind(strTitleAndYear);
   if (match >= 0)
   {
      identifier = reIdentifier.GetMatch(0);
      strTitleAndYear.erase(match, identifier.length());
      identifier = identifier.substr(1, identifier.length() - 2);
      const std::vector<std::string> delimitors = {"=", "-"};
      std::vector<std::string> parts = StringUtils::Split(identifier, delimitors);
      database = parts[0];
      identifier = parts[1];
      StringUtils::ToLower(identifier);
      StringUtils::ToLower(database);
      StringUtils::TrimRight(database, "id");
   }
  }

  if (!reYear.RegComp(advancedSettings->m_videoCleanDateTimeRegExp))
  {
    CLog::Log(LOGERROR, "{}: Invalid datetime clean RegExp:'{}'", __FUNCTION__,
              advancedSettings->m_videoCleanDateTimeRegExp);
  }
  else
  {
    if (reYear.RegFind(strTitleAndYear.c_str()) >= 0)

Review comment (suggestion):
There is a lot of hardcoding going on here.
- move the regex pattern to an advanced setting
- use regex capture groups to gather the database / idType and identifier
- "id" should be a part of the regex pattern _outside_ of a capture group, if trimming is desired. I'm not sure it should be in the default pattern

Code after:

  pos = std::string::npos;
  number.clear();

  // Handle Disc, Disk and locale specific spellings
  std::string discStr{StringUtils::Format("/{} ", g_localizeStrings.Get(427))};
  size_t discPos = path.rfind(discStr);

  if (discPos == std::string::npos)
  {
    discStr = "/Disc ";
    discPos = path.rfind(discStr);
  }

  if (discPos == std::string::npos)
  {
    discStr = "/Disk ";
    discPos = path.rfind(discStr);
  }

  if (discPos != std::string::npos)
  {
    // Check remainder of path is numeric (eg. Disc 1)
    const std::string discNum{path.substr(discPos + discStr.size())};
    if (discNum.find_first_not_of("0123456789") == std::string::npos)
    {
      pos = discPos;
      number = discNum;
    }
  }
}
} // unnamed namespace

std::string CUtil::RemoveTrailingDiscNumberSegmentFromPath(std::string path)
{
  size_t discPos{std::string::npos};
  std::string discNum;
  GetTrailingDiscNumberSegmentInfoFromPath(path, discPos, discNum);

  if (discPos != std::string::npos)
    path.erase(discPos);

  return path;
}

std::string CUtil::GetDiscNumberFromPath(const std::string& path)
{
  size_t discPos{std::string::npos};
  std::string discNum;
  GetTrailingDiscNumberSegmentInfoFromPath(path, discPos, discNum);
  return discNum;