2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "network/Network.h"
10 #include "network/NetworkFileItemClassify.h"
11 #include "playlists/PlayListFileItemClassify.h"
12 #include "video/VideoFileItemClassify.h"
13 #if defined(TARGET_DARWIN)
14 #include <sys/param.h>
15 #include <mach-o/dyld.h>
18 #if defined(TARGET_FREEBSD)
19 #include <sys/param.h>
20 #include <sys/sysctl.h>
24 #include <sys/types.h>
29 #if defined(TARGET_ANDROID)
30 #include <androidjni/ApplicationInfo.h>
31 #include "platform/android/activity/XBMCApp.h"
32 #include "CompileInfo.h"
34 #include "ServiceBroker.h"
36 #include "addons/VFSEntry.h"
37 #include "filesystem/Directory.h"
38 #include "filesystem/MultiPathDirectory.h"
39 #include "filesystem/PVRDirectory.h"
40 #include "filesystem/RSSDirectory.h"
41 #include "filesystem/SpecialProtocol.h"
42 #include "filesystem/StackDirectory.h"
48 #include "filesystem/UPnPDirectory.h"
50 #include "profiles/ProfileManager.h"
51 #include "utils/RegExp.h"
52 #include "windowing/GraphicContext.h"
53 #include "guilib/TextureManager.h"
54 #include "storage/MediaManager.h"
56 #include "utils/CharsetConverter.h"
57 #include "WIN32Util.h"
59 #if defined(TARGET_DARWIN)
60 #include "CompileInfo.h"
61 #include "platform/darwin/DarwinUtils.h"
64 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleStream.h"
65 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleTagSami.h"
66 #include "filesystem/File.h"
67 #include "guilib/LocalizeStrings.h"
68 #include "platform/Environment.h"
69 #include "settings/AdvancedSettings.h"
70 #include "settings/MediaSettings.h"
71 #include "settings/Settings.h"
72 #include "settings/SettingsComponent.h"
73 #include "utils/Digest.h"
74 #include "utils/FileExtensionProvider.h"
75 #include "utils/FontUtils.h"
76 #include "utils/LangCodeExpander.h"
77 #include "utils/StringUtils.h"
78 #include "utils/TimeUtils.h"
79 #include "utils/URIUtils.h"
80 #include "utils/log.h"
81 #include "video/VideoDatabase.h"
82 #include "video/VideoInfoTag.h"
84 #include <sys/capability.h>
87 #include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h"
91 #ifdef HAS_OPTICAL_DRIVE
92 using namespace MEDIA_DETECT
;
95 using namespace XFILE
;
96 using KODI::UTILITY::CDigest
;
99 #if !defined(TARGET_WINDOWS)
100 unsigned int CUtil::s_randomSeed
= time(NULL
);
105 #ifdef TARGET_WINDOWS
106 bool IsDirectoryValidRoot(std::wstring path
)
108 path
+= L
"\\system\\settings\\settings.xml";
109 #if defined(TARGET_WINDOWS_STORE)
110 auto h
= CreateFile2(path
.c_str(), GENERIC_READ
, 0, OPEN_EXISTING
, NULL
);
112 auto h
= CreateFileW(path
.c_str(), GENERIC_READ
, 0, nullptr,
113 OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
115 if (h
!= INVALID_HANDLE_VALUE
)
124 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
126 std::wstring strPathW
;
128 // Environment variable was set and we have a path
129 // Let's make sure it's not relative and test it
130 // so it's actually pointing to a directory containing
132 if (strPath
.find("..") != std::string::npos
)
134 //expand potential relative path to full path
135 g_charsetConverter
.utf8ToW(strPath
, strPathW
, false);
136 CWIN32Util::AddExtraLongPathPrefix(strPathW
);
137 auto bufSize
= GetFullPathNameW(strPathW
.c_str(), 0, nullptr, nullptr);
140 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
141 if (GetFullPathNameW(strPathW
.c_str(), bufSize
, buf
.get(), nullptr) <= bufSize
- 1)
143 strPathW
= buf
.get();
144 CWIN32Util::RemoveExtraLongPathPrefix(strPathW
);
146 if (IsDirectoryValidRoot(strPathW
))
148 g_charsetConverter
.wToUTF8(strPathW
, strPath
);
155 // Okay se no environment variable is set, let's
156 // grab the executable path and check if it's being
157 // run from a directory containing our stuff
158 strPath
= CUtil::ResolveExecutablePath();
159 auto last_sep
= strPath
.find_last_of(PATH_SEPARATOR_CHAR
);
160 if (last_sep
!= std::string::npos
)
161 strPath
.resize(last_sep
);
163 g_charsetConverter
.utf8ToW(strPath
, strPathW
);
164 if (IsDirectoryValidRoot(strPathW
))
167 // Still nothing, let's check the current working
168 // directory and see if it points to a directory
169 // with our stuff in it. This bit should never be
170 // needed when running on a users system, it's intended
171 // to make our dev environment easier.
172 auto bufSize
= GetCurrentDirectoryW(0, nullptr);
175 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
176 if (0 != GetCurrentDirectoryW(bufSize
, buf
.get()))
178 std::string currentDirectory
;
179 std::wstring
currentDirectoryW(buf
.get());
180 CWIN32Util::RemoveExtraLongPathPrefix(currentDirectoryW
);
182 if (IsDirectoryValidRoot(currentDirectoryW
))
184 g_charsetConverter
.wToUTF8(currentDirectoryW
, currentDirectory
);
185 return currentDirectory
;
190 // If we ended up here we're most likely screwed
191 // we will crash in a few seconds
195 #if defined(TARGET_DARWIN)
196 #if !defined(TARGET_DARWIN_EMBEDDED)
197 bool IsDirectoryValidRoot(std::string path
)
199 path
+= "/system/settings/settings.xml";
200 return CFile::Exists(path
);
204 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
208 auto strHomePath
= CUtil::ResolveExecutablePath();
210 char given_path
[2 * MAXPATHLEN
];
211 size_t path_size
= 2 * MAXPATHLEN
;
213 result
= CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
216 // Move backwards to last /.
217 for (int n
= strlen(given_path
) - 1; given_path
[n
] != '/'; n
--)
218 given_path
[n
] = '\0';
220 #if defined(TARGET_DARWIN_EMBEDDED)
221 strcat(given_path
, "/AppData/AppHome/");
223 // Assume local path inside application bundle.
224 strcat(given_path
, "../Resources/");
225 strcat(given_path
, CCompileInfo::GetAppName());
226 strcat(given_path
, "/");
228 // if this path doesn't exist we
229 // might not be started from the app bundle
230 // but from the debugger/xcode. Lets
231 // see if this assumption is valid
232 if (!CDirectory::Exists(given_path
))
234 std::string given_path_stdstr
= CUtil::ResolveExecutablePath();
235 // try to find the correct folder by going back
236 // in the executable path until settings.xml was found
237 bool validRoot
= false;
240 given_path_stdstr
= URIUtils::GetParentPath(given_path_stdstr
);
241 validRoot
= IsDirectoryValidRoot(given_path_stdstr
);
243 while(given_path_stdstr
.length() > 0 && !validRoot
);
244 strncpy(given_path
, given_path_stdstr
.c_str(), sizeof(given_path
)-1);
249 // Convert to real path.
250 char real_path
[2 * MAXPATHLEN
];
251 if (realpath(given_path
, real_path
) != NULL
)
257 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
258 if (last_sep
!= std::string::npos
)
259 strPath
= strHomePath
.substr(0, last_sep
);
261 strPath
= strHomePath
;
267 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
268 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
272 auto strHomePath
= CUtil::ResolveExecutablePath();
273 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
274 if (last_sep
!= std::string::npos
)
275 strPath
= strHomePath
.substr(0, last_sep
);
277 strPath
= strHomePath
;
279 /* Change strPath accordingly when target is KODI_HOME and when INSTALL_PATH
280 * and BIN_INSTALL_PATH differ
282 std::string installPath
= INSTALL_PATH
;
283 std::string binInstallPath
= BIN_INSTALL_PATH
;
285 if (strTarget
.empty() && installPath
.compare(binInstallPath
))
287 int pos
= strPath
.length() - binInstallPath
.length();
288 std::string tmp
= strPath
;
290 if (!tmp
.compare(binInstallPath
))
292 strPath
.erase(pos
, strPath
.length());
293 strPath
.append(installPath
);
302 std::string
CUtil::GetTitleFromPath(const std::string
& strFileNameAndPath
, bool bIsFolder
/* = false */)
304 CURL
pathToUrl(strFileNameAndPath
);
305 return GetTitleFromPath(pathToUrl
, bIsFolder
);
308 std::string
CUtil::GetTitleFromPath(const CURL
& url
, bool bIsFolder
/* = false */)
310 // use above to get the filename
311 std::string
path(url
.Get());
312 URIUtils::RemoveSlashAtEnd(path
);
313 std::string strFilename
= URIUtils::GetFileName(path
);
317 if (url
.IsProtocol("upnp"))
318 strFilename
= CUPnPDirectory::GetFriendlyName(url
);
321 if (url
.IsProtocol("rss") || url
.IsProtocol("rsss"))
325 if(dir
.GetDirectory(url
, items
) && !items
.m_strTitle
.empty())
326 return items
.m_strTitle
;
330 else if (url
.IsProtocol("shout"))
332 const std::string strFileNameAndPath
= url
.Get();
333 const size_t genre
= strFileNameAndPath
.find_first_of('=');
334 if(genre
== std::string::npos
)
335 strFilename
= g_localizeStrings
.Get(260);
337 strFilename
= g_localizeStrings
.Get(260) + " - " + strFileNameAndPath
.substr(genre
+1).c_str();
340 // Windows SMB Network (SMB)
341 else if (url
.IsProtocol("smb") && strFilename
.empty())
343 if (url
.GetHostName().empty())
345 strFilename
= g_localizeStrings
.Get(20171);
349 strFilename
= url
.GetHostName();
354 else if (url
.IsProtocol("sources"))
355 strFilename
= g_localizeStrings
.Get(744);
358 else if (StringUtils::StartsWith(path
, "special://musicplaylists"))
359 strFilename
= g_localizeStrings
.Get(136);
362 else if (StringUtils::StartsWith(path
, "special://videoplaylists"))
363 strFilename
= g_localizeStrings
.Get(136);
365 else if (URIUtils::HasParentInHostname(url
) && strFilename
.empty())
366 strFilename
= URIUtils::GetFileName(url
.GetHostName());
368 // now remove the extension if needed
369 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS
) && !bIsFolder
)
371 URIUtils::RemoveExtension(strFilename
);
375 // URLDecode since the original path may be an URL
376 strFilename
= CURL::Decode(strFilename
);
382 void GetTrailingDiscNumberSegmentInfoFromPath(const std::string
& pathIn
,
386 std::string path
{pathIn
};
387 URIUtils::RemoveSlashAtEnd(path
);
389 pos
= std::string::npos
;
392 // Handle Disc, Disk and locale specific spellings
393 std::string discStr
{StringUtils::Format("/{} ", g_localizeStrings
.Get(427))};
394 size_t discPos
= path
.rfind(discStr
);
396 if (discPos
== std::string::npos
)
399 discPos
= path
.rfind(discStr
);
402 if (discPos
== std::string::npos
)
405 discPos
= path
.rfind(discStr
);
408 if (discPos
!= std::string::npos
)
410 // Check remainder of path is numeric (eg. Disc 1)
411 const std::string discNum
{path
.substr(discPos
+ discStr
.size())};
412 if (discNum
.find_first_not_of("0123456789") == std::string::npos
)
419 } // unnamed namespace
421 std::string
CUtil::RemoveTrailingDiscNumberSegmentFromPath(std::string path
)
423 size_t discPos
{std::string::npos
};
425 GetTrailingDiscNumberSegmentInfoFromPath(path
, discPos
, discNum
);
427 if (discPos
!= std::string::npos
)
433 std::string
CUtil::GetDiscNumberFromPath(const std::string
& path
)
435 size_t discPos
{std::string::npos
};
437 GetTrailingDiscNumberSegmentInfoFromPath(path
, discPos
, discNum
);
441 bool CUtil::GetFilenameIdentifier(const std::string
& fileName
,
442 std::string
& identifierType
,
443 std::string
& identifier
)
446 return GetFilenameIdentifier(fileName
, identifierType
, identifier
, match
);
449 bool CUtil::GetFilenameIdentifier(const std::string
& fileName
,
450 std::string
& identifierType
,
451 std::string
& identifier
,
454 CRegExp
reIdentifier(true, CRegExp::autoUtf8
);
456 const std::shared_ptr
<CAdvancedSettings
> advancedSettings
=
457 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
458 if (!reIdentifier
.RegComp(advancedSettings
->m_videoFilenameIdentifierRegExp
))
460 CLog::LogF(LOGERROR
, "Invalid filename identifier RegExp:'{}'",
461 advancedSettings
->m_videoFilenameIdentifierRegExp
);
466 if (reIdentifier
.RegComp(advancedSettings
->m_videoFilenameIdentifierRegExp
))
468 if (reIdentifier
.RegFind(fileName
) >= 0)
470 match
= reIdentifier
.GetMatch(0);
471 identifierType
= reIdentifier
.GetMatch(1);
472 identifier
= reIdentifier
.GetMatch(2);
473 StringUtils::ToLower(identifierType
);
481 bool CUtil::HasFilenameIdentifier(const std::string
& fileName
)
483 std::string identifierType
;
484 std::string identifier
;
485 return GetFilenameIdentifier(fileName
, identifierType
, identifier
);
488 void CUtil::CleanString(const std::string
& strFileName
,
489 std::string
& strTitle
,
490 std::string
& strTitleAndYear
,
491 std::string
& strYear
,
492 bool bRemoveExtension
/* = false */,
493 bool bCleanChars
/* = true */)
495 strTitleAndYear
= strFileName
;
497 if (strFileName
== "..")
500 std::string identifier
;
501 std::string identifierType
;
502 std::string identifierMatch
;
503 if (GetFilenameIdentifier(strFileName
, identifierType
, identifier
, identifierMatch
))
504 StringUtils::Replace(strTitleAndYear
, identifierMatch
, "");
506 const std::shared_ptr
<CAdvancedSettings
> advancedSettings
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
507 const std::vector
<std::string
> ®exps
= advancedSettings
->m_videoCleanStringRegExps
;
509 CRegExp
reTags(true, CRegExp::autoUtf8
);
510 CRegExp
reYear(false, CRegExp::autoUtf8
);
512 if (!reYear
.RegComp(advancedSettings
->m_videoCleanDateTimeRegExp
))
514 CLog::Log(LOGERROR
, "{}: Invalid datetime clean RegExp:'{}'", __FUNCTION__
,
515 advancedSettings
->m_videoCleanDateTimeRegExp
);
519 if (reYear
.RegFind(strTitleAndYear
.c_str()) >= 0)
521 strTitleAndYear
= reYear
.GetMatch(1);
522 strYear
= reYear
.GetMatch(2);
526 URIUtils::RemoveExtension(strTitleAndYear
);
528 for (const auto ®exp
: regexps
)
530 if (!reTags
.RegComp(regexp
.c_str()))
531 { // invalid regexp - complain in logs
532 CLog::Log(LOGERROR
, "{}: Invalid string clean RegExp:'{}'", __FUNCTION__
, regexp
);
536 if ((j
=reTags
.RegFind(strTitleAndYear
.c_str())) > 0)
537 strTitleAndYear
.resize(j
);
540 // final cleanup - special characters used instead of spaces:
541 // all '_' tokens should be replaced by spaces
542 // if the file contains no spaces, all '.' tokens should be replaced by
543 // spaces - one possibility of a mistake here could be something like:
544 // "Dr..StrangeLove" - hopefully no one would have anything like this.
547 bool initialDots
= true;
548 bool alreadyContainsSpace
= (strTitleAndYear
.find(' ') != std::string::npos
);
550 for (char &c
: strTitleAndYear
)
555 if ((c
== '_') || ((!alreadyContainsSpace
) && !initialDots
&& (c
== '.')))
562 StringUtils::Trim(strTitleAndYear
);
563 strTitle
= strTitleAndYear
;
566 if (!strYear
.empty())
567 strTitleAndYear
= strTitle
+ " (" + strYear
+ ")";
569 // restore extension if needed
570 if (!bRemoveExtension
)
571 strTitleAndYear
+= URIUtils::GetExtension(strFileName
);
574 void CUtil::GetQualifiedFilename(const std::string
&strBasePath
, std::string
&strFilename
)
576 // Check if the filename is a fully qualified URL such as protocol://path/to/file
577 CURL
plItemUrl(strFilename
);
578 if (!plItemUrl
.GetProtocol().empty())
581 // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
582 if (strFilename
.size() > 1)
584 if ( (strFilename
[1] == ':') || (strFilename
[0] == '/') )
586 if ( strFilename
[1] == ':' || (strFilename
[0] == '\\' && strFilename
[1] == '\\'))
590 // add to base path and then clean
591 strFilename
= URIUtils::AddFileToFolder(strBasePath
, strFilename
);
593 // get rid of any /./ or \.\ that happen to be there
594 StringUtils::Replace(strFilename
, "\\.\\", "\\");
595 StringUtils::Replace(strFilename
, "/./", "/");
597 // now find any "\\..\\" and remove them via GetParentPath
599 while ((pos
= strFilename
.find("/../")) != std::string::npos
)
601 std::string basePath
= strFilename
.substr(0, pos
+ 1);
602 strFilename
.erase(0, pos
+ 4);
603 basePath
= URIUtils::GetParentPath(basePath
);
604 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
606 while ((pos
= strFilename
.find("\\..\\")) != std::string::npos
)
608 std::string basePath
= strFilename
.substr(0, pos
+ 1);
609 strFilename
.erase(0, pos
+ 4);
610 basePath
= URIUtils::GetParentPath(basePath
);
611 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
615 void CUtil::RunShortcut(const char* szShortcutPath
)
619 std::string
CUtil::GetHomePath(const std::string
& strTarget
)
621 auto strPath
= CEnvironment::getenv(strTarget
);
623 return ::GetHomePath(strTarget
, strPath
);
626 bool CUtil::IsPicture(const std::string
& strFile
)
628 return URIUtils::HasExtension(strFile
,
629 CServiceBroker::GetFileExtensionProvider().GetPictureExtensions()+ "|.tbn|.dds");
632 std::string
CUtil::GetSplashPath()
634 std::array
<std::string
, 4> candidates
{{ "special://home/media/splash.jpg", "special://home/media/splash.png", "special://xbmc/media/splash.jpg", "special://xbmc/media/splash.png" }};
635 auto it
= std::find_if(candidates
.begin(), candidates
.end(), [](std::string
const& file
) { return XFILE::CFile::Exists(file
); });
636 if (it
== candidates
.end())
637 throw std::runtime_error("No splash image found");
638 return CSpecialProtocol::TranslatePathConvertCase(*it
);
641 bool CUtil::ExcludeFileOrFolder(const std::string
& strFileOrFolder
, const std::vector
<std::string
>& regexps
)
643 if (strFileOrFolder
.empty())
646 CRegExp
regExExcludes(true, CRegExp::autoUtf8
); // case insensitive regex
648 for (const auto ®exp
: regexps
)
650 if (!regExExcludes
.RegComp(regexp
.c_str()))
651 { // invalid regexp - complain in logs
652 CLog::Log(LOGERROR
, "{}: Invalid exclude RegExp:'{}'", __FUNCTION__
, regexp
);
655 if (regExExcludes
.RegFind(strFileOrFolder
) > -1)
657 CLog::LogF(LOGDEBUG
, "File '{}' excluded. (Matches exclude rule RegExp: '{}')", CURL::GetRedacted(strFileOrFolder
), regexp
);
664 void CUtil::GetFileAndProtocol(const std::string
& strURL
, std::string
& strDir
)
667 if (!URIUtils::IsRemote(strURL
)) return ;
668 if (URIUtils::IsDVD(strURL
)) return ;
671 strDir
= StringUtils::Format("{}://{}", url
.GetProtocol(), url
.GetFileName());
674 int CUtil::GetDVDIfoTitle(const std::string
& strFile
)
676 std::string strFilename
= URIUtils::GetFileName(strFile
);
677 if (StringUtils::EqualsNoCase(strFilename
, "video_ts.ifo")) return 0;
679 return atoi(strFilename
.substr(4, 2).c_str());
682 std::string
CUtil::GetFileDigest(const std::string
& strPath
, KODI::UTILITY::CDigest::Type type
)
686 if (file
.Open(strPath
))
688 CDigest digest
{type
};
692 ssize_t read
= file
.Read(temp
,1024);
695 digest
.Update(temp
,read
);
697 result
= digest
.Finalize();
704 bool CUtil::GetDirectoryName(const std::string
& strFileName
, std::string
& strDescription
)
706 std::string strFName
= URIUtils::GetFileName(strFileName
);
707 strDescription
= URIUtils::GetDirectory(strFileName
);
708 URIUtils::RemoveSlashAtEnd(strDescription
);
710 size_t iPos
= strDescription
.find_last_of("/\\");
711 if (iPos
!= std::string::npos
)
712 strDescription
= strDescription
.substr(iPos
+ 1);
713 else if (strDescription
.size() <= 0)
714 strDescription
= strFName
;
718 void CUtil::GetDVDDriveIcon(const std::string
& strPath
, std::string
& strIcon
)
720 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
722 strIcon
= "DefaultDVDEmpty.png";
726 CFileItem item
= CFileItem(strPath
, false);
730 strIcon
= "DefaultBluray.png";
734 if ( URIUtils::IsDVD(strPath
) )
736 strIcon
= "DefaultDVDFull.png";
740 if ( URIUtils::IsISO9660(strPath
) )
742 #ifdef HAS_OPTICAL_DRIVE
743 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo();
744 if ( pInfo
!= NULL
&& pInfo
->IsVideoCd( 1 ) )
746 strIcon
= "DefaultVCD.png";
750 strIcon
= "DefaultDVDRom.png";
754 if ( URIUtils::IsCDDA(strPath
) )
756 strIcon
= "DefaultCDDA.png";
761 void CUtil::RemoveTempFiles()
763 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
765 std::string searchPath
= profileManager
->GetDatabaseFolder();
767 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".tmp", DIR_FLAG_NO_FILE_DIRS
))
770 for (const auto &item
: items
)
772 if (item
->m_bIsFolder
)
774 XFILE::CFile::Delete(item
->GetPath());
778 void CUtil::ClearSubtitles()
782 CDirectory::GetDirectory("special://temp/",items
, "", DIR_FLAG_DEFAULTS
);
783 for (const auto &item
: items
)
785 if (!item
->m_bIsFolder
)
787 if (item
->GetPath().find("subtitle") != std::string::npos
||
788 item
->GetPath().find("vobsub_queue") != std::string::npos
)
790 CLog::Log(LOGDEBUG
, "{} - Deleting temporary subtitle {}", __FUNCTION__
, item
->GetPath());
791 CFile::Delete(item
->GetPath());
797 int64_t CUtil::ToInt64(uint32_t high
, uint32_t low
)
807 \brief Finds next unused filename that matches padded int format identifier provided
808 \param[in] fn_template filename template consisting of a padded int format identifier (eg screenshot%03d)
809 \param[in] max maximum number to search for available name
810 \return "" on failure, string next available name matching format identifier on success
813 std::string
CUtil::GetNextFilename(const std::string
&fn_template
, int max
)
815 std::string searchPath
= URIUtils::GetDirectory(fn_template
);
816 std::string mask
= URIUtils::GetExtension(fn_template
);
817 std::string name
= StringUtils::Format(fn_template
, 0);
820 if (!CDirectory::GetDirectory(searchPath
, items
, mask
, DIR_FLAG_NO_FILE_DIRS
))
823 items
.SetFastLookup(true);
824 for (int i
= 0; i
<= max
; i
++)
826 std::string name
= StringUtils::Format(fn_template
, i
);
827 if (!items
.Get(name
))
833 std::string
CUtil::GetNextPathname(const std::string
&path_template
, int max
)
835 if (path_template
.find("%04d") == std::string::npos
)
838 for (int i
= 0; i
<= max
; i
++)
840 std::string name
= StringUtils::Format(path_template
, i
);
841 if (!CFile::Exists(name
) && !CDirectory::Exists(name
))
847 void CUtil::StatToStatI64(struct _stati64
*result
, struct stat
*stat
)
849 result
->st_dev
= stat
->st_dev
;
850 result
->st_ino
= stat
->st_ino
;
851 result
->st_mode
= stat
->st_mode
;
852 result
->st_nlink
= stat
->st_nlink
;
853 result
->st_uid
= stat
->st_uid
;
854 result
->st_gid
= stat
->st_gid
;
855 result
->st_rdev
= stat
->st_rdev
;
856 result
->st_size
= (int64_t)stat
->st_size
;
859 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
860 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
861 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
863 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
864 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
865 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
869 void CUtil::Stat64ToStatI64(struct _stati64
*result
, struct __stat64
*stat
)
871 result
->st_dev
= stat
->st_dev
;
872 result
->st_ino
= stat
->st_ino
;
873 result
->st_mode
= stat
->st_mode
;
874 result
->st_nlink
= stat
->st_nlink
;
875 result
->st_uid
= stat
->st_uid
;
876 result
->st_gid
= stat
->st_gid
;
877 result
->st_rdev
= stat
->st_rdev
;
878 result
->st_size
= stat
->st_size
;
880 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
881 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
882 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
884 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
885 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
886 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
890 void CUtil::StatI64ToStat64(struct __stat64
*result
, struct _stati64
*stat
)
892 result
->st_dev
= stat
->st_dev
;
893 result
->st_ino
= stat
->st_ino
;
894 result
->st_mode
= stat
->st_mode
;
895 result
->st_nlink
= stat
->st_nlink
;
896 result
->st_uid
= stat
->st_uid
;
897 result
->st_gid
= stat
->st_gid
;
898 result
->st_rdev
= stat
->st_rdev
;
899 result
->st_size
= stat
->st_size
;
901 result
->st_atime
= stat
->st_atime
;
902 result
->st_mtime
= stat
->st_mtime
;
903 result
->st_ctime
= stat
->st_ctime
;
905 result
->st_atime
= stat
->_st_atime
;
906 result
->st_mtime
= stat
->_st_mtime
;
907 result
->st_ctime
= stat
->_st_ctime
;
911 void CUtil::StatToStat64(struct __stat64
*result
, const struct stat
*stat
)
913 memset(result
, 0, sizeof(*result
));
914 result
->st_dev
= stat
->st_dev
;
915 result
->st_ino
= stat
->st_ino
;
916 result
->st_mode
= stat
->st_mode
;
917 result
->st_nlink
= stat
->st_nlink
;
918 result
->st_uid
= stat
->st_uid
;
919 result
->st_gid
= stat
->st_gid
;
920 result
->st_rdev
= stat
->st_rdev
;
921 result
->st_size
= stat
->st_size
;
922 result
->st_atime
= stat
->st_atime
;
923 result
->st_mtime
= stat
->st_mtime
;
924 result
->st_ctime
= stat
->st_ctime
;
927 void CUtil::Stat64ToStat(struct stat
*result
, struct __stat64
*stat
)
929 result
->st_dev
= stat
->st_dev
;
930 result
->st_ino
= stat
->st_ino
;
931 result
->st_mode
= stat
->st_mode
;
932 result
->st_nlink
= stat
->st_nlink
;
933 result
->st_uid
= stat
->st_uid
;
934 result
->st_gid
= stat
->st_gid
;
935 result
->st_rdev
= stat
->st_rdev
;
937 if (stat
->st_size
<= LONG_MAX
)
938 result
->st_size
= (_off_t
)stat
->st_size
;
940 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
941 result
->st_size
= stat
->st_size
;
946 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
948 result
->st_atime
= (time_t)(stat
->st_atime
& 0xFFFFFFFF);
949 result
->st_mtime
= (time_t)(stat
->st_mtime
& 0xFFFFFFFF);
950 result
->st_ctime
= (time_t)(stat
->st_ctime
& 0xFFFFFFFF);
953 #ifdef TARGET_WINDOWS
954 void CUtil::Stat64ToStat64i32(struct _stat64i32
*result
, struct __stat64
*stat
)
956 result
->st_dev
= stat
->st_dev
;
957 result
->st_ino
= stat
->st_ino
;
958 result
->st_mode
= stat
->st_mode
;
959 result
->st_nlink
= stat
->st_nlink
;
960 result
->st_uid
= stat
->st_uid
;
961 result
->st_gid
= stat
->st_gid
;
962 result
->st_rdev
= stat
->st_rdev
;
964 if (stat
->st_size
<= LONG_MAX
)
965 result
->st_size
= (_off_t
)stat
->st_size
;
967 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
968 result
->st_size
= stat
->st_size
;
973 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
976 result
->st_atime
= stat
->st_atime
;
977 result
->st_mtime
= stat
->st_mtime
;
978 result
->st_ctime
= stat
->st_ctime
;
980 result
->st_atime
= stat
->_st_atime
;
981 result
->st_mtime
= stat
->_st_mtime
;
982 result
->st_ctime
= stat
->_st_ctime
;
987 bool CUtil::CreateDirectoryEx(const std::string
& strPath
)
989 // Function to create all directories at once instead
990 // of calling CreateDirectory for every subdir.
991 // Creates the directory and subdirectories if needed.
993 // return true if directory already exist
994 if (CDirectory::Exists(strPath
)) return true;
996 // we currently only allow HD and smb and nfs paths
997 if (!URIUtils::IsHD(strPath
) && !URIUtils::IsSmb(strPath
) && !URIUtils::IsNfs(strPath
))
999 CLog::Log(LOGERROR
, "{} called with an unsupported path: {}", __FUNCTION__
, strPath
);
1003 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPath
);
1006 std::string
dir(dirs
.front());
1007 URIUtils::AddSlashAtEnd(dir
);
1008 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
1010 dir
= URIUtils::AddFileToFolder(dir
, *it
);
1011 CDirectory::Create(dir
);
1014 // was the final destination directory successfully created ?
1015 return CDirectory::Exists(strPath
);
1018 std::string
CUtil::MakeLegalFileName(std::string strFile
, int LegalType
)
1020 StringUtils::Replace(strFile
, '/', '_');
1021 StringUtils::Replace(strFile
, '\\', '_');
1022 StringUtils::Replace(strFile
, '?', '_');
1024 if (LegalType
== LEGAL_WIN32_COMPAT
)
1026 // just filter out some illegal characters on windows
1027 StringUtils::Replace(strFile
, ':', '_');
1028 StringUtils::Replace(strFile
, '*', '_');
1029 StringUtils::Replace(strFile
, '?', '_');
1030 StringUtils::Replace(strFile
, '\"', '_');
1031 StringUtils::Replace(strFile
, '<', '_');
1032 StringUtils::Replace(strFile
, '>', '_');
1033 StringUtils::Replace(strFile
, '|', '_');
1034 StringUtils::TrimRight(strFile
, ". ");
1039 // legalize entire path
1040 std::string
CUtil::MakeLegalPath(std::string strPathAndFile
, int LegalType
)
1042 if (URIUtils::IsStack(strPathAndFile
))
1043 return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile
));
1044 if (URIUtils::IsMultiPath(strPathAndFile
))
1045 return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile
));
1046 if (!URIUtils::IsHD(strPathAndFile
) && !URIUtils::IsSmb(strPathAndFile
) && !URIUtils::IsNfs(strPathAndFile
))
1047 return strPathAndFile
; // we don't support writing anywhere except HD, SMB and NFS - no need to legalize path
1049 bool trailingSlash
= URIUtils::HasSlashAtEnd(strPathAndFile
);
1050 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPathAndFile
);
1052 return strPathAndFile
;
1053 // we just add first token to path and don't legalize it - possible values:
1054 // "X:" (local win32), "" (local unix - empty string before '/') or
1055 // "protocol://domain"
1056 std::string
dir(dirs
.front());
1057 URIUtils::AddSlashAtEnd(dir
);
1058 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
1059 dir
= URIUtils::AddFileToFolder(dir
, MakeLegalFileName(*it
, LegalType
));
1060 if (trailingSlash
) URIUtils::AddSlashAtEnd(dir
);
1064 std::string
CUtil::ValidatePath(std::string path
, bool bFixDoubleSlashes
/* = false */)
1067 // Don't do any stuff on URLs containing %-characters or protocols that embed
1068 // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
1069 // recurse and crash XBMC
1070 if (URIUtils::IsURL(path
) &&
1071 (path
.find('%') != std::string::npos
||
1072 StringUtils::StartsWithNoCase(path
, "apk:") ||
1073 StringUtils::StartsWithNoCase(path
, "zip:") ||
1074 StringUtils::StartsWithNoCase(path
, "rar:") ||
1075 StringUtils::StartsWithNoCase(path
, "stack:") ||
1076 StringUtils::StartsWithNoCase(path
, "bluray:") ||
1077 StringUtils::StartsWithNoCase(path
, "multipath:") ))
1080 // check the path for incorrect slashes
1081 #ifdef TARGET_WINDOWS
1082 if (URIUtils::IsDOSPath(path
))
1084 StringUtils::Replace(path
, '/', '\\');
1085 /* The double slash correction should only be used when *absolutely*
1086 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1087 that incorrectly generate double (back) slashes.
1089 if (bFixDoubleSlashes
&& !path
.empty())
1091 // Fixup for double back slashes (but ignore the \\ of unc-paths)
1092 for (size_t x
= 1; x
< path
.size() - 1; x
++)
1094 if (path
[x
] == '\\' && path
[x
+ 1] == '\\')
1099 else if (path
.find("://") != std::string::npos
|| path
.find(":\\\\") != std::string::npos
)
1102 StringUtils::Replace(path
, '\\', '/');
1103 /* The double slash correction should only be used when *absolutely*
1104 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1105 that incorrectly generate double (back) slashes.
1107 if (bFixDoubleSlashes
&& !path
.empty())
1109 // Fixup for double forward slashes(/) but don't touch the :// of URLs
1110 for (size_t x
= 2; x
< path
.size() - 1; x
++)
1112 if (path
[x
] == '/' && path
[x
+ 1] == '/' &&
1113 !(path
[x
- 1] == ':' || (path
[x
- 1] == '/' && path
[x
- 2] == ':')))
1121 void CUtil::SplitParams(const std::string
¶mString
, std::vector
<std::string
> ¶meters
)
1123 bool inQuotes
= false;
1124 bool lastEscaped
= false; // only every second character can be escaped
1126 size_t whiteSpacePos
= 0;
1127 std::string parameter
;
1129 for (size_t pos
= 0; pos
< paramString
.size(); pos
++)
1131 char ch
= paramString
[pos
];
1132 bool escaped
= (pos
> 0 && paramString
[pos
- 1] == '\\' && !lastEscaped
);
1133 lastEscaped
= escaped
;
1135 { // if we're in a quote, we accept everything until the closing quote
1136 if (ch
== '"' && !escaped
)
1137 { // finished a quote - no need to add the end quote to our string
1142 { // not in a quote, so check if we should be starting one
1143 if (ch
== '"' && !escaped
)
1144 { // start of quote - no need to add the quote to our string
1147 if (inFunction
&& ch
== ')')
1148 { // end of a function
1152 { // start of function
1155 if (!inFunction
&& ch
== ',')
1156 { // not in a function, so a comma signifies the end of this parameter
1158 parameter
.resize(whiteSpacePos
);
1159 // trim off start and end quotes
1160 if (parameter
.length() > 1 && parameter
[0] == '"' && parameter
[parameter
.length() - 1] == '"')
1161 parameter
= parameter
.substr(1, parameter
.length() - 2);
1162 else if (parameter
.length() > 3 && parameter
[parameter
.length() - 1] == '"')
1164 // check name="value" style param.
1165 size_t quotaPos
= parameter
.find('"');
1166 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1168 parameter
.erase(parameter
.length() - 1);
1169 parameter
.erase(quotaPos
, 1);
1172 parameters
.push_back(parameter
);
1178 if ((ch
== '"' || ch
== '\\') && escaped
)
1179 { // escaped quote or backslash
1180 parameter
[parameter
.size()-1] = ch
;
1183 // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1184 if (ch
== ' ' && !inQuotes
)
1186 if (parameter
.empty()) // skip whitespace on left
1188 if (!whiteSpacePos
) // make a note of where whitespace starts on the right
1189 whiteSpacePos
= parameter
.size();
1195 if (inFunction
|| inQuotes
)
1196 CLog::Log(LOGWARNING
, "{}({}) - end of string while searching for ) or \"", __FUNCTION__
,
1199 parameter
.erase(whiteSpacePos
);
1200 // trim off start and end quotes
1201 if (parameter
.size() > 1 && parameter
[0] == '"' && parameter
[parameter
.size() - 1] == '"')
1202 parameter
= parameter
.substr(1,parameter
.size() - 2);
1203 else if (parameter
.size() > 3 && parameter
[parameter
.size() - 1] == '"')
1205 // check name="value" style param.
1206 size_t quotaPos
= parameter
.find('"');
1207 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1209 parameter
.erase(parameter
.length() - 1);
1210 parameter
.erase(quotaPos
, 1);
1213 if (!parameter
.empty() || parameters
.size())
1214 parameters
.push_back(parameter
);
1217 int CUtil::GetMatchingSource(const std::string
& strPath1
, VECSOURCES
& VECSOURCES
, bool& bIsSourceName
)
1219 if (strPath1
.empty())
1222 // copy as we may change strPath
1223 std::string strPath
= strPath1
;
1225 // Check for special protocols
1226 CURL
checkURL(strPath
);
1228 if (StringUtils::StartsWith(strPath
, "special://skin/"))
1231 // do not return early if URL protocol is "plugin"
1232 // since video- and/or audio-plugins can be configured as mediasource
1235 if (checkURL
.IsProtocol("stack"))
1236 strPath
.erase(0, 8); // remove the stack protocol
1238 if (checkURL
.IsProtocol("shout"))
1239 strPath
= checkURL
.GetHostName();
1241 if (checkURL
.IsProtocol("multipath"))
1242 strPath
= CMultiPathDirectory::GetFirstPath(strPath
);
1244 bIsSourceName
= false;
1247 // we first test the NAME of a source
1248 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1250 const CMediaSource
&share
= VECSOURCES
[i
];
1251 std::string strName
= share
.strName
;
1253 // special cases for dvds
1254 if (URIUtils::IsOnDVD(share
.strPath
))
1256 if (URIUtils::IsOnDVD(strPath
))
1259 // not a path, so we need to modify the source name
1260 // since we add the drive status and disc name to the source
1261 // "Name (Drive Status/Disc Name)"
1262 size_t iPos
= strName
.rfind('(');
1263 if (iPos
!= std::string::npos
&& iPos
> 1)
1264 strName
.resize(iPos
- 1);
1266 if (StringUtils::EqualsNoCase(strPath
, strName
))
1268 bIsSourceName
= true;
1273 // now test the paths
1275 // remove user details, and ensure path only uses forward slashes
1276 // and ends with a trailing slash so as not to match a substring
1277 CURL
urlDest(strPath
);
1278 urlDest
.SetOptions("");
1279 urlDest
.SetProtocolOptions("");
1280 std::string strDest
= urlDest
.GetWithoutUserDetails();
1281 ForceForwardSlashes(strDest
);
1282 if (!URIUtils::HasSlashAtEnd(strDest
))
1286 size_t iLenPath
= strDest
.size();
1287 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1289 const CMediaSource
&share
= VECSOURCES
[i
];
1291 // does it match a source name?
1292 if (share
.strPath
.substr(0,8) == "shout://")
1294 CURL
url(share
.strPath
);
1295 if (strPath
== url
.GetHostName())
1299 // doesn't match a name, so try the source path
1300 std::vector
<std::string
> vecPaths
;
1302 // add any concatenated paths if they exist
1303 if (!share
.vecPaths
.empty())
1304 vecPaths
= share
.vecPaths
;
1306 // add the actual share path at the front of the vector
1307 vecPaths
.insert(vecPaths
.begin(), share
.strPath
);
1310 for (const auto &path
: vecPaths
)
1312 // remove user details, and ensure path only uses forward slashes
1313 // and ends with a trailing slash so as not to match a substring
1314 CURL
urlShare(path
);
1315 urlShare
.SetOptions("");
1316 urlShare
.SetProtocolOptions("");
1317 std::string strShare
= urlShare
.GetWithoutUserDetails();
1318 ForceForwardSlashes(strShare
);
1319 if (!URIUtils::HasSlashAtEnd(strShare
))
1321 size_t iLenShare
= strShare
.size();
1323 if ((iLenPath
>= iLenShare
) && StringUtils::StartsWithNoCase(strDest
, strShare
) && (iLenShare
> iLength
))
1325 // if exact match, return it immediately
1326 if (iLenPath
== iLenShare
)
1328 // if the path EXACTLY matches an item in a concatenated path
1329 // set source name to true to load the full virtualpath
1330 bIsSourceName
= false;
1331 if (vecPaths
.size() > 1)
1332 bIsSourceName
= true;
1336 iLength
= iLenShare
;
1341 // return the index of the share with the longest match
1345 // rar:// and zip://
1346 // if archive wasn't mounted, look for a matching share for the archive instead
1347 if( StringUtils::StartsWithNoCase(strPath
, "rar://") || StringUtils::StartsWithNoCase(strPath
, "zip://") )
1349 // get the hostname portion of the url since it contains the archive file
1350 strPath
= checkURL
.GetHostName();
1352 bIsSourceName
= false;
1354 return GetMatchingSource(strPath
, VECSOURCES
, bDummy
);
1357 CLog::Log(LOGDEBUG
, "CUtil::GetMatchingSource: no matching source found for [{}]", strPath1
);
1362 std::string
CUtil::TranslateSpecialSource(const std::string
&strSpecial
)
1364 if (!strSpecial
.empty() && strSpecial
[0] == '$')
1366 if (StringUtils::StartsWithNoCase(strSpecial
, "$home"))
1367 return URIUtils::AddFileToFolder("special://home/", strSpecial
.substr(5));
1368 else if (StringUtils::StartsWithNoCase(strSpecial
, "$subtitles"))
1369 return URIUtils::AddFileToFolder("special://subtitles/", strSpecial
.substr(10));
1370 else if (StringUtils::StartsWithNoCase(strSpecial
, "$userdata"))
1371 return URIUtils::AddFileToFolder("special://userdata/", strSpecial
.substr(9));
1372 else if (StringUtils::StartsWithNoCase(strSpecial
, "$database"))
1373 return URIUtils::AddFileToFolder("special://database/", strSpecial
.substr(9));
1374 else if (StringUtils::StartsWithNoCase(strSpecial
, "$thumbnails"))
1375 return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial
.substr(11));
1376 else if (StringUtils::StartsWithNoCase(strSpecial
, "$recordings"))
1377 return URIUtils::AddFileToFolder("special://recordings/", strSpecial
.substr(11));
1378 else if (StringUtils::StartsWithNoCase(strSpecial
, "$screenshots"))
1379 return URIUtils::AddFileToFolder("special://screenshots/", strSpecial
.substr(12));
1380 else if (StringUtils::StartsWithNoCase(strSpecial
, "$musicplaylists"))
1381 return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial
.substr(15));
1382 else if (StringUtils::StartsWithNoCase(strSpecial
, "$videoplaylists"))
1383 return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial
.substr(15));
1384 else if (StringUtils::StartsWithNoCase(strSpecial
, "$cdrips"))
1385 return URIUtils::AddFileToFolder("special://cdrips/", strSpecial
.substr(7));
1386 // this one will be removed post 2.0
1387 else if (StringUtils::StartsWithNoCase(strSpecial
, "$playlists"))
1388 return URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
), strSpecial
.substr(10));
1393 std::string
CUtil::MusicPlaylistsLocation()
1395 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1396 std::vector
<std::string
> vec
;
1397 vec
.push_back(URIUtils::AddFileToFolder(path
, "music"));
1398 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1399 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1402 std::string
CUtil::VideoPlaylistsLocation()
1404 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1405 std::vector
<std::string
> vec
;
1406 vec
.push_back(URIUtils::AddFileToFolder(path
, "video"));
1407 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1408 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1411 void CUtil::DeleteMusicDatabaseDirectoryCache()
1413 CUtil::DeleteDirectoryCache("mdb-");
1414 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1417 void CUtil::DeleteVideoDatabaseDirectoryCache()
1419 CUtil::DeleteDirectoryCache("vdb-");
1420 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1423 void CUtil::DeleteDirectoryCache(const std::string
&prefix
)
1425 std::string searchPath
= "special://temp/";
1426 CFileItemList items
;
1427 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".fi", DIR_FLAG_NO_FILE_DIRS
))
1430 for (const auto &item
: items
)
1432 if (item
->m_bIsFolder
)
1434 std::string fileName
= URIUtils::GetFileName(item
->GetPath());
1435 if (StringUtils::StartsWith(fileName
, prefix
))
1436 XFILE::CFile::Delete(item
->GetPath());
1441 void CUtil::GetRecursiveListing(const std::string
& strPath
, CFileItemList
& items
, const std::string
& strMask
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1443 CFileItemList myItems
;
1444 CDirectory::GetDirectory(strPath
,myItems
,strMask
,flags
);
1445 for (const auto &item
: myItems
)
1447 if (item
->m_bIsFolder
)
1448 CUtil::GetRecursiveListing(item
->GetPath(),items
,strMask
,flags
);
1454 void CUtil::GetRecursiveDirsListing(const std::string
& strPath
, CFileItemList
& item
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1456 CFileItemList myItems
;
1457 CDirectory::GetDirectory(strPath
,myItems
,"",flags
);
1458 for (const auto &i
: myItems
)
1460 if (i
->m_bIsFolder
&& !i
->IsPath(".."))
1463 CUtil::GetRecursiveDirsListing(i
->GetPath(),item
,flags
);
1468 void CUtil::ForceForwardSlashes(std::string
& strPath
)
1470 size_t iPos
= strPath
.rfind('\\');
1471 while (iPos
!= std::string::npos
)
1473 strPath
.at(iPos
) = '/';
1474 iPos
= strPath
.rfind('\\');
1478 double CUtil::AlbumRelevance(const std::string
& strAlbumTemp1
, const std::string
& strAlbum1
, const std::string
& strArtistTemp1
, const std::string
& strArtist1
)
1480 // case-insensitive fuzzy string comparison on the album and artist for relevance
1481 // weighting is identical, both album and artist are 50% of the total relevance
1482 // a missing artist means the maximum relevance can only be 0.50
1483 std::string strAlbumTemp
= strAlbumTemp1
;
1484 StringUtils::ToLower(strAlbumTemp
);
1485 std::string strAlbum
= strAlbum1
;
1486 StringUtils::ToLower(strAlbum
);
1487 double fAlbumPercentage
= fstrcmp(strAlbumTemp
.c_str(), strAlbum
.c_str());
1488 double fArtistPercentage
= 0.0;
1489 if (!strArtist1
.empty())
1491 std::string strArtistTemp
= strArtistTemp1
;
1492 StringUtils::ToLower(strArtistTemp
);
1493 std::string strArtist
= strArtist1
;
1494 StringUtils::ToLower(strArtist
);
1495 fArtistPercentage
= fstrcmp(strArtistTemp
.c_str(), strArtist
.c_str());
1497 double fRelevance
= fAlbumPercentage
* 0.5 + fArtistPercentage
* 0.5;
1501 bool CUtil::MakeShortenPath(std::string StrInput
, std::string
& StrOutput
, size_t iTextMaxLength
)
1503 size_t iStrInputSize
= StrInput
.size();
1504 if(iStrInputSize
<= 0 || iTextMaxLength
>= iStrInputSize
)
1506 StrOutput
= StrInput
;
1511 size_t nGreaterDelim
, nPos
;
1513 nPos
= StrInput
.find_last_of( '\\' );
1514 if (nPos
!= std::string::npos
)
1518 nPos
= StrInput
.find_last_of( '/' );
1519 if (nPos
!= std::string::npos
)
1522 if ( cDelim
== '\0' )
1525 if (nPos
== StrInput
.size() - 1)
1527 StrInput
.erase(StrInput
.size() - 1);
1528 nPos
= StrInput
.find_last_of(cDelim
);
1530 while( iTextMaxLength
< iStrInputSize
)
1532 nPos
= StrInput
.find_last_of( cDelim
, nPos
);
1533 nGreaterDelim
= nPos
;
1535 if (nPos
== std::string::npos
|| nPos
== 0)
1538 nPos
= StrInput
.find_last_of( cDelim
, nPos
- 1 );
1540 if ( nPos
== std::string::npos
)
1542 if ( nGreaterDelim
> nPos
)
1543 StrInput
.replace( nPos
+ 1, nGreaterDelim
- nPos
- 1, ".." );
1544 iStrInputSize
= StrInput
.size();
1546 // replace any additional /../../ with just /../ if necessary
1547 std::string replaceDots
= StringUtils::Format("..{}..", cDelim
);
1548 while (StrInput
.size() > (unsigned int)iTextMaxLength
)
1549 if (!StringUtils::Replace(StrInput
, replaceDots
, ".."))
1551 // finally, truncate our string to force inside our max text length,
1552 // replacing the last 2 characters with ".."
1555 // "smb://../Playboy Swimsuit Cal.."
1556 if (iTextMaxLength
> 2 && StrInput
.size() > (unsigned int)iTextMaxLength
)
1558 StrInput
.erase(iTextMaxLength
- 2);
1561 StrOutput
= StrInput
;
1565 bool CUtil::SupportsWriteFileOperations(const std::string
& strPath
)
1567 // currently only hd, smb, nfs and dav support delete and rename
1568 if (URIUtils::IsHD(strPath
))
1570 if (URIUtils::IsSmb(strPath
))
1572 if (URIUtils::IsPVRRecording(strPath
))
1573 return CPVRDirectory::SupportsWriteFileOperations(strPath
);
1574 if (URIUtils::IsNfs(strPath
))
1576 if (URIUtils::IsDAV(strPath
))
1578 if (URIUtils::IsStack(strPath
))
1579 return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath
));
1580 if (URIUtils::IsMultiPath(strPath
))
1581 return CMultiPathDirectory::SupportsWriteFileOperations(strPath
);
1583 if (CServiceBroker::IsAddonInterfaceUp())
1586 for (const auto& addon
: CServiceBroker::GetVFSAddonCache().GetAddonInstances())
1588 const auto& info
= addon
->GetProtocolInfo();
1589 auto prots
= StringUtils::Split(info
.type
, "|");
1590 if (info
.supportWrite
&&
1591 std::find(prots
.begin(), prots
.end(), url
.GetProtocol()) != prots
.end())
1599 bool CUtil::SupportsReadFileOperations(const std::string
& strPath
)
1601 return !URIUtils::IsVideoDb(strPath
);
1604 std::string
CUtil::GetDefaultFolderThumb(const std::string
&folderThumb
)
1606 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(folderThumb
))
1611 void CUtil::GetSkinThemes(std::vector
<std::string
>& vecTheme
)
1613 static const std::string TexturesXbt
= "Textures.xbt";
1615 std::string strPath
= URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media");
1616 CFileItemList items
;
1617 CDirectory::GetDirectory(strPath
, items
, "", DIR_FLAG_DEFAULTS
);
1618 // Search for Themes in the Current skin!
1619 for (const auto &pItem
: items
)
1621 if (!pItem
->m_bIsFolder
)
1623 std::string strExtension
= URIUtils::GetExtension(pItem
->GetPath());
1624 std::string strLabel
= pItem
->GetLabel();
1625 if ((strExtension
== ".xbt" && !StringUtils::EqualsNoCase(strLabel
, TexturesXbt
)))
1626 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - strExtension
.size()));
1630 // check if this is an xbt:// VFS path
1631 CURL
itemUrl(pItem
->GetPath());
1632 if (!itemUrl
.IsProtocol("xbt") || !itemUrl
.GetFileName().empty())
1635 std::string strLabel
= URIUtils::GetFileName(itemUrl
.GetHostName());
1636 if (!StringUtils::EqualsNoCase(strLabel
, TexturesXbt
))
1637 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - URIUtils::GetExtension(strLabel
).size()));
1640 std::sort(vecTheme
.begin(), vecTheme
.end(), sortstringbyname());
1643 void CUtil::InitRandomSeed()
1646 auto now
= std::chrono::steady_clock::now();
1647 auto seed
= now
.time_since_epoch();
1649 srand(static_cast<unsigned int>(seed
.count()));
1652 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN_TVOS)
1653 bool CUtil::RunCommandLine(const std::string
& cmdLine
, bool waitExit
)
1655 std::vector
<std::string
> args
= StringUtils::Split(cmdLine
, ",");
1657 // Strip quotes and whitespace around the arguments, or exec will fail.
1658 // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1659 // But it's still limited, for example quotes inside the strings are not expanded, etc.
1660 //! @todo Maybe some python library routine can parse this more properly ?
1661 for (std::vector
<std::string
>::iterator it
= args
.begin(); it
!= args
.end(); ++it
)
1664 pos
= it
->find_first_not_of(" \t\n\"'");
1665 if (pos
!= std::string::npos
)
1670 pos
= it
->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1672 it
->erase(++pos
, it
->size());
1676 return Command(args
, waitExit
);
1679 bool CUtil::Command(const std::vector
<std::string
>& arrArgs
, bool waitExit
)
1682 printf("Executing: ");
1683 for (const auto &arg
: arrArgs
)
1684 printf("%s ", arg
.c_str());
1688 pid_t child
= fork();
1694 // fork again in order not to leave a zombie process
1698 else if (child
!= 0)
1704 if (!arrArgs
.empty())
1706 char **args
= (char **)alloca(sizeof(char *) * (arrArgs
.size() + 3));
1707 memset(args
, 0, (sizeof(char *) * (arrArgs
.size() + 3)));
1708 for (size_t i
=0; i
<arrArgs
.size(); i
++)
1709 args
[i
] = const_cast<char *>(arrArgs
[i
].c_str());
1710 execvp(args
[0], args
);
1715 waitpid(child
, &n
, 0);
1718 return (waitExit
) ? (WEXITSTATUS(n
) == 0) : true;
1722 int CUtil::LookupRomanDigit(char roman_digit
)
1724 switch (roman_digit
)
1752 int CUtil::TranslateRomanNumeral(const char* roman_numeral
)
1757 if (roman_numeral
&& roman_numeral
[0])
1764 while (*roman_numeral
)
1766 int digit
= CUtil::LookupRomanDigit(*roman_numeral
);
1769 // General sanity checks
1771 // numeral not in LUT
1778 // N = 10^n may not precede (N+1) > 10^(N+1)
1779 if (test
== 1 && digit
> last
* 10)
1782 // N = 5*10^n may not precede (N+1) >= N
1783 if (test
== 5 && digit
>= last
)
1786 // End general sanity checks
1790 // smaller numerals may not repeat before a larger one
1799 else if (last
== digit
)
1808 decimal
+= 2 * last
- temp_sum
;
1810 decimal
+= temp_sum
;
1817 // Post general sanity checks
1819 // numerals may not repeat more than thrice
1828 decimal
+= temp_sum
;
1830 decimal
+= 2 * last
- temp_sum
;
1835 std::string
CUtil::ResolveExecutablePath()
1837 std::string strExecutablePath
;
1838 #ifdef TARGET_WINDOWS
1839 static const size_t bufSize
= MAX_PATH
* 2;
1840 wchar_t* buf
= new wchar_t[bufSize
];
1842 ::GetModuleFileNameW(0, buf
, bufSize
);
1844 g_charsetConverter
.wToUTF8(buf
,strExecutablePath
);
1846 #elif defined(TARGET_DARWIN)
1847 char given_path
[2*MAXPATHLEN
];
1848 size_t path_size
=2*MAXPATHLEN
;
1850 CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
1851 strExecutablePath
= given_path
;
1852 #elif defined(TARGET_FREEBSD)
1859 mib
[2] = KERN_PROC_PATHNAME
;
1862 buflen
= sizeof(buf
) - 1;
1863 if(sysctl(mib
, 4, buf
, &buflen
, NULL
, 0) < 0)
1864 strExecutablePath
= "";
1866 strExecutablePath
= buf
;
1867 #elif defined(TARGET_ANDROID)
1868 strExecutablePath
= CXBMCApp::getApplicationInfo().nativeLibraryDir
;
1870 std::string appName
= CCompileInfo::GetAppName();
1871 std::string libName
= "lib" + appName
+ ".so";
1872 StringUtils::ToLower(libName
);
1873 strExecutablePath
+= "/" + libName
;
1875 /* Get our PID and build the name of the link in /proc */
1876 pid_t pid
= getpid();
1877 char linkname
[64]; /* /proc/<pid>/exe */
1878 snprintf(linkname
, sizeof(linkname
), "/proc/%i/exe", pid
);
1880 /* Now read the symbolic link */
1881 char buf
[PATH_MAX
+ 1];
1884 int ret
= readlink(linkname
, buf
, sizeof(buf
) - 1);
1888 strExecutablePath
= buf
;
1890 return strExecutablePath
;
1893 std::string
CUtil::GetFrameworksPath(bool forPython
)
1895 std::string strFrameworksPath
;
1896 #if defined(TARGET_DARWIN)
1897 strFrameworksPath
= CDarwinUtils::GetFrameworkPath(forPython
);
1899 return strFrameworksPath
;
1902 void CUtil::GetVideoBasePathAndFileName(const std::string
& videoPath
, std::string
& basePath
, std::string
& videoFileName
)
1904 CFileItem
item(videoPath
, false);
1905 videoFileName
= URIUtils::ReplaceExtension(URIUtils::GetFileName(videoPath
), "");
1907 if (item
.HasVideoInfoTag())
1908 basePath
= item
.GetVideoInfoTag()->m_basePath
;
1910 if (basePath
.empty() && item
.IsOpticalMediaFile())
1911 basePath
= item
.GetLocalMetadataPath();
1913 CURL
url(videoPath
);
1914 if (basePath
.empty() && url
.IsProtocol("bluray"))
1916 basePath
= url
.GetHostName();
1917 videoFileName
= URIUtils::ReplaceExtension(GetTitleFromPath(url
.GetHostName()), "");
1919 url
= CURL(url
.GetHostName());
1920 if (url
.IsProtocol("udf"))
1921 basePath
= URIUtils::GetParentPath(url
.GetHostName());
1924 if (basePath
.empty())
1925 basePath
= URIUtils::GetBasePath(videoPath
);
1928 void CUtil::GetItemsToScan(const std::string
& videoPath
,
1929 const std::string
& item_exts
,
1930 const std::vector
<std::string
>& sub_dirs
,
1931 CFileItemList
& items
)
1933 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
1935 if (!videoPath
.empty())
1936 CDirectory::GetDirectory(videoPath
, items
, item_exts
, flags
);
1938 std::vector
<std::string
> additionalPaths
;
1939 for (const auto &item
: items
)
1941 for (const auto& subdir
: sub_dirs
)
1943 if (StringUtils::EqualsNoCase(item
->GetLabel(), subdir
))
1944 additionalPaths
.push_back(item
->GetPath());
1948 for (std::vector
<std::string
>::const_iterator it
= additionalPaths
.begin(); it
!= additionalPaths
.end(); ++it
)
1950 CFileItemList moreItems
;
1951 CDirectory::GetDirectory(*it
, moreItems
, item_exts
, flags
);
1952 items
.Append(moreItems
);
1957 void CUtil::ScanPathsForAssociatedItems(const std::string
& videoName
,
1958 const CFileItemList
& items
,
1959 const std::vector
<std::string
>& item_exts
,
1960 std::vector
<std::string
>& associatedFiles
)
1962 for (const auto &pItem
: items
)
1964 if (pItem
->m_bIsFolder
)
1967 std::string strCandidate
= URIUtils::GetFileName(pItem
->GetPath());
1970 if (std::find(associatedFiles
.begin(), associatedFiles
.end(), pItem
->GetPath()) != associatedFiles
.end())
1973 URIUtils::RemoveExtension(strCandidate
);
1974 // NOTE: We don't know if one of videoName or strCandidate is URL-encoded and the other is not, so try both
1975 if (StringUtils::StartsWithNoCase(strCandidate
, videoName
) || (StringUtils::StartsWithNoCase(strCandidate
, CURL::Decode(videoName
))))
1977 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1978 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), "", item_exts
, associatedFiles
);
1981 associatedFiles
.push_back(pItem
->GetPath());
1982 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
1983 CURL::GetRedacted(pItem
->GetPath()));
1988 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1989 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), videoName
, item_exts
, associatedFiles
);
1994 int CUtil::ScanArchiveForAssociatedItems(const std::string
& strArchivePath
,
1995 const std::string
& videoNameNoExt
,
1996 const std::vector
<std::string
>& item_exts
,
1997 std::vector
<std::string
>& associatedFiles
)
1999 CLog::LogF(LOGDEBUG
, "Scanning archive {}", CURL::GetRedacted(strArchivePath
));
2000 int nItemsAdded
= 0;
2001 CFileItemList ItemList
;
2003 // zip only gets the root dir
2004 if (URIUtils::HasExtension(strArchivePath
, ".zip"))
2006 CURL
pathToUrl(strArchivePath
);
2007 CURL zipURL
= URIUtils::CreateArchivePath("zip", pathToUrl
, "");
2008 if (!CDirectory::GetDirectory(zipURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
2011 else if (URIUtils::HasExtension(strArchivePath
, ".rar"))
2013 CURL
pathToUrl(strArchivePath
);
2014 CURL rarURL
= URIUtils::CreateArchivePath("rar", pathToUrl
, "");
2015 if (!CDirectory::GetDirectory(rarURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
2018 for (const auto &item
: ItemList
)
2020 std::string strPathInRar
= item
->GetPath();
2021 std::string strExt
= URIUtils::GetExtension(strPathInRar
);
2023 // Check another archive in archive
2024 if (strExt
== ".zip" || strExt
== ".rar")
2027 ScanArchiveForAssociatedItems(strPathInRar
, videoNameNoExt
, item_exts
, associatedFiles
);
2031 // check that the found filename matches the movie filename
2032 size_t fnl
= videoNameNoExt
.size();
2033 // NOTE: We don't know if videoNameNoExt is URL-encoded, so try both
2035 !(StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), videoNameNoExt
) ||
2036 StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), CURL::Decode(videoNameNoExt
))))
2039 for (const auto& ext
: item_exts
)
2041 if (StringUtils::EqualsNoCase(strExt
, ext
))
2043 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
2044 CURL::GetRedacted(strPathInRar
));
2045 associatedFiles
.push_back(strPathInRar
);
2055 void CUtil::ScanForExternalSubtitles(const std::string
& strMovie
, std::vector
<std::string
>& vecSubtitles
)
2057 auto start
= std::chrono::steady_clock::now();
2059 CFileItem
item(strMovie
, false);
2060 if ((NETWORK::IsInternetStream(item
) && !URIUtils::IsOnLAN(item
.GetDynPath())) ||
2061 PLAYLIST::IsPlayList(item
) || item
.IsLiveTV() || !VIDEO::IsVideo(item
))
2064 CLog::Log(LOGDEBUG
, "{}: Searching for subtitles...", __FUNCTION__
);
2066 std::string strBasePath
;
2067 std::string strSubtitle
;
2069 GetVideoBasePathAndFileName(strMovie
, strBasePath
, strSubtitle
);
2071 CFileItemList items
;
2072 const std::vector
<std::string
> common_sub_dirs
= { "subs", "subtitles", "vobsubs", "sub", "vobsub", "subtitle" };
2073 const std::string subtitleExtensions
= CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions();
2074 GetItemsToScan(strBasePath
, subtitleExtensions
, common_sub_dirs
, items
);
2076 const std::string customPath
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH
);
2078 if (!CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() && !customPath
.empty()) // to avoid checking non-existent directories (network) every time..
2080 if (!CServiceBroker::GetNetwork().IsAvailable() && !URIUtils::IsHD(customPath
))
2082 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's inaccessible");
2083 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2085 else if (!CDirectory::Exists(customPath
))
2087 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistent");
2088 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2091 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(1);
2094 std::vector
<std::string
> strLookInPaths
;
2095 // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
2096 if (CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() == 1)
2098 std::string strPath2
= customPath
;
2099 URIUtils::AddSlashAtEnd(strPath2
);
2100 strLookInPaths
.push_back(strPath2
);
2103 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
2104 for (const std::string
& path
: strLookInPaths
)
2106 CFileItemList moreItems
;
2107 CDirectory::GetDirectory(path
, moreItems
, subtitleExtensions
, flags
);
2108 items
.Append(moreItems
);
2111 std::vector
<std::string
> exts
= StringUtils::Split(subtitleExtensions
, '|');
2112 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".zip"), exts
.end());
2113 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".rar"), exts
.end());
2115 ScanPathsForAssociatedItems(strSubtitle
, items
, exts
, vecSubtitles
);
2117 size_t iSize
= vecSubtitles
.size();
2118 for (size_t i
= 0; i
< iSize
; i
++)
2120 if (URIUtils::HasExtension(vecSubtitles
[i
], ".smi"))
2122 //Cache multi-language sami subtitle
2123 CDVDSubtitleStream stream
;
2124 if (stream
.Open(vecSubtitles
[i
]))
2126 CDVDSubtitleTagSami TagConv
;
2127 TagConv
.LoadHead(&stream
);
2128 if (TagConv
.m_Langclass
.size() >= 2)
2130 for (const auto &lang
: TagConv
.m_Langclass
)
2132 std::string strDest
=
2133 StringUtils::Format("special://temp/subtitle.{}.{}.smi", lang
.Name
, i
);
2134 if (CFile::Copy(vecSubtitles
[i
], strDest
))
2136 CLog::Log(LOGINFO
, " cached subtitle {}->{}", CURL::GetRedacted(vecSubtitles
[i
]),
2138 vecSubtitles
.push_back(strDest
);
2146 auto end
= std::chrono::steady_clock::now();
2147 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
2148 CLog::Log(LOGDEBUG
, "{}: END (total time: {} ms)", __FUNCTION__
, duration
.count());
2151 ExternalStreamInfo
CUtil::GetExternalStreamDetailsFromFilename(const std::string
& videoPath
, const std::string
& associatedFile
)
2153 ExternalStreamInfo info
;
2155 std::string videoBaseName
= URIUtils::GetFileName(videoPath
);
2156 URIUtils::RemoveExtension(videoBaseName
);
2158 std::string toParse
= URIUtils::GetFileName(associatedFile
);
2159 URIUtils::RemoveExtension(toParse
);
2161 // we check left part - if it's same as video base name - strip it
2162 if (StringUtils::StartsWithNoCase(toParse
, videoBaseName
))
2163 toParse
= toParse
.substr(videoBaseName
.length());
2164 else if (URIUtils::GetExtension(associatedFile
) == ".sub" && URIUtils::IsInArchive(associatedFile
))
2166 // exclude parsing of vobsub file names that are embedded in an archive
2167 CLog::Log(LOGDEBUG
, "{} - skipping archived vobsub filename parsing: {}", __FUNCTION__
,
2168 CURL::GetRedacted(associatedFile
));
2172 // trim any non-alphanumeric char in the beginning
2173 std::string::iterator result
= std::find_if(toParse
.begin(), toParse
.end(), StringUtils::isasciialphanum
);
2176 if (result
!= toParse
.end()) // if we have anything to parse
2178 std::string
inputString(result
, toParse
.end());
2179 std::string
delimiters(" .-");
2180 std::vector
<std::string
> tokens
;
2181 StringUtils::Tokenize(inputString
, tokens
, delimiters
);
2183 for (auto it
= tokens
.rbegin(); it
!= tokens
.rend(); ++it
)
2185 // try to recognize a flag
2186 std::string
flag_tmp(*it
);
2187 StringUtils::ToLower(flag_tmp
);
2188 if (!flag_tmp
.compare("none"))
2190 info
.flag
|= StreamFlags::FLAG_NONE
;
2193 else if (!flag_tmp
.compare("default"))
2195 info
.flag
|= StreamFlags::FLAG_DEFAULT
;
2198 else if (!flag_tmp
.compare("forced"))
2200 info
.flag
|= StreamFlags::FLAG_FORCED
;
2204 if (info
.language
.empty())
2206 std::string langCode
;
2207 // try to recognize language
2208 if (g_LangCodeExpander
.ConvertToISO6392B(*it
, langCode
))
2210 info
.language
= langCode
;
2215 name
= (*it
) + " " + name
;
2219 name
+= g_localizeStrings
.Get(21602); // External
2220 StringUtils::Trim(name
);
2221 info
.name
= StringUtils::RemoveDuplicatedSpacesAndTabs(name
);
2223 info
.flag
= StreamFlags::FLAG_NONE
;
2225 CLog::Log(LOGDEBUG
, "{} - Language = '{}' / Name = '{}' / Flag = '{}' from {}", __FUNCTION__
,
2226 info
.language
, info
.name
, info
.flag
, CURL::GetRedacted(associatedFile
));
2231 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2233 bool CUtil::FindVobSubPair(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strIdxPath
, std::string
& strSubPath
)
2235 if (URIUtils::HasExtension(strIdxPath
, ".idx"))
2237 std::string strIdxFile
;
2238 std::string strIdxDirectory
;
2239 URIUtils::Split(strIdxPath
, strIdxDirectory
, strIdxFile
);
2240 for (const auto &subtitlePath
: vecSubtitles
)
2242 std::string strSubFile
;
2243 std::string strSubDirectory
;
2244 URIUtils::Split(subtitlePath
, strSubDirectory
, strSubFile
);
2245 if (URIUtils::IsInArchive(subtitlePath
))
2246 strSubDirectory
= CURL::Decode(strSubDirectory
);
2247 if (URIUtils::HasExtension(strSubFile
, ".sub") &&
2248 (URIUtils::PathEquals(URIUtils::ReplaceExtension(strIdxPath
,""),
2249 URIUtils::ReplaceExtension(subtitlePath
,"")) ||
2250 (strSubDirectory
.size() >= 11 &&
2251 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(strIdxPath
,"")))))
2253 strSubPath
= subtitlePath
;
2261 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2263 bool CUtil::IsVobSub(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strSubPath
)
2265 if (URIUtils::HasExtension(strSubPath
, ".sub"))
2267 std::string strSubFile
;
2268 std::string strSubDirectory
;
2269 URIUtils::Split(strSubPath
, strSubDirectory
, strSubFile
);
2270 if (URIUtils::IsInArchive(strSubPath
))
2271 strSubDirectory
= CURL::Decode(strSubDirectory
);
2272 for (const auto &subtitlePath
: vecSubtitles
)
2274 std::string strIdxFile
;
2275 std::string strIdxDirectory
;
2276 URIUtils::Split(subtitlePath
, strIdxDirectory
, strIdxFile
);
2277 if (URIUtils::HasExtension(strIdxFile
, ".idx") &&
2278 (URIUtils::PathEquals(URIUtils::ReplaceExtension(subtitlePath
,""),
2279 URIUtils::ReplaceExtension(strSubPath
,"")) ||
2280 (strSubDirectory
.size() >= 11 &&
2281 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(subtitlePath
,"")))))
2288 /*! \brief find a plain or archived vobsub .sub file corresponding to an .idx file
2290 std::string
CUtil::GetVobSubSubFromIdx(const std::string
& vobSubIdx
)
2292 std::string vobSub
= URIUtils::ReplaceExtension(vobSubIdx
, ".sub");
2294 // check if a .sub file exists in the same directory
2295 if (CFile::Exists(vobSub
))
2300 // look inside a .rar or .zip in the same directory
2301 const std::string archTypes
[] = { "rar", "zip" };
2302 std::string vobSubFilename
= URIUtils::GetFileName(vobSub
);
2303 for (const std::string
& archType
: archTypes
)
2305 vobSub
= URIUtils::CreateArchivePath(archType
,
2306 CURL(URIUtils::ReplaceExtension(vobSubIdx
, std::string(".") + archType
)),
2307 vobSubFilename
).Get();
2308 if (CFile::Exists(vobSub
))
2312 return std::string();
2315 /*! \brief find a .idx file from a path of a plain or archived vobsub .sub file
2317 std::string
CUtil::GetVobSubIdxFromSub(const std::string
& vobSub
)
2319 std::string vobSubIdx
= URIUtils::ReplaceExtension(vobSub
, ".idx");
2321 // check if a .idx file exists in the same directory
2322 if (CFile::Exists(vobSubIdx
))
2327 // look outside archive (usually .rar) if the .sub is inside one
2328 if (URIUtils::IsInArchive(vobSub
))
2331 std::string archiveFile
= URIUtils::GetDirectory(vobSub
);
2332 std::string vobSubIdxDir
= URIUtils::GetParentPath(archiveFile
);
2334 if (!vobSubIdxDir
.empty())
2336 std::string vobSubIdxFilename
= URIUtils::GetFileName(vobSubIdx
);
2337 std::string vobSubIdx
= URIUtils::AddFileToFolder(vobSubIdxDir
, vobSubIdxFilename
);
2339 if (CFile::Exists(vobSubIdx
))
2344 return std::string();
2347 void CUtil::ScanForExternalAudio(const std::string
& videoPath
, std::vector
<std::string
>& vecAudio
)
2349 CFileItem
item(videoPath
, false);
2350 if (NETWORK::IsInternetStream(item
) || PLAYLIST::IsPlayList(item
) || item
.IsLiveTV() ||
2351 item
.IsPVR() || !VIDEO::IsVideo(item
))
2354 std::string strBasePath
;
2355 std::string strAudio
;
2357 GetVideoBasePathAndFileName(videoPath
, strBasePath
, strAudio
);
2359 CFileItemList items
;
2360 const std::vector
<std::string
> common_sub_dirs
= { "audio", "tracks"};
2361 GetItemsToScan(strBasePath
, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), common_sub_dirs
, items
);
2363 std::vector
<std::string
> exts
= StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2365 CVideoDatabase database
;
2367 bool useAllExternalAudio
= database
.GetUseAllExternalAudioForVideo(videoPath
);
2369 if (useAllExternalAudio
)
2371 for (const auto& audioItem
: items
.GetList())
2373 vecAudio
.push_back(audioItem
.get()->GetPath());
2377 ScanPathsForAssociatedItems(strAudio
, items
, exts
, vecAudio
);
2380 bool CUtil::CanBindPrivileged()
2385 return true; //root user can always bind to privileged ports
2389 //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2390 bool canbind
= false;
2391 cap_t capabilities
= cap_get_proc();
2394 cap_flag_value_t value
;
2395 if (cap_get_flag(capabilities
, CAP_NET_BIND_SERVICE
, CAP_EFFECTIVE
, &value
) == 0)
2398 cap_free(capabilities
);
2407 #endif //HAVE_LIBCAP
2409 #else //TARGET_POSIX
2413 #endif //TARGET_POSIX
2416 bool CUtil::ValidatePort(int port
)
2418 // check that it's a valid port
2420 if (!CUtil::CanBindPrivileged() && (port
< 1024 || port
> 65535))
2424 if (port
<= 0 || port
> 65535)
2430 int CUtil::GetRandomNumber()
2432 #if !defined(TARGET_WINDOWS)
2433 return rand_r(&s_randomSeed
);
2435 unsigned int number
;
2436 if (rand_s(&number
) == 0)
2443 void CUtil::CopyUserDataIfNeeded(const std::string
& strPath
,
2444 const std::string
& file
,
2445 const std::string
& destname
)
2447 std::string destPath
;
2448 if (destname
.empty())
2449 destPath
= URIUtils::AddFileToFolder(strPath
, file
);
2451 destPath
= URIUtils::AddFileToFolder(strPath
, destname
);
2453 if (!CFile::Exists(destPath
))
2455 // need to copy it across
2456 std::string srcPath
= URIUtils::AddFileToFolder("special://xbmc/userdata/", file
);
2457 CFile::Copy(srcPath
, destPath
);