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 #if defined(TARGET_DARWIN)
11 #include <sys/param.h>
12 #include <mach-o/dyld.h>
15 #if defined(TARGET_FREEBSD)
16 #include <sys/param.h>
17 #include <sys/sysctl.h>
21 #include <sys/types.h>
26 #if defined(TARGET_ANDROID)
27 #include <androidjni/ApplicationInfo.h>
28 #include "platform/android/activity/XBMCApp.h"
29 #include "CompileInfo.h"
31 #include "ServiceBroker.h"
33 #include "addons/VFSEntry.h"
34 #include "filesystem/Directory.h"
35 #include "filesystem/MultiPathDirectory.h"
36 #include "filesystem/PVRDirectory.h"
37 #include "filesystem/RSSDirectory.h"
38 #include "filesystem/SpecialProtocol.h"
39 #include "filesystem/StackDirectory.h"
45 #include "filesystem/UPnPDirectory.h"
47 #include "profiles/ProfileManager.h"
48 #include "utils/RegExp.h"
49 #include "windowing/GraphicContext.h"
50 #include "guilib/TextureManager.h"
51 #include "storage/MediaManager.h"
53 #include "utils/CharsetConverter.h"
54 #include "WIN32Util.h"
56 #if defined(TARGET_DARWIN)
57 #include "CompileInfo.h"
58 #include "platform/darwin/DarwinUtils.h"
61 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleStream.h"
62 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleTagSami.h"
63 #include "filesystem/File.h"
64 #include "guilib/LocalizeStrings.h"
65 #include "platform/Environment.h"
66 #include "settings/AdvancedSettings.h"
67 #include "settings/MediaSettings.h"
68 #include "settings/Settings.h"
69 #include "settings/SettingsComponent.h"
70 #include "utils/Digest.h"
71 #include "utils/FileExtensionProvider.h"
72 #include "utils/FontUtils.h"
73 #include "utils/LangCodeExpander.h"
74 #include "utils/StringUtils.h"
75 #include "utils/TimeUtils.h"
76 #include "utils/URIUtils.h"
77 #include "utils/log.h"
78 #include "video/VideoDatabase.h"
79 #include "video/VideoInfoTag.h"
81 #include <sys/capability.h>
84 #include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h"
88 #ifdef HAS_OPTICAL_DRIVE
89 using namespace MEDIA_DETECT
;
92 using namespace XFILE
;
93 using namespace PLAYLIST
;
94 using KODI::UTILITY::CDigest
;
96 #if !defined(TARGET_WINDOWS)
97 unsigned int CUtil::s_randomSeed
= time(NULL
);
102 #ifdef TARGET_WINDOWS
103 bool IsDirectoryValidRoot(std::wstring path
)
105 path
+= L
"\\system\\settings\\settings.xml";
106 #if defined(TARGET_WINDOWS_STORE)
107 auto h
= CreateFile2(path
.c_str(), GENERIC_READ
, 0, OPEN_EXISTING
, NULL
);
109 auto h
= CreateFileW(path
.c_str(), GENERIC_READ
, 0, nullptr,
110 OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
112 if (h
!= INVALID_HANDLE_VALUE
)
121 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
123 std::wstring strPathW
;
125 // Environment variable was set and we have a path
126 // Let's make sure it's not relative and test it
127 // so it's actually pointing to a directory containing
129 if (strPath
.find("..") != std::string::npos
)
131 //expand potential relative path to full path
132 g_charsetConverter
.utf8ToW(strPath
, strPathW
, false);
133 CWIN32Util::AddExtraLongPathPrefix(strPathW
);
134 auto bufSize
= GetFullPathNameW(strPathW
.c_str(), 0, nullptr, nullptr);
137 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
138 if (GetFullPathNameW(strPathW
.c_str(), bufSize
, buf
.get(), nullptr) <= bufSize
- 1)
140 strPathW
= buf
.get();
141 CWIN32Util::RemoveExtraLongPathPrefix(strPathW
);
143 if (IsDirectoryValidRoot(strPathW
))
145 g_charsetConverter
.wToUTF8(strPathW
, strPath
);
152 // Okay se no environment variable is set, let's
153 // grab the executable path and check if it's being
154 // run from a directory containing our stuff
155 strPath
= CUtil::ResolveExecutablePath();
156 auto last_sep
= strPath
.find_last_of(PATH_SEPARATOR_CHAR
);
157 if (last_sep
!= std::string::npos
)
158 strPath
= strPath
.substr(0, last_sep
);
160 g_charsetConverter
.utf8ToW(strPath
, strPathW
);
161 if (IsDirectoryValidRoot(strPathW
))
164 // Still nothing, let's check the current working
165 // directory and see if it points to a directory
166 // with our stuff in it. This bit should never be
167 // needed when running on a users system, it's intended
168 // to make our dev environment easier.
169 auto bufSize
= GetCurrentDirectoryW(0, nullptr);
172 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
173 if (0 != GetCurrentDirectoryW(bufSize
, buf
.get()))
175 std::string currentDirectory
;
176 std::wstring
currentDirectoryW(buf
.get());
177 CWIN32Util::RemoveExtraLongPathPrefix(currentDirectoryW
);
179 if (IsDirectoryValidRoot(currentDirectoryW
))
181 g_charsetConverter
.wToUTF8(currentDirectoryW
, currentDirectory
);
182 return currentDirectory
;
187 // If we ended up here we're most likely screwed
188 // we will crash in a few seconds
192 #if defined(TARGET_DARWIN)
193 #if !defined(TARGET_DARWIN_EMBEDDED)
194 bool IsDirectoryValidRoot(std::string path
)
196 path
+= "/system/settings/settings.xml";
197 return CFile::Exists(path
);
201 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
205 auto strHomePath
= CUtil::ResolveExecutablePath();
207 char given_path
[2 * MAXPATHLEN
];
208 size_t path_size
= 2 * MAXPATHLEN
;
210 result
= CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
213 // Move backwards to last /.
214 for (int n
= strlen(given_path
) - 1; given_path
[n
] != '/'; n
--)
215 given_path
[n
] = '\0';
217 #if defined(TARGET_DARWIN_EMBEDDED)
218 strcat(given_path
, "/AppData/AppHome/");
220 // Assume local path inside application bundle.
221 strcat(given_path
, "../Resources/");
222 strcat(given_path
, CCompileInfo::GetAppName());
223 strcat(given_path
, "/");
225 // if this path doesn't exist we
226 // might not be started from the app bundle
227 // but from the debugger/xcode. Lets
228 // see if this assumption is valid
229 if (!CDirectory::Exists(given_path
))
231 std::string given_path_stdstr
= CUtil::ResolveExecutablePath();
232 // try to find the correct folder by going back
233 // in the executable path until settings.xml was found
234 bool validRoot
= false;
237 given_path_stdstr
= URIUtils::GetParentPath(given_path_stdstr
);
238 validRoot
= IsDirectoryValidRoot(given_path_stdstr
);
240 while(given_path_stdstr
.length() > 0 && !validRoot
);
241 strncpy(given_path
, given_path_stdstr
.c_str(), sizeof(given_path
)-1);
246 // Convert to real path.
247 char real_path
[2 * MAXPATHLEN
];
248 if (realpath(given_path
, real_path
) != NULL
)
254 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
255 if (last_sep
!= std::string::npos
)
256 strPath
= strHomePath
.substr(0, last_sep
);
258 strPath
= strHomePath
;
264 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
265 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
269 auto strHomePath
= CUtil::ResolveExecutablePath();
270 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
271 if (last_sep
!= std::string::npos
)
272 strPath
= strHomePath
.substr(0, last_sep
);
274 strPath
= strHomePath
;
276 /* Change strPath accordingly when target is KODI_HOME and when INSTALL_PATH
277 * and BIN_INSTALL_PATH differ
279 std::string installPath
= INSTALL_PATH
;
280 std::string binInstallPath
= BIN_INSTALL_PATH
;
282 if (strTarget
.empty() && installPath
.compare(binInstallPath
))
284 int pos
= strPath
.length() - binInstallPath
.length();
285 std::string tmp
= strPath
;
287 if (!tmp
.compare(binInstallPath
))
289 strPath
.erase(pos
, strPath
.length());
290 strPath
.append(installPath
);
299 std::string
CUtil::GetTitleFromPath(const std::string
& strFileNameAndPath
, bool bIsFolder
/* = false */)
301 CURL
pathToUrl(strFileNameAndPath
);
302 return GetTitleFromPath(pathToUrl
, bIsFolder
);
305 std::string
CUtil::GetTitleFromPath(const CURL
& url
, bool bIsFolder
/* = false */)
307 // use above to get the filename
308 std::string
path(url
.Get());
309 URIUtils::RemoveSlashAtEnd(path
);
310 std::string strFilename
= URIUtils::GetFileName(path
);
314 if (url
.IsProtocol("upnp"))
315 strFilename
= CUPnPDirectory::GetFriendlyName(url
);
318 if (url
.IsProtocol("rss") || url
.IsProtocol("rsss"))
322 if(dir
.GetDirectory(url
, items
) && !items
.m_strTitle
.empty())
323 return items
.m_strTitle
;
327 else if (url
.IsProtocol("shout"))
329 const std::string strFileNameAndPath
= url
.Get();
330 const size_t genre
= strFileNameAndPath
.find_first_of('=');
331 if(genre
== std::string::npos
)
332 strFilename
= g_localizeStrings
.Get(260);
334 strFilename
= g_localizeStrings
.Get(260) + " - " + strFileNameAndPath
.substr(genre
+1).c_str();
337 // Windows SMB Network (SMB)
338 else if (url
.IsProtocol("smb") && strFilename
.empty())
340 if (url
.GetHostName().empty())
342 strFilename
= g_localizeStrings
.Get(20171);
346 strFilename
= url
.GetHostName();
351 else if (url
.IsProtocol("sources"))
352 strFilename
= g_localizeStrings
.Get(744);
355 else if (StringUtils::StartsWith(path
, "special://musicplaylists"))
356 strFilename
= g_localizeStrings
.Get(136);
359 else if (StringUtils::StartsWith(path
, "special://videoplaylists"))
360 strFilename
= g_localizeStrings
.Get(136);
362 else if (URIUtils::HasParentInHostname(url
) && strFilename
.empty())
363 strFilename
= URIUtils::GetFileName(url
.GetHostName());
365 // now remove the extension if needed
366 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS
) && !bIsFolder
)
368 URIUtils::RemoveExtension(strFilename
);
372 // URLDecode since the original path may be an URL
373 strFilename
= CURL::Decode(strFilename
);
379 void GetTrailingDiscNumberSegmentInfoFromPath(const std::string
& pathIn
,
383 std::string path
{pathIn
};
384 URIUtils::RemoveSlashAtEnd(path
);
386 pos
= std::string::npos
;
389 // Handle Disc, Disk and locale specific spellings
390 std::string discStr
{StringUtils::Format("/{} ", g_localizeStrings
.Get(427))};
391 size_t discPos
= path
.rfind(discStr
);
393 if (discPos
== std::string::npos
)
396 discPos
= path
.rfind(discStr
);
399 if (discPos
== std::string::npos
)
402 discPos
= path
.rfind(discStr
);
405 if (discPos
!= std::string::npos
)
407 // Check remainder of path is numeric (eg. Disc 1)
408 const std::string discNum
{path
.substr(discPos
+ discStr
.size())};
409 if (discNum
.find_first_not_of("0123456789") == std::string::npos
)
416 } // unnamed namespace
418 std::string
CUtil::RemoveTrailingDiscNumberSegmentFromPath(std::string path
)
420 size_t discPos
{std::string::npos
};
422 GetTrailingDiscNumberSegmentInfoFromPath(path
, discPos
, discNum
);
424 if (discPos
!= std::string::npos
)
430 std::string
CUtil::GetDiscNumberFromPath(const std::string
& path
)
432 size_t discPos
{std::string::npos
};
434 GetTrailingDiscNumberSegmentInfoFromPath(path
, discPos
, discNum
);
438 bool CUtil::GetFilenameIdentifier(const std::string
& fileName
,
439 std::string
& identifierType
,
440 std::string
& identifier
)
443 return GetFilenameIdentifier(fileName
, identifierType
, identifier
, match
);
446 bool CUtil::GetFilenameIdentifier(const std::string
& fileName
,
447 std::string
& identifierType
,
448 std::string
& identifier
,
451 CRegExp
reIdentifier(true, CRegExp::autoUtf8
);
453 const std::shared_ptr
<CAdvancedSettings
> advancedSettings
=
454 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
455 if (!reIdentifier
.RegComp(advancedSettings
->m_videoFilenameIdentifierRegExp
))
457 CLog::LogF(LOGERROR
, "Invalid filename identifier RegExp:'{}'",
458 advancedSettings
->m_videoFilenameIdentifierRegExp
);
463 if (reIdentifier
.RegComp(advancedSettings
->m_videoFilenameIdentifierRegExp
))
465 if (reIdentifier
.RegFind(fileName
) >= 0)
467 match
= reIdentifier
.GetMatch(0);
468 identifierType
= reIdentifier
.GetMatch(1);
469 identifier
= reIdentifier
.GetMatch(2);
470 StringUtils::ToLower(identifierType
);
478 bool CUtil::HasFilenameIdentifier(const std::string
& fileName
)
480 std::string identifierType
;
481 std::string identifier
;
482 return GetFilenameIdentifier(fileName
, identifierType
, identifier
);
485 void CUtil::CleanString(const std::string
& strFileName
,
486 std::string
& strTitle
,
487 std::string
& strTitleAndYear
,
488 std::string
& strYear
,
489 bool bRemoveExtension
/* = false */,
490 bool bCleanChars
/* = true */)
492 strTitleAndYear
= strFileName
;
494 if (strFileName
== "..")
497 std::string identifier
;
498 std::string identifierType
;
499 std::string identifierMatch
;
500 if (GetFilenameIdentifier(strFileName
, identifierType
, identifier
, identifierMatch
))
501 StringUtils::Replace(strTitleAndYear
, identifierMatch
, "");
503 const std::shared_ptr
<CAdvancedSettings
> advancedSettings
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
504 const std::vector
<std::string
> ®exps
= advancedSettings
->m_videoCleanStringRegExps
;
506 CRegExp
reTags(true, CRegExp::autoUtf8
);
507 CRegExp
reYear(false, CRegExp::autoUtf8
);
509 if (!reYear
.RegComp(advancedSettings
->m_videoCleanDateTimeRegExp
))
511 CLog::Log(LOGERROR
, "{}: Invalid datetime clean RegExp:'{}'", __FUNCTION__
,
512 advancedSettings
->m_videoCleanDateTimeRegExp
);
516 if (reYear
.RegFind(strTitleAndYear
.c_str()) >= 0)
518 strTitleAndYear
= reYear
.GetMatch(1);
519 strYear
= reYear
.GetMatch(2);
523 URIUtils::RemoveExtension(strTitleAndYear
);
525 for (const auto ®exp
: regexps
)
527 if (!reTags
.RegComp(regexp
.c_str()))
528 { // invalid regexp - complain in logs
529 CLog::Log(LOGERROR
, "{}: Invalid string clean RegExp:'{}'", __FUNCTION__
, regexp
);
533 if ((j
=reTags
.RegFind(strTitleAndYear
.c_str())) > 0)
534 strTitleAndYear
.resize(j
);
537 // final cleanup - special characters used instead of spaces:
538 // all '_' tokens should be replaced by spaces
539 // if the file contains no spaces, all '.' tokens should be replaced by
540 // spaces - one possibility of a mistake here could be something like:
541 // "Dr..StrangeLove" - hopefully no one would have anything like this.
544 bool initialDots
= true;
545 bool alreadyContainsSpace
= (strTitleAndYear
.find(' ') != std::string::npos
);
547 for (char &c
: strTitleAndYear
)
552 if ((c
== '_') || ((!alreadyContainsSpace
) && !initialDots
&& (c
== '.')))
559 StringUtils::Trim(strTitleAndYear
);
560 strTitle
= strTitleAndYear
;
563 if (!strYear
.empty())
564 strTitleAndYear
= strTitle
+ " (" + strYear
+ ")";
566 // restore extension if needed
567 if (!bRemoveExtension
)
568 strTitleAndYear
+= URIUtils::GetExtension(strFileName
);
571 void CUtil::GetQualifiedFilename(const std::string
&strBasePath
, std::string
&strFilename
)
573 // Check if the filename is a fully qualified URL such as protocol://path/to/file
574 CURL
plItemUrl(strFilename
);
575 if (!plItemUrl
.GetProtocol().empty())
578 // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
579 if (strFilename
.size() > 1)
581 if ( (strFilename
[1] == ':') || (strFilename
[0] == '/') )
583 if ( strFilename
[1] == ':' || (strFilename
[0] == '\\' && strFilename
[1] == '\\'))
587 // add to base path and then clean
588 strFilename
= URIUtils::AddFileToFolder(strBasePath
, strFilename
);
590 // get rid of any /./ or \.\ that happen to be there
591 StringUtils::Replace(strFilename
, "\\.\\", "\\");
592 StringUtils::Replace(strFilename
, "/./", "/");
594 // now find any "\\..\\" and remove them via GetParentPath
596 while ((pos
= strFilename
.find("/../")) != std::string::npos
)
598 std::string basePath
= strFilename
.substr(0, pos
+ 1);
599 strFilename
.erase(0, pos
+ 4);
600 basePath
= URIUtils::GetParentPath(basePath
);
601 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
603 while ((pos
= strFilename
.find("\\..\\")) != std::string::npos
)
605 std::string basePath
= strFilename
.substr(0, pos
+ 1);
606 strFilename
.erase(0, pos
+ 4);
607 basePath
= URIUtils::GetParentPath(basePath
);
608 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
612 void CUtil::RunShortcut(const char* szShortcutPath
)
616 std::string
CUtil::GetHomePath(const std::string
& strTarget
)
618 auto strPath
= CEnvironment::getenv(strTarget
);
620 return ::GetHomePath(strTarget
, strPath
);
623 bool CUtil::IsPicture(const std::string
& strFile
)
625 return URIUtils::HasExtension(strFile
,
626 CServiceBroker::GetFileExtensionProvider().GetPictureExtensions()+ "|.tbn|.dds");
629 std::string
CUtil::GetSplashPath()
631 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" }};
632 auto it
= std::find_if(candidates
.begin(), candidates
.end(), [](std::string
const& file
) { return XFILE::CFile::Exists(file
); });
633 if (it
== candidates
.end())
634 throw std::runtime_error("No splash image found");
635 return CSpecialProtocol::TranslatePathConvertCase(*it
);
638 bool CUtil::ExcludeFileOrFolder(const std::string
& strFileOrFolder
, const std::vector
<std::string
>& regexps
)
640 if (strFileOrFolder
.empty())
643 CRegExp
regExExcludes(true, CRegExp::autoUtf8
); // case insensitive regex
645 for (const auto ®exp
: regexps
)
647 if (!regExExcludes
.RegComp(regexp
.c_str()))
648 { // invalid regexp - complain in logs
649 CLog::Log(LOGERROR
, "{}: Invalid exclude RegExp:'{}'", __FUNCTION__
, regexp
);
652 if (regExExcludes
.RegFind(strFileOrFolder
) > -1)
654 CLog::LogF(LOGDEBUG
, "File '{}' excluded. (Matches exclude rule RegExp: '{}')", CURL::GetRedacted(strFileOrFolder
), regexp
);
661 void CUtil::GetFileAndProtocol(const std::string
& strURL
, std::string
& strDir
)
664 if (!URIUtils::IsRemote(strURL
)) return ;
665 if (URIUtils::IsDVD(strURL
)) return ;
668 strDir
= StringUtils::Format("{}://{}", url
.GetProtocol(), url
.GetFileName());
671 int CUtil::GetDVDIfoTitle(const std::string
& strFile
)
673 std::string strFilename
= URIUtils::GetFileName(strFile
);
674 if (StringUtils::EqualsNoCase(strFilename
, "video_ts.ifo")) return 0;
676 return atoi(strFilename
.substr(4, 2).c_str());
679 std::string
CUtil::GetFileDigest(const std::string
& strPath
, KODI::UTILITY::CDigest::Type type
)
683 if (file
.Open(strPath
))
685 CDigest digest
{type
};
689 ssize_t read
= file
.Read(temp
,1024);
692 digest
.Update(temp
,read
);
694 result
= digest
.Finalize();
701 bool CUtil::GetDirectoryName(const std::string
& strFileName
, std::string
& strDescription
)
703 std::string strFName
= URIUtils::GetFileName(strFileName
);
704 strDescription
= URIUtils::GetDirectory(strFileName
);
705 URIUtils::RemoveSlashAtEnd(strDescription
);
707 size_t iPos
= strDescription
.find_last_of("/\\");
708 if (iPos
!= std::string::npos
)
709 strDescription
= strDescription
.substr(iPos
+ 1);
710 else if (strDescription
.size() <= 0)
711 strDescription
= strFName
;
715 void CUtil::GetDVDDriveIcon(const std::string
& strPath
, std::string
& strIcon
)
717 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
719 strIcon
= "DefaultDVDEmpty.png";
723 CFileItem item
= CFileItem(strPath
, false);
727 strIcon
= "DefaultBluray.png";
731 if ( URIUtils::IsDVD(strPath
) )
733 strIcon
= "DefaultDVDFull.png";
737 if ( URIUtils::IsISO9660(strPath
) )
739 #ifdef HAS_OPTICAL_DRIVE
740 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo();
741 if ( pInfo
!= NULL
&& pInfo
->IsVideoCd( 1 ) )
743 strIcon
= "DefaultVCD.png";
747 strIcon
= "DefaultDVDRom.png";
751 if ( URIUtils::IsCDDA(strPath
) )
753 strIcon
= "DefaultCDDA.png";
758 void CUtil::RemoveTempFiles()
760 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
762 std::string searchPath
= profileManager
->GetDatabaseFolder();
764 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".tmp", DIR_FLAG_NO_FILE_DIRS
))
767 for (const auto &item
: items
)
769 if (item
->m_bIsFolder
)
771 XFILE::CFile::Delete(item
->GetPath());
775 void CUtil::ClearSubtitles()
779 CDirectory::GetDirectory("special://temp/",items
, "", DIR_FLAG_DEFAULTS
);
780 for (const auto &item
: items
)
782 if (!item
->m_bIsFolder
)
784 if (item
->GetPath().find("subtitle") != std::string::npos
||
785 item
->GetPath().find("vobsub_queue") != std::string::npos
)
787 CLog::Log(LOGDEBUG
, "{} - Deleting temporary subtitle {}", __FUNCTION__
, item
->GetPath());
788 CFile::Delete(item
->GetPath());
794 int64_t CUtil::ToInt64(uint32_t high
, uint32_t low
)
804 \brief Finds next unused filename that matches padded int format identifier provided
805 \param[in] fn_template filename template consisting of a padded int format identifier (eg screenshot%03d)
806 \param[in] max maximum number to search for available name
807 \return "" on failure, string next available name matching format identifier on success
810 std::string
CUtil::GetNextFilename(const std::string
&fn_template
, int max
)
812 std::string searchPath
= URIUtils::GetDirectory(fn_template
);
813 std::string mask
= URIUtils::GetExtension(fn_template
);
814 std::string name
= StringUtils::Format(fn_template
, 0);
817 if (!CDirectory::GetDirectory(searchPath
, items
, mask
, DIR_FLAG_NO_FILE_DIRS
))
820 items
.SetFastLookup(true);
821 for (int i
= 0; i
<= max
; i
++)
823 std::string name
= StringUtils::Format(fn_template
, i
);
824 if (!items
.Get(name
))
830 std::string
CUtil::GetNextPathname(const std::string
&path_template
, int max
)
832 if (path_template
.find("%04d") == std::string::npos
)
835 for (int i
= 0; i
<= max
; i
++)
837 std::string name
= StringUtils::Format(path_template
, i
);
838 if (!CFile::Exists(name
) && !CDirectory::Exists(name
))
844 void CUtil::StatToStatI64(struct _stati64
*result
, struct stat
*stat
)
846 result
->st_dev
= stat
->st_dev
;
847 result
->st_ino
= stat
->st_ino
;
848 result
->st_mode
= stat
->st_mode
;
849 result
->st_nlink
= stat
->st_nlink
;
850 result
->st_uid
= stat
->st_uid
;
851 result
->st_gid
= stat
->st_gid
;
852 result
->st_rdev
= stat
->st_rdev
;
853 result
->st_size
= (int64_t)stat
->st_size
;
856 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
857 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
858 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
860 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
861 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
862 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
866 void CUtil::Stat64ToStatI64(struct _stati64
*result
, struct __stat64
*stat
)
868 result
->st_dev
= stat
->st_dev
;
869 result
->st_ino
= stat
->st_ino
;
870 result
->st_mode
= stat
->st_mode
;
871 result
->st_nlink
= stat
->st_nlink
;
872 result
->st_uid
= stat
->st_uid
;
873 result
->st_gid
= stat
->st_gid
;
874 result
->st_rdev
= stat
->st_rdev
;
875 result
->st_size
= stat
->st_size
;
877 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
878 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
879 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
881 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
882 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
883 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
887 void CUtil::StatI64ToStat64(struct __stat64
*result
, struct _stati64
*stat
)
889 result
->st_dev
= stat
->st_dev
;
890 result
->st_ino
= stat
->st_ino
;
891 result
->st_mode
= stat
->st_mode
;
892 result
->st_nlink
= stat
->st_nlink
;
893 result
->st_uid
= stat
->st_uid
;
894 result
->st_gid
= stat
->st_gid
;
895 result
->st_rdev
= stat
->st_rdev
;
896 result
->st_size
= stat
->st_size
;
898 result
->st_atime
= stat
->st_atime
;
899 result
->st_mtime
= stat
->st_mtime
;
900 result
->st_ctime
= stat
->st_ctime
;
902 result
->st_atime
= stat
->_st_atime
;
903 result
->st_mtime
= stat
->_st_mtime
;
904 result
->st_ctime
= stat
->_st_ctime
;
908 void CUtil::StatToStat64(struct __stat64
*result
, const struct stat
*stat
)
910 memset(result
, 0, sizeof(*result
));
911 result
->st_dev
= stat
->st_dev
;
912 result
->st_ino
= stat
->st_ino
;
913 result
->st_mode
= stat
->st_mode
;
914 result
->st_nlink
= stat
->st_nlink
;
915 result
->st_uid
= stat
->st_uid
;
916 result
->st_gid
= stat
->st_gid
;
917 result
->st_rdev
= stat
->st_rdev
;
918 result
->st_size
= stat
->st_size
;
919 result
->st_atime
= stat
->st_atime
;
920 result
->st_mtime
= stat
->st_mtime
;
921 result
->st_ctime
= stat
->st_ctime
;
924 void CUtil::Stat64ToStat(struct stat
*result
, struct __stat64
*stat
)
926 result
->st_dev
= stat
->st_dev
;
927 result
->st_ino
= stat
->st_ino
;
928 result
->st_mode
= stat
->st_mode
;
929 result
->st_nlink
= stat
->st_nlink
;
930 result
->st_uid
= stat
->st_uid
;
931 result
->st_gid
= stat
->st_gid
;
932 result
->st_rdev
= stat
->st_rdev
;
934 if (stat
->st_size
<= LONG_MAX
)
935 result
->st_size
= (_off_t
)stat
->st_size
;
937 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
938 result
->st_size
= stat
->st_size
;
943 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
945 result
->st_atime
= (time_t)(stat
->st_atime
& 0xFFFFFFFF);
946 result
->st_mtime
= (time_t)(stat
->st_mtime
& 0xFFFFFFFF);
947 result
->st_ctime
= (time_t)(stat
->st_ctime
& 0xFFFFFFFF);
950 #ifdef TARGET_WINDOWS
951 void CUtil::Stat64ToStat64i32(struct _stat64i32
*result
, struct __stat64
*stat
)
953 result
->st_dev
= stat
->st_dev
;
954 result
->st_ino
= stat
->st_ino
;
955 result
->st_mode
= stat
->st_mode
;
956 result
->st_nlink
= stat
->st_nlink
;
957 result
->st_uid
= stat
->st_uid
;
958 result
->st_gid
= stat
->st_gid
;
959 result
->st_rdev
= stat
->st_rdev
;
961 if (stat
->st_size
<= LONG_MAX
)
962 result
->st_size
= (_off_t
)stat
->st_size
;
964 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
965 result
->st_size
= stat
->st_size
;
970 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
973 result
->st_atime
= stat
->st_atime
;
974 result
->st_mtime
= stat
->st_mtime
;
975 result
->st_ctime
= stat
->st_ctime
;
977 result
->st_atime
= stat
->_st_atime
;
978 result
->st_mtime
= stat
->_st_mtime
;
979 result
->st_ctime
= stat
->_st_ctime
;
984 bool CUtil::CreateDirectoryEx(const std::string
& strPath
)
986 // Function to create all directories at once instead
987 // of calling CreateDirectory for every subdir.
988 // Creates the directory and subdirectories if needed.
990 // return true if directory already exist
991 if (CDirectory::Exists(strPath
)) return true;
993 // we currently only allow HD and smb and nfs paths
994 if (!URIUtils::IsHD(strPath
) && !URIUtils::IsSmb(strPath
) && !URIUtils::IsNfs(strPath
))
996 CLog::Log(LOGERROR
, "{} called with an unsupported path: {}", __FUNCTION__
, strPath
);
1000 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPath
);
1003 std::string
dir(dirs
.front());
1004 URIUtils::AddSlashAtEnd(dir
);
1005 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
1007 dir
= URIUtils::AddFileToFolder(dir
, *it
);
1008 CDirectory::Create(dir
);
1011 // was the final destination directory successfully created ?
1012 return CDirectory::Exists(strPath
);
1015 std::string
CUtil::MakeLegalFileName(std::string strFile
, int LegalType
)
1017 StringUtils::Replace(strFile
, '/', '_');
1018 StringUtils::Replace(strFile
, '\\', '_');
1019 StringUtils::Replace(strFile
, '?', '_');
1021 if (LegalType
== LEGAL_WIN32_COMPAT
)
1023 // just filter out some illegal characters on windows
1024 StringUtils::Replace(strFile
, ':', '_');
1025 StringUtils::Replace(strFile
, '*', '_');
1026 StringUtils::Replace(strFile
, '?', '_');
1027 StringUtils::Replace(strFile
, '\"', '_');
1028 StringUtils::Replace(strFile
, '<', '_');
1029 StringUtils::Replace(strFile
, '>', '_');
1030 StringUtils::Replace(strFile
, '|', '_');
1031 StringUtils::TrimRight(strFile
, ". ");
1036 // legalize entire path
1037 std::string
CUtil::MakeLegalPath(std::string strPathAndFile
, int LegalType
)
1039 if (URIUtils::IsStack(strPathAndFile
))
1040 return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile
));
1041 if (URIUtils::IsMultiPath(strPathAndFile
))
1042 return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile
));
1043 if (!URIUtils::IsHD(strPathAndFile
) && !URIUtils::IsSmb(strPathAndFile
) && !URIUtils::IsNfs(strPathAndFile
))
1044 return strPathAndFile
; // we don't support writing anywhere except HD, SMB and NFS - no need to legalize path
1046 bool trailingSlash
= URIUtils::HasSlashAtEnd(strPathAndFile
);
1047 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPathAndFile
);
1049 return strPathAndFile
;
1050 // we just add first token to path and don't legalize it - possible values:
1051 // "X:" (local win32), "" (local unix - empty string before '/') or
1052 // "protocol://domain"
1053 std::string
dir(dirs
.front());
1054 URIUtils::AddSlashAtEnd(dir
);
1055 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
1056 dir
= URIUtils::AddFileToFolder(dir
, MakeLegalFileName(*it
, LegalType
));
1057 if (trailingSlash
) URIUtils::AddSlashAtEnd(dir
);
1061 std::string
CUtil::ValidatePath(std::string path
, bool bFixDoubleSlashes
/* = false */)
1064 // Don't do any stuff on URLs containing %-characters or protocols that embed
1065 // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
1066 // recurse and crash XBMC
1067 if (URIUtils::IsURL(path
) &&
1068 (path
.find('%') != std::string::npos
||
1069 StringUtils::StartsWithNoCase(path
, "apk:") ||
1070 StringUtils::StartsWithNoCase(path
, "zip:") ||
1071 StringUtils::StartsWithNoCase(path
, "rar:") ||
1072 StringUtils::StartsWithNoCase(path
, "stack:") ||
1073 StringUtils::StartsWithNoCase(path
, "bluray:") ||
1074 StringUtils::StartsWithNoCase(path
, "multipath:") ))
1077 // check the path for incorrect slashes
1078 #ifdef TARGET_WINDOWS
1079 if (URIUtils::IsDOSPath(path
))
1081 StringUtils::Replace(path
, '/', '\\');
1082 /* The double slash correction should only be used when *absolutely*
1083 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1084 that incorrectly generate double (back) slashes.
1086 if (bFixDoubleSlashes
&& !path
.empty())
1088 // Fixup for double back slashes (but ignore the \\ of unc-paths)
1089 for (size_t x
= 1; x
< path
.size() - 1; x
++)
1091 if (path
[x
] == '\\' && path
[x
+ 1] == '\\')
1096 else if (path
.find("://") != std::string::npos
|| path
.find(":\\\\") != std::string::npos
)
1099 StringUtils::Replace(path
, '\\', '/');
1100 /* The double slash correction should only be used when *absolutely*
1101 necessary! This applies to certain DLLs or use from Python DLLs/scripts
1102 that incorrectly generate double (back) slashes.
1104 if (bFixDoubleSlashes
&& !path
.empty())
1106 // Fixup for double forward slashes(/) but don't touch the :// of URLs
1107 for (size_t x
= 2; x
< path
.size() - 1; x
++)
1109 if (path
[x
] == '/' && path
[x
+ 1] == '/' &&
1110 !(path
[x
- 1] == ':' || (path
[x
- 1] == '/' && path
[x
- 2] == ':')))
1118 void CUtil::SplitParams(const std::string
¶mString
, std::vector
<std::string
> ¶meters
)
1120 bool inQuotes
= false;
1121 bool lastEscaped
= false; // only every second character can be escaped
1123 size_t whiteSpacePos
= 0;
1124 std::string parameter
;
1126 for (size_t pos
= 0; pos
< paramString
.size(); pos
++)
1128 char ch
= paramString
[pos
];
1129 bool escaped
= (pos
> 0 && paramString
[pos
- 1] == '\\' && !lastEscaped
);
1130 lastEscaped
= escaped
;
1132 { // if we're in a quote, we accept everything until the closing quote
1133 if (ch
== '"' && !escaped
)
1134 { // finished a quote - no need to add the end quote to our string
1139 { // not in a quote, so check if we should be starting one
1140 if (ch
== '"' && !escaped
)
1141 { // start of quote - no need to add the quote to our string
1144 if (inFunction
&& ch
== ')')
1145 { // end of a function
1149 { // start of function
1152 if (!inFunction
&& ch
== ',')
1153 { // not in a function, so a comma signifies the end of this parameter
1155 parameter
.resize(whiteSpacePos
);
1156 // trim off start and end quotes
1157 if (parameter
.length() > 1 && parameter
[0] == '"' && parameter
[parameter
.length() - 1] == '"')
1158 parameter
= parameter
.substr(1, parameter
.length() - 2);
1159 else if (parameter
.length() > 3 && parameter
[parameter
.length() - 1] == '"')
1161 // check name="value" style param.
1162 size_t quotaPos
= parameter
.find('"');
1163 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1165 parameter
.erase(parameter
.length() - 1);
1166 parameter
.erase(quotaPos
);
1169 parameters
.push_back(parameter
);
1175 if ((ch
== '"' || ch
== '\\') && escaped
)
1176 { // escaped quote or backslash
1177 parameter
[parameter
.size()-1] = ch
;
1180 // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1181 if (ch
== ' ' && !inQuotes
)
1183 if (parameter
.empty()) // skip whitespace on left
1185 if (!whiteSpacePos
) // make a note of where whitespace starts on the right
1186 whiteSpacePos
= parameter
.size();
1192 if (inFunction
|| inQuotes
)
1193 CLog::Log(LOGWARNING
, "{}({}) - end of string while searching for ) or \"", __FUNCTION__
,
1196 parameter
.erase(whiteSpacePos
);
1197 // trim off start and end quotes
1198 if (parameter
.size() > 1 && parameter
[0] == '"' && parameter
[parameter
.size() - 1] == '"')
1199 parameter
= parameter
.substr(1,parameter
.size() - 2);
1200 else if (parameter
.size() > 3 && parameter
[parameter
.size() - 1] == '"')
1202 // check name="value" style param.
1203 size_t quotaPos
= parameter
.find('"');
1204 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1206 parameter
.erase(parameter
.length() - 1);
1207 parameter
.erase(quotaPos
);
1210 if (!parameter
.empty() || parameters
.size())
1211 parameters
.push_back(parameter
);
1214 int CUtil::GetMatchingSource(const std::string
& strPath1
, VECSOURCES
& VECSOURCES
, bool& bIsSourceName
)
1216 if (strPath1
.empty())
1219 // copy as we may change strPath
1220 std::string strPath
= strPath1
;
1222 // Check for special protocols
1223 CURL
checkURL(strPath
);
1225 if (StringUtils::StartsWith(strPath
, "special://skin/"))
1228 // do not return early if URL protocol is "plugin"
1229 // since video- and/or audio-plugins can be configured as mediasource
1232 if (checkURL
.IsProtocol("stack"))
1233 strPath
.erase(0, 8); // remove the stack protocol
1235 if (checkURL
.IsProtocol("shout"))
1236 strPath
= checkURL
.GetHostName();
1238 if (checkURL
.IsProtocol("multipath"))
1239 strPath
= CMultiPathDirectory::GetFirstPath(strPath
);
1241 bIsSourceName
= false;
1244 // we first test the NAME of a source
1245 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1247 const CMediaSource
&share
= VECSOURCES
[i
];
1248 std::string strName
= share
.strName
;
1250 // special cases for dvds
1251 if (URIUtils::IsOnDVD(share
.strPath
))
1253 if (URIUtils::IsOnDVD(strPath
))
1256 // not a path, so we need to modify the source name
1257 // since we add the drive status and disc name to the source
1258 // "Name (Drive Status/Disc Name)"
1259 size_t iPos
= strName
.rfind('(');
1260 if (iPos
!= std::string::npos
&& iPos
> 1)
1261 strName
.resize(iPos
- 1);
1263 if (StringUtils::EqualsNoCase(strPath
, strName
))
1265 bIsSourceName
= true;
1270 // now test the paths
1272 // remove user details, and ensure path only uses forward slashes
1273 // and ends with a trailing slash so as not to match a substring
1274 CURL
urlDest(strPath
);
1275 urlDest
.SetOptions("");
1276 urlDest
.SetProtocolOptions("");
1277 std::string strDest
= urlDest
.GetWithoutUserDetails();
1278 ForceForwardSlashes(strDest
);
1279 if (!URIUtils::HasSlashAtEnd(strDest
))
1283 size_t iLenPath
= strDest
.size();
1284 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1286 const CMediaSource
&share
= VECSOURCES
[i
];
1288 // does it match a source name?
1289 if (share
.strPath
.substr(0,8) == "shout://")
1291 CURL
url(share
.strPath
);
1292 if (strPath
== url
.GetHostName())
1296 // doesn't match a name, so try the source path
1297 std::vector
<std::string
> vecPaths
;
1299 // add any concatenated paths if they exist
1300 if (!share
.vecPaths
.empty())
1301 vecPaths
= share
.vecPaths
;
1303 // add the actual share path at the front of the vector
1304 vecPaths
.insert(vecPaths
.begin(), share
.strPath
);
1307 for (const auto &path
: vecPaths
)
1309 // remove user details, and ensure path only uses forward slashes
1310 // and ends with a trailing slash so as not to match a substring
1311 CURL
urlShare(path
);
1312 urlShare
.SetOptions("");
1313 urlShare
.SetProtocolOptions("");
1314 std::string strShare
= urlShare
.GetWithoutUserDetails();
1315 ForceForwardSlashes(strShare
);
1316 if (!URIUtils::HasSlashAtEnd(strShare
))
1318 size_t iLenShare
= strShare
.size();
1320 if ((iLenPath
>= iLenShare
) && StringUtils::StartsWithNoCase(strDest
, strShare
) && (iLenShare
> iLength
))
1322 // if exact match, return it immediately
1323 if (iLenPath
== iLenShare
)
1325 // if the path EXACTLY matches an item in a concatenated path
1326 // set source name to true to load the full virtualpath
1327 bIsSourceName
= false;
1328 if (vecPaths
.size() > 1)
1329 bIsSourceName
= true;
1333 iLength
= iLenShare
;
1338 // return the index of the share with the longest match
1342 // rar:// and zip://
1343 // if archive wasn't mounted, look for a matching share for the archive instead
1344 if( StringUtils::StartsWithNoCase(strPath
, "rar://") || StringUtils::StartsWithNoCase(strPath
, "zip://") )
1346 // get the hostname portion of the url since it contains the archive file
1347 strPath
= checkURL
.GetHostName();
1349 bIsSourceName
= false;
1351 return GetMatchingSource(strPath
, VECSOURCES
, bDummy
);
1354 CLog::Log(LOGDEBUG
, "CUtil::GetMatchingSource: no matching source found for [{}]", strPath1
);
1359 std::string
CUtil::TranslateSpecialSource(const std::string
&strSpecial
)
1361 if (!strSpecial
.empty() && strSpecial
[0] == '$')
1363 if (StringUtils::StartsWithNoCase(strSpecial
, "$home"))
1364 return URIUtils::AddFileToFolder("special://home/", strSpecial
.substr(5));
1365 else if (StringUtils::StartsWithNoCase(strSpecial
, "$subtitles"))
1366 return URIUtils::AddFileToFolder("special://subtitles/", strSpecial
.substr(10));
1367 else if (StringUtils::StartsWithNoCase(strSpecial
, "$userdata"))
1368 return URIUtils::AddFileToFolder("special://userdata/", strSpecial
.substr(9));
1369 else if (StringUtils::StartsWithNoCase(strSpecial
, "$database"))
1370 return URIUtils::AddFileToFolder("special://database/", strSpecial
.substr(9));
1371 else if (StringUtils::StartsWithNoCase(strSpecial
, "$thumbnails"))
1372 return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial
.substr(11));
1373 else if (StringUtils::StartsWithNoCase(strSpecial
, "$recordings"))
1374 return URIUtils::AddFileToFolder("special://recordings/", strSpecial
.substr(11));
1375 else if (StringUtils::StartsWithNoCase(strSpecial
, "$screenshots"))
1376 return URIUtils::AddFileToFolder("special://screenshots/", strSpecial
.substr(12));
1377 else if (StringUtils::StartsWithNoCase(strSpecial
, "$musicplaylists"))
1378 return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial
.substr(15));
1379 else if (StringUtils::StartsWithNoCase(strSpecial
, "$videoplaylists"))
1380 return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial
.substr(15));
1381 else if (StringUtils::StartsWithNoCase(strSpecial
, "$cdrips"))
1382 return URIUtils::AddFileToFolder("special://cdrips/", strSpecial
.substr(7));
1383 // this one will be removed post 2.0
1384 else if (StringUtils::StartsWithNoCase(strSpecial
, "$playlists"))
1385 return URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
), strSpecial
.substr(10));
1390 std::string
CUtil::MusicPlaylistsLocation()
1392 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1393 std::vector
<std::string
> vec
;
1394 vec
.push_back(URIUtils::AddFileToFolder(path
, "music"));
1395 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1396 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1399 std::string
CUtil::VideoPlaylistsLocation()
1401 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1402 std::vector
<std::string
> vec
;
1403 vec
.push_back(URIUtils::AddFileToFolder(path
, "video"));
1404 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1405 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1408 void CUtil::DeleteMusicDatabaseDirectoryCache()
1410 CUtil::DeleteDirectoryCache("mdb-");
1411 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1414 void CUtil::DeleteVideoDatabaseDirectoryCache()
1416 CUtil::DeleteDirectoryCache("vdb-");
1417 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1420 void CUtil::DeleteDirectoryCache(const std::string
&prefix
)
1422 std::string searchPath
= "special://temp/";
1423 CFileItemList items
;
1424 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".fi", DIR_FLAG_NO_FILE_DIRS
))
1427 for (const auto &item
: items
)
1429 if (item
->m_bIsFolder
)
1431 std::string fileName
= URIUtils::GetFileName(item
->GetPath());
1432 if (StringUtils::StartsWith(fileName
, prefix
))
1433 XFILE::CFile::Delete(item
->GetPath());
1438 void CUtil::GetRecursiveListing(const std::string
& strPath
, CFileItemList
& items
, const std::string
& strMask
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1440 CFileItemList myItems
;
1441 CDirectory::GetDirectory(strPath
,myItems
,strMask
,flags
);
1442 for (const auto &item
: myItems
)
1444 if (item
->m_bIsFolder
)
1445 CUtil::GetRecursiveListing(item
->GetPath(),items
,strMask
,flags
);
1451 void CUtil::GetRecursiveDirsListing(const std::string
& strPath
, CFileItemList
& item
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1453 CFileItemList myItems
;
1454 CDirectory::GetDirectory(strPath
,myItems
,"",flags
);
1455 for (const auto &i
: myItems
)
1457 if (i
->m_bIsFolder
&& !i
->IsPath(".."))
1460 CUtil::GetRecursiveDirsListing(i
->GetPath(),item
,flags
);
1465 void CUtil::ForceForwardSlashes(std::string
& strPath
)
1467 size_t iPos
= strPath
.rfind('\\');
1468 while (iPos
!= std::string::npos
)
1470 strPath
.at(iPos
) = '/';
1471 iPos
= strPath
.rfind('\\');
1475 double CUtil::AlbumRelevance(const std::string
& strAlbumTemp1
, const std::string
& strAlbum1
, const std::string
& strArtistTemp1
, const std::string
& strArtist1
)
1477 // case-insensitive fuzzy string comparison on the album and artist for relevance
1478 // weighting is identical, both album and artist are 50% of the total relevance
1479 // a missing artist means the maximum relevance can only be 0.50
1480 std::string strAlbumTemp
= strAlbumTemp1
;
1481 StringUtils::ToLower(strAlbumTemp
);
1482 std::string strAlbum
= strAlbum1
;
1483 StringUtils::ToLower(strAlbum
);
1484 double fAlbumPercentage
= fstrcmp(strAlbumTemp
.c_str(), strAlbum
.c_str());
1485 double fArtistPercentage
= 0.0;
1486 if (!strArtist1
.empty())
1488 std::string strArtistTemp
= strArtistTemp1
;
1489 StringUtils::ToLower(strArtistTemp
);
1490 std::string strArtist
= strArtist1
;
1491 StringUtils::ToLower(strArtist
);
1492 fArtistPercentage
= fstrcmp(strArtistTemp
.c_str(), strArtist
.c_str());
1494 double fRelevance
= fAlbumPercentage
* 0.5 + fArtistPercentage
* 0.5;
1498 bool CUtil::MakeShortenPath(std::string StrInput
, std::string
& StrOutput
, size_t iTextMaxLength
)
1500 size_t iStrInputSize
= StrInput
.size();
1501 if(iStrInputSize
<= 0 || iTextMaxLength
>= iStrInputSize
)
1503 StrOutput
= StrInput
;
1508 size_t nGreaterDelim
, nPos
;
1510 nPos
= StrInput
.find_last_of( '\\' );
1511 if (nPos
!= std::string::npos
)
1515 nPos
= StrInput
.find_last_of( '/' );
1516 if (nPos
!= std::string::npos
)
1519 if ( cDelim
== '\0' )
1522 if (nPos
== StrInput
.size() - 1)
1524 StrInput
.erase(StrInput
.size() - 1);
1525 nPos
= StrInput
.find_last_of(cDelim
);
1527 while( iTextMaxLength
< iStrInputSize
)
1529 nPos
= StrInput
.find_last_of( cDelim
, nPos
);
1530 nGreaterDelim
= nPos
;
1532 if (nPos
== std::string::npos
|| nPos
== 0)
1535 nPos
= StrInput
.find_last_of( cDelim
, nPos
- 1 );
1537 if ( nPos
== std::string::npos
)
1539 if ( nGreaterDelim
> nPos
)
1540 StrInput
.replace( nPos
+ 1, nGreaterDelim
- nPos
- 1, ".." );
1541 iStrInputSize
= StrInput
.size();
1543 // replace any additional /../../ with just /../ if necessary
1544 std::string replaceDots
= StringUtils::Format("..{}..", cDelim
);
1545 while (StrInput
.size() > (unsigned int)iTextMaxLength
)
1546 if (!StringUtils::Replace(StrInput
, replaceDots
, ".."))
1548 // finally, truncate our string to force inside our max text length,
1549 // replacing the last 2 characters with ".."
1552 // "smb://../Playboy Swimsuit Cal.."
1553 if (iTextMaxLength
> 2 && StrInput
.size() > (unsigned int)iTextMaxLength
)
1555 StrInput
.erase(iTextMaxLength
- 2);
1558 StrOutput
= StrInput
;
1562 bool CUtil::SupportsWriteFileOperations(const std::string
& strPath
)
1564 // currently only hd, smb, nfs and dav support delete and rename
1565 if (URIUtils::IsHD(strPath
))
1567 if (URIUtils::IsSmb(strPath
))
1569 if (URIUtils::IsPVRRecording(strPath
))
1570 return CPVRDirectory::SupportsWriteFileOperations(strPath
);
1571 if (URIUtils::IsNfs(strPath
))
1573 if (URIUtils::IsDAV(strPath
))
1575 if (URIUtils::IsStack(strPath
))
1576 return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath
));
1577 if (URIUtils::IsMultiPath(strPath
))
1578 return CMultiPathDirectory::SupportsWriteFileOperations(strPath
);
1580 if (CServiceBroker::IsAddonInterfaceUp())
1583 for (const auto& addon
: CServiceBroker::GetVFSAddonCache().GetAddonInstances())
1585 const auto& info
= addon
->GetProtocolInfo();
1586 auto prots
= StringUtils::Split(info
.type
, "|");
1587 if (info
.supportWrite
&&
1588 std::find(prots
.begin(), prots
.end(), url
.GetProtocol()) != prots
.end())
1596 bool CUtil::SupportsReadFileOperations(const std::string
& strPath
)
1598 return !URIUtils::IsVideoDb(strPath
);
1601 std::string
CUtil::GetDefaultFolderThumb(const std::string
&folderThumb
)
1603 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(folderThumb
))
1608 void CUtil::GetSkinThemes(std::vector
<std::string
>& vecTheme
)
1610 static const std::string TexturesXbt
= "Textures.xbt";
1612 std::string strPath
= URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media");
1613 CFileItemList items
;
1614 CDirectory::GetDirectory(strPath
, items
, "", DIR_FLAG_DEFAULTS
);
1615 // Search for Themes in the Current skin!
1616 for (const auto &pItem
: items
)
1618 if (!pItem
->m_bIsFolder
)
1620 std::string strExtension
= URIUtils::GetExtension(pItem
->GetPath());
1621 std::string strLabel
= pItem
->GetLabel();
1622 if ((strExtension
== ".xbt" && !StringUtils::EqualsNoCase(strLabel
, TexturesXbt
)))
1623 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - strExtension
.size()));
1627 // check if this is an xbt:// VFS path
1628 CURL
itemUrl(pItem
->GetPath());
1629 if (!itemUrl
.IsProtocol("xbt") || !itemUrl
.GetFileName().empty())
1632 std::string strLabel
= URIUtils::GetFileName(itemUrl
.GetHostName());
1633 if (!StringUtils::EqualsNoCase(strLabel
, TexturesXbt
))
1634 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - URIUtils::GetExtension(strLabel
).size()));
1637 std::sort(vecTheme
.begin(), vecTheme
.end(), sortstringbyname());
1640 void CUtil::InitRandomSeed()
1643 auto now
= std::chrono::steady_clock::now();
1644 auto seed
= now
.time_since_epoch();
1646 srand(static_cast<unsigned int>(seed
.count()));
1649 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN_TVOS)
1650 bool CUtil::RunCommandLine(const std::string
& cmdLine
, bool waitExit
)
1652 std::vector
<std::string
> args
= StringUtils::Split(cmdLine
, ",");
1654 // Strip quotes and whitespace around the arguments, or exec will fail.
1655 // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1656 // But it's still limited, for example quotes inside the strings are not expanded, etc.
1657 //! @todo Maybe some python library routine can parse this more properly ?
1658 for (std::vector
<std::string
>::iterator it
= args
.begin(); it
!= args
.end(); ++it
)
1661 pos
= it
->find_first_not_of(" \t\n\"'");
1662 if (pos
!= std::string::npos
)
1667 pos
= it
->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1669 it
->erase(++pos
, it
->size());
1673 return Command(args
, waitExit
);
1676 bool CUtil::Command(const std::vector
<std::string
>& arrArgs
, bool waitExit
)
1679 printf("Executing: ");
1680 for (const auto &arg
: arrArgs
)
1681 printf("%s ", arg
.c_str());
1685 pid_t child
= fork();
1691 // fork again in order not to leave a zombie process
1695 else if (child
!= 0)
1701 if (!arrArgs
.empty())
1703 char **args
= (char **)alloca(sizeof(char *) * (arrArgs
.size() + 3));
1704 memset(args
, 0, (sizeof(char *) * (arrArgs
.size() + 3)));
1705 for (size_t i
=0; i
<arrArgs
.size(); i
++)
1706 args
[i
] = const_cast<char *>(arrArgs
[i
].c_str());
1707 execvp(args
[0], args
);
1712 waitpid(child
, &n
, 0);
1715 return (waitExit
) ? (WEXITSTATUS(n
) == 0) : true;
1719 int CUtil::LookupRomanDigit(char roman_digit
)
1721 switch (roman_digit
)
1749 int CUtil::TranslateRomanNumeral(const char* roman_numeral
)
1754 if (roman_numeral
&& roman_numeral
[0])
1761 while (*roman_numeral
)
1763 int digit
= CUtil::LookupRomanDigit(*roman_numeral
);
1766 // General sanity checks
1768 // numeral not in LUT
1775 // N = 10^n may not precede (N+1) > 10^(N+1)
1776 if (test
== 1 && digit
> last
* 10)
1779 // N = 5*10^n may not precede (N+1) >= N
1780 if (test
== 5 && digit
>= last
)
1783 // End general sanity checks
1787 // smaller numerals may not repeat before a larger one
1796 else if (last
== digit
)
1805 decimal
+= 2 * last
- temp_sum
;
1807 decimal
+= temp_sum
;
1814 // Post general sanity checks
1816 // numerals may not repeat more than thrice
1825 decimal
+= temp_sum
;
1827 decimal
+= 2 * last
- temp_sum
;
1832 std::string
CUtil::ResolveExecutablePath()
1834 std::string strExecutablePath
;
1835 #ifdef TARGET_WINDOWS
1836 static const size_t bufSize
= MAX_PATH
* 2;
1837 wchar_t* buf
= new wchar_t[bufSize
];
1839 ::GetModuleFileNameW(0, buf
, bufSize
);
1841 g_charsetConverter
.wToUTF8(buf
,strExecutablePath
);
1843 #elif defined(TARGET_DARWIN)
1844 char given_path
[2*MAXPATHLEN
];
1845 size_t path_size
=2*MAXPATHLEN
;
1847 CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
1848 strExecutablePath
= given_path
;
1849 #elif defined(TARGET_FREEBSD)
1856 mib
[2] = KERN_PROC_PATHNAME
;
1859 buflen
= sizeof(buf
) - 1;
1860 if(sysctl(mib
, 4, buf
, &buflen
, NULL
, 0) < 0)
1861 strExecutablePath
= "";
1863 strExecutablePath
= buf
;
1864 #elif defined(TARGET_ANDROID)
1865 strExecutablePath
= CXBMCApp::getApplicationInfo().nativeLibraryDir
;
1867 std::string appName
= CCompileInfo::GetAppName();
1868 std::string libName
= "lib" + appName
+ ".so";
1869 StringUtils::ToLower(libName
);
1870 strExecutablePath
+= "/" + libName
;
1872 /* Get our PID and build the name of the link in /proc */
1873 pid_t pid
= getpid();
1874 char linkname
[64]; /* /proc/<pid>/exe */
1875 snprintf(linkname
, sizeof(linkname
), "/proc/%i/exe", pid
);
1877 /* Now read the symbolic link */
1878 char buf
[PATH_MAX
+ 1];
1881 int ret
= readlink(linkname
, buf
, sizeof(buf
) - 1);
1885 strExecutablePath
= buf
;
1887 return strExecutablePath
;
1890 std::string
CUtil::GetFrameworksPath(bool forPython
)
1892 std::string strFrameworksPath
;
1893 #if defined(TARGET_DARWIN)
1894 strFrameworksPath
= CDarwinUtils::GetFrameworkPath(forPython
);
1896 return strFrameworksPath
;
1899 void CUtil::GetVideoBasePathAndFileName(const std::string
& videoPath
, std::string
& basePath
, std::string
& videoFileName
)
1901 CFileItem
item(videoPath
, false);
1902 videoFileName
= URIUtils::ReplaceExtension(URIUtils::GetFileName(videoPath
), "");
1904 if (item
.HasVideoInfoTag())
1905 basePath
= item
.GetVideoInfoTag()->m_basePath
;
1907 if (basePath
.empty() && item
.IsOpticalMediaFile())
1908 basePath
= item
.GetLocalMetadataPath();
1910 CURL
url(videoPath
);
1911 if (basePath
.empty() && url
.IsProtocol("bluray"))
1913 basePath
= url
.GetHostName();
1914 videoFileName
= URIUtils::ReplaceExtension(GetTitleFromPath(url
.GetHostName()), "");
1916 url
= CURL(url
.GetHostName());
1917 if (url
.IsProtocol("udf"))
1918 basePath
= URIUtils::GetParentPath(url
.GetHostName());
1921 if (basePath
.empty())
1922 basePath
= URIUtils::GetBasePath(videoPath
);
1925 void CUtil::GetItemsToScan(const std::string
& videoPath
,
1926 const std::string
& item_exts
,
1927 const std::vector
<std::string
>& sub_dirs
,
1928 CFileItemList
& items
)
1930 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
1932 if (!videoPath
.empty())
1933 CDirectory::GetDirectory(videoPath
, items
, item_exts
, flags
);
1935 std::vector
<std::string
> additionalPaths
;
1936 for (const auto &item
: items
)
1938 for (const auto& subdir
: sub_dirs
)
1940 if (StringUtils::EqualsNoCase(item
->GetLabel(), subdir
))
1941 additionalPaths
.push_back(item
->GetPath());
1945 for (std::vector
<std::string
>::const_iterator it
= additionalPaths
.begin(); it
!= additionalPaths
.end(); ++it
)
1947 CFileItemList moreItems
;
1948 CDirectory::GetDirectory(*it
, moreItems
, item_exts
, flags
);
1949 items
.Append(moreItems
);
1954 void CUtil::ScanPathsForAssociatedItems(const std::string
& videoName
,
1955 const CFileItemList
& items
,
1956 const std::vector
<std::string
>& item_exts
,
1957 std::vector
<std::string
>& associatedFiles
)
1959 for (const auto &pItem
: items
)
1961 if (pItem
->m_bIsFolder
)
1964 std::string strCandidate
= URIUtils::GetFileName(pItem
->GetPath());
1967 if (std::find(associatedFiles
.begin(), associatedFiles
.end(), pItem
->GetPath()) != associatedFiles
.end())
1970 URIUtils::RemoveExtension(strCandidate
);
1971 // NOTE: We don't know if one of videoName or strCandidate is URL-encoded and the other is not, so try both
1972 if (StringUtils::StartsWithNoCase(strCandidate
, videoName
) || (StringUtils::StartsWithNoCase(strCandidate
, CURL::Decode(videoName
))))
1974 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1975 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), "", item_exts
, associatedFiles
);
1978 associatedFiles
.push_back(pItem
->GetPath());
1979 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
1980 CURL::GetRedacted(pItem
->GetPath()));
1985 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1986 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), videoName
, item_exts
, associatedFiles
);
1991 int CUtil::ScanArchiveForAssociatedItems(const std::string
& strArchivePath
,
1992 const std::string
& videoNameNoExt
,
1993 const std::vector
<std::string
>& item_exts
,
1994 std::vector
<std::string
>& associatedFiles
)
1996 CLog::LogF(LOGDEBUG
, "Scanning archive {}", CURL::GetRedacted(strArchivePath
));
1997 int nItemsAdded
= 0;
1998 CFileItemList ItemList
;
2000 // zip only gets the root dir
2001 if (URIUtils::HasExtension(strArchivePath
, ".zip"))
2003 CURL
pathToUrl(strArchivePath
);
2004 CURL zipURL
= URIUtils::CreateArchivePath("zip", pathToUrl
, "");
2005 if (!CDirectory::GetDirectory(zipURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
2008 else if (URIUtils::HasExtension(strArchivePath
, ".rar"))
2010 CURL
pathToUrl(strArchivePath
);
2011 CURL rarURL
= URIUtils::CreateArchivePath("rar", pathToUrl
, "");
2012 if (!CDirectory::GetDirectory(rarURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
2015 for (const auto &item
: ItemList
)
2017 std::string strPathInRar
= item
->GetPath();
2018 std::string strExt
= URIUtils::GetExtension(strPathInRar
);
2020 // Check another archive in archive
2021 if (strExt
== ".zip" || strExt
== ".rar")
2024 ScanArchiveForAssociatedItems(strPathInRar
, videoNameNoExt
, item_exts
, associatedFiles
);
2028 // check that the found filename matches the movie filename
2029 size_t fnl
= videoNameNoExt
.size();
2030 // NOTE: We don't know if videoNameNoExt is URL-encoded, so try both
2032 !(StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), videoNameNoExt
) ||
2033 StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), CURL::Decode(videoNameNoExt
))))
2036 for (const auto& ext
: item_exts
)
2038 if (StringUtils::EqualsNoCase(strExt
, ext
))
2040 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
2041 CURL::GetRedacted(strPathInRar
));
2042 associatedFiles
.push_back(strPathInRar
);
2052 void CUtil::ScanForExternalSubtitles(const std::string
& strMovie
, std::vector
<std::string
>& vecSubtitles
)
2054 auto start
= std::chrono::steady_clock::now();
2056 CFileItem
item(strMovie
, false);
2057 if ((item
.IsInternetStream() && !URIUtils::IsOnLAN(item
.GetDynPath()))
2058 || item
.IsPlayList()
2063 CLog::Log(LOGDEBUG
, "{}: Searching for subtitles...", __FUNCTION__
);
2065 std::string strBasePath
;
2066 std::string strSubtitle
;
2068 GetVideoBasePathAndFileName(strMovie
, strBasePath
, strSubtitle
);
2070 CFileItemList items
;
2071 const std::vector
<std::string
> common_sub_dirs
= { "subs", "subtitles", "vobsubs", "sub", "vobsub", "subtitle" };
2072 const std::string subtitleExtensions
= CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions();
2073 GetItemsToScan(strBasePath
, subtitleExtensions
, common_sub_dirs
, items
);
2075 const std::string customPath
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH
);
2077 if (!CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() && !customPath
.empty()) // to avoid checking non-existent directories (network) every time..
2079 if (!CServiceBroker::GetNetwork().IsAvailable() && !URIUtils::IsHD(customPath
))
2081 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's inaccessible");
2082 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2084 else if (!CDirectory::Exists(customPath
))
2086 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistent");
2087 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
2090 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(1);
2093 std::vector
<std::string
> strLookInPaths
;
2094 // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
2095 if (CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() == 1)
2097 std::string strPath2
= customPath
;
2098 URIUtils::AddSlashAtEnd(strPath2
);
2099 strLookInPaths
.push_back(strPath2
);
2102 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
2103 for (const std::string
& path
: strLookInPaths
)
2105 CFileItemList moreItems
;
2106 CDirectory::GetDirectory(path
, moreItems
, subtitleExtensions
, flags
);
2107 items
.Append(moreItems
);
2110 std::vector
<std::string
> exts
= StringUtils::Split(subtitleExtensions
, '|');
2111 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".zip"), exts
.end());
2112 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".rar"), exts
.end());
2114 ScanPathsForAssociatedItems(strSubtitle
, items
, exts
, vecSubtitles
);
2116 size_t iSize
= vecSubtitles
.size();
2117 for (size_t i
= 0; i
< iSize
; i
++)
2119 if (URIUtils::HasExtension(vecSubtitles
[i
], ".smi"))
2121 //Cache multi-language sami subtitle
2122 CDVDSubtitleStream stream
;
2123 if (stream
.Open(vecSubtitles
[i
]))
2125 CDVDSubtitleTagSami TagConv
;
2126 TagConv
.LoadHead(&stream
);
2127 if (TagConv
.m_Langclass
.size() >= 2)
2129 for (const auto &lang
: TagConv
.m_Langclass
)
2131 std::string strDest
=
2132 StringUtils::Format("special://temp/subtitle.{}.{}.smi", lang
.Name
, i
);
2133 if (CFile::Copy(vecSubtitles
[i
], strDest
))
2135 CLog::Log(LOGINFO
, " cached subtitle {}->{}", CURL::GetRedacted(vecSubtitles
[i
]),
2137 vecSubtitles
.push_back(strDest
);
2145 auto end
= std::chrono::steady_clock::now();
2146 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
2147 CLog::Log(LOGDEBUG
, "{}: END (total time: {} ms)", __FUNCTION__
, duration
.count());
2150 ExternalStreamInfo
CUtil::GetExternalStreamDetailsFromFilename(const std::string
& videoPath
, const std::string
& associatedFile
)
2152 ExternalStreamInfo info
;
2154 std::string videoBaseName
= URIUtils::GetFileName(videoPath
);
2155 URIUtils::RemoveExtension(videoBaseName
);
2157 std::string toParse
= URIUtils::GetFileName(associatedFile
);
2158 URIUtils::RemoveExtension(toParse
);
2160 // we check left part - if it's same as video base name - strip it
2161 if (StringUtils::StartsWithNoCase(toParse
, videoBaseName
))
2162 toParse
= toParse
.substr(videoBaseName
.length());
2163 else if (URIUtils::GetExtension(associatedFile
) == ".sub" && URIUtils::IsInArchive(associatedFile
))
2165 // exclude parsing of vobsub file names that are embedded in an archive
2166 CLog::Log(LOGDEBUG
, "{} - skipping archived vobsub filename parsing: {}", __FUNCTION__
,
2167 CURL::GetRedacted(associatedFile
));
2171 // trim any non-alphanumeric char in the beginning
2172 std::string::iterator result
= std::find_if(toParse
.begin(), toParse
.end(), StringUtils::isasciialphanum
);
2175 if (result
!= toParse
.end()) // if we have anything to parse
2177 std::string
inputString(result
, toParse
.end());
2178 std::string
delimiters(" .-");
2179 std::vector
<std::string
> tokens
;
2180 StringUtils::Tokenize(inputString
, tokens
, delimiters
);
2182 for (auto it
= tokens
.rbegin(); it
!= tokens
.rend(); ++it
)
2184 // try to recognize a flag
2185 std::string
flag_tmp(*it
);
2186 StringUtils::ToLower(flag_tmp
);
2187 if (!flag_tmp
.compare("none"))
2189 info
.flag
|= StreamFlags::FLAG_NONE
;
2192 else if (!flag_tmp
.compare("default"))
2194 info
.flag
|= StreamFlags::FLAG_DEFAULT
;
2197 else if (!flag_tmp
.compare("forced"))
2199 info
.flag
|= StreamFlags::FLAG_FORCED
;
2203 if (info
.language
.empty())
2205 std::string langCode
;
2206 // try to recognize language
2207 if (g_LangCodeExpander
.ConvertToISO6392B(*it
, langCode
))
2209 info
.language
= langCode
;
2214 name
= (*it
) + " " + name
;
2218 name
+= g_localizeStrings
.Get(21602); // External
2219 StringUtils::Trim(name
);
2220 info
.name
= StringUtils::RemoveDuplicatedSpacesAndTabs(name
);
2222 info
.flag
= StreamFlags::FLAG_NONE
;
2224 CLog::Log(LOGDEBUG
, "{} - Language = '{}' / Name = '{}' / Flag = '{}' from {}", __FUNCTION__
,
2225 info
.language
, info
.name
, info
.flag
, CURL::GetRedacted(associatedFile
));
2230 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2232 bool CUtil::FindVobSubPair(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strIdxPath
, std::string
& strSubPath
)
2234 if (URIUtils::HasExtension(strIdxPath
, ".idx"))
2236 std::string strIdxFile
;
2237 std::string strIdxDirectory
;
2238 URIUtils::Split(strIdxPath
, strIdxDirectory
, strIdxFile
);
2239 for (const auto &subtitlePath
: vecSubtitles
)
2241 std::string strSubFile
;
2242 std::string strSubDirectory
;
2243 URIUtils::Split(subtitlePath
, strSubDirectory
, strSubFile
);
2244 if (URIUtils::IsInArchive(subtitlePath
))
2245 strSubDirectory
= CURL::Decode(strSubDirectory
);
2246 if (URIUtils::HasExtension(strSubFile
, ".sub") &&
2247 (URIUtils::PathEquals(URIUtils::ReplaceExtension(strIdxPath
,""),
2248 URIUtils::ReplaceExtension(subtitlePath
,"")) ||
2249 (strSubDirectory
.size() >= 11 &&
2250 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(strIdxPath
,"")))))
2252 strSubPath
= subtitlePath
;
2260 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2262 bool CUtil::IsVobSub(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strSubPath
)
2264 if (URIUtils::HasExtension(strSubPath
, ".sub"))
2266 std::string strSubFile
;
2267 std::string strSubDirectory
;
2268 URIUtils::Split(strSubPath
, strSubDirectory
, strSubFile
);
2269 if (URIUtils::IsInArchive(strSubPath
))
2270 strSubDirectory
= CURL::Decode(strSubDirectory
);
2271 for (const auto &subtitlePath
: vecSubtitles
)
2273 std::string strIdxFile
;
2274 std::string strIdxDirectory
;
2275 URIUtils::Split(subtitlePath
, strIdxDirectory
, strIdxFile
);
2276 if (URIUtils::HasExtension(strIdxFile
, ".idx") &&
2277 (URIUtils::PathEquals(URIUtils::ReplaceExtension(subtitlePath
,""),
2278 URIUtils::ReplaceExtension(strSubPath
,"")) ||
2279 (strSubDirectory
.size() >= 11 &&
2280 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(subtitlePath
,"")))))
2287 /*! \brief find a plain or archived vobsub .sub file corresponding to an .idx file
2289 std::string
CUtil::GetVobSubSubFromIdx(const std::string
& vobSubIdx
)
2291 std::string vobSub
= URIUtils::ReplaceExtension(vobSubIdx
, ".sub");
2293 // check if a .sub file exists in the same directory
2294 if (CFile::Exists(vobSub
))
2299 // look inside a .rar or .zip in the same directory
2300 const std::string archTypes
[] = { "rar", "zip" };
2301 std::string vobSubFilename
= URIUtils::GetFileName(vobSub
);
2302 for (const std::string
& archType
: archTypes
)
2304 vobSub
= URIUtils::CreateArchivePath(archType
,
2305 CURL(URIUtils::ReplaceExtension(vobSubIdx
, std::string(".") + archType
)),
2306 vobSubFilename
).Get();
2307 if (CFile::Exists(vobSub
))
2311 return std::string();
2314 /*! \brief find a .idx file from a path of a plain or archived vobsub .sub file
2316 std::string
CUtil::GetVobSubIdxFromSub(const std::string
& vobSub
)
2318 std::string vobSubIdx
= URIUtils::ReplaceExtension(vobSub
, ".idx");
2320 // check if a .idx file exists in the same directory
2321 if (CFile::Exists(vobSubIdx
))
2326 // look outside archive (usually .rar) if the .sub is inside one
2327 if (URIUtils::IsInArchive(vobSub
))
2330 std::string archiveFile
= URIUtils::GetDirectory(vobSub
);
2331 std::string vobSubIdxDir
= URIUtils::GetParentPath(archiveFile
);
2333 if (!vobSubIdxDir
.empty())
2335 std::string vobSubIdxFilename
= URIUtils::GetFileName(vobSubIdx
);
2336 std::string vobSubIdx
= URIUtils::AddFileToFolder(vobSubIdxDir
, vobSubIdxFilename
);
2338 if (CFile::Exists(vobSubIdx
))
2343 return std::string();
2346 void CUtil::ScanForExternalAudio(const std::string
& videoPath
, std::vector
<std::string
>& vecAudio
)
2348 CFileItem
item(videoPath
, false);
2349 if ( item
.IsInternetStream()
2350 || item
.IsPlayList()
2356 std::string strBasePath
;
2357 std::string strAudio
;
2359 GetVideoBasePathAndFileName(videoPath
, strBasePath
, strAudio
);
2361 CFileItemList items
;
2362 const std::vector
<std::string
> common_sub_dirs
= { "audio", "tracks"};
2363 GetItemsToScan(strBasePath
, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), common_sub_dirs
, items
);
2365 std::vector
<std::string
> exts
= StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2367 CVideoDatabase database
;
2369 bool useAllExternalAudio
= database
.GetUseAllExternalAudioForVideo(videoPath
);
2371 if (useAllExternalAudio
)
2373 for (const auto& audioItem
: items
.GetList())
2375 vecAudio
.push_back(audioItem
.get()->GetPath());
2379 ScanPathsForAssociatedItems(strAudio
, items
, exts
, vecAudio
);
2382 bool CUtil::CanBindPrivileged()
2387 return true; //root user can always bind to privileged ports
2391 //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2392 bool canbind
= false;
2393 cap_t capabilities
= cap_get_proc();
2396 cap_flag_value_t value
;
2397 if (cap_get_flag(capabilities
, CAP_NET_BIND_SERVICE
, CAP_EFFECTIVE
, &value
) == 0)
2400 cap_free(capabilities
);
2409 #endif //HAVE_LIBCAP
2411 #else //TARGET_POSIX
2415 #endif //TARGET_POSIX
2418 bool CUtil::ValidatePort(int port
)
2420 // check that it's a valid port
2422 if (!CUtil::CanBindPrivileged() && (port
< 1024 || port
> 65535))
2426 if (port
<= 0 || port
> 65535)
2432 int CUtil::GetRandomNumber()
2434 #if !defined(TARGET_WINDOWS)
2435 return rand_r(&s_randomSeed
);
2437 unsigned int number
;
2438 if (rand_s(&number
) == 0)
2445 void CUtil::CopyUserDataIfNeeded(const std::string
& strPath
,
2446 const std::string
& file
,
2447 const std::string
& destname
)
2449 std::string destPath
;
2450 if (destname
.empty())
2451 destPath
= URIUtils::AddFileToFolder(strPath
, file
);
2453 destPath
= URIUtils::AddFileToFolder(strPath
, destname
);
2455 if (!CFile::Exists(destPath
))
2457 // need to copy it across
2458 std::string srcPath
= URIUtils::AddFileToFolder("special://xbmc/userdata/", file
);
2459 CFile::Copy(srcPath
, destPath
);