Repository: xbmc/xbmc
File: xbmc/video/VideoInfoScanner.cpp (C++)

Code before:
        const int64_t size{pItem->GetSize()};
        if (size)
          digest.Update(std::to_string(size));

        const CDateTime& dateTime{pItem->GetDateTime()};
        if (dateTime.IsValid())
        {
          digest.Update(StringUtils::Format("{:02}.{:02}.{:04}", dateTime.GetDay(),
                                            dateTime.GetMonth(), dateTime.GetYear()));
        }
      }
      else
      {
        const int64_t size{pItem->GetSize()};
        digest.Update(&size, sizeof(size));
        // linux and windows platform don't follow the same output format 
        // (linux return a zero value for milliseconds member).
        // for consistency, use string format instead and discard milliseconds 
        // field. Unless a modification occur during the 1 second window when 
        // kodi hash and update this particular file, we are safe.
        const CDateTime& dateTime{pItem->GetDateTime()};
        if (dateTime.IsValid())
        {
          digest.Update(StringUtils::Format("{:02}.{:02}.{:04} {:02}:{:02}:{:02}", 
                                            dateTime.GetDay(), dateTime.GetMonth(), dateTime.GetYear(), 
                                            dateTime.GetHour(), dateTime.GetMinute(), dateTime.GetSecond()));
        }
      }
      if (IsVideo(*pItem) && !PLAYLIST::IsPlayList(*pItem) && !pItem->IsNFO())
        count++;
    }
    hash = digest.Finalize();
    return count;
  }

  bool CVideoInfoScanner::CanFastHash(const CFileItemList &items, const std::vector<std::string> &excludes) const
  {
    if (!m_advancedSettings->m_bVideoLibraryUseFastHash || items.IsPlugin())
      return false;

    for (int i = 0; i < items.Size(); ++i)
    {
      if (items[i]->IsFolder() && !CUtil::ExcludeFileOrFolder(items[i]->GetPath(), excludes))
        return false;
    }
    return true;
  }

  std::string CVideoInfoScanner::GetFastHash(const std::string &directory,
      const std::vector<std::string> &excludes) const
  {

Review comment (performance):
Looking at the details of CDateTime, more efficient for similar and equally valid result:
```suggestion
          digest.Update(dateTime.GetAsDBDateTime());
```
However most likely even more efficient, convert the CDateTime to a time_t, which has a resolution of the second, which should align linux and Windows behavior, without the relatively more costly conversion to text.
```
        time_t tt{};
        pItem->GetDateTime().GetAsTime(tt);
        digest.Update(&tt, sizeof(tt));
```

... with a nice explanation comment, similar to what you added.

Code after:
      EPISODELIST& files,
      const ADDON::ScraperPtr& scraper,
      bool useLocal,
      const CVideoInfoTag& showInfo,
      CGUIDialogProgress* pDlgProgress /* = NULL */)
  {
    if (pDlgProgress)
    {
      pDlgProgress->SetLine(1, CVariant{20361}); // Loading episode details
      pDlgProgress->SetPercentage(0);
      pDlgProgress->ShowProgressBar(true);
      pDlgProgress->Progress();
    }

    EPISODELIST episodes;
    bool hasEpisodeGuide = false;

    int iMax = files.size();
    int iCurr = 1;
    for (EPISODELIST::iterator file = files.begin(); file != files.end(); ++file)
    {
      if (pDlgProgress)
      {
        pDlgProgress->SetLine(1, CVariant{20361}); // Loading episode details
        pDlgProgress->SetLine(2, StringUtils::Format("{} {}", g_localizeStrings.Get(20373),
                                                     file->iSeason)); // Season x
        pDlgProgress->SetLine(3, StringUtils::Format("{} {}", g_localizeStrings.Get(20359),
                                                     file->iEpisode)); // Episode y
        pDlgProgress->SetPercentage((int)((float)(iCurr++)/iMax*100));
        pDlgProgress->Progress();
      }
      if (m_handle)
        m_handle->SetPercentage(100.f*iCurr++/iMax);

      if ((pDlgProgress && pDlgProgress->IsCanceled()) || m_bStop)
        return InfoRet::CANCELLED;

      if (m_database.GetEpisodeId(file->strPath, file->iEpisode, file->iSeason) > -1)
      {
        if (m_handle)
          m_handle->SetText(g_localizeStrings.Get(20415));
        continue;
      }

      CFileItem item;
      if (file->item)
        item = *file->item;
      else
      {
        item.SetPath(file->strPath);
        item.GetVideoInfoTag()->m_iSeason = file->iSeason;