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/bionic_supplement/bionic_supplement.h"
29 #include "platform/android/activity/XBMCApp.h"
30 #include "CompileInfo.h"
32 #include "ServiceBroker.h"
34 #include "addons/VFSEntry.h"
35 #include "filesystem/Directory.h"
36 #include "filesystem/MultiPathDirectory.h"
37 #include "filesystem/PVRDirectory.h"
38 #include "filesystem/RSSDirectory.h"
39 #include "filesystem/SpecialProtocol.h"
40 #include "filesystem/StackDirectory.h"
46 #include "filesystem/UPnPDirectory.h"
48 #include "profiles/ProfileManager.h"
49 #include "utils/RegExp.h"
50 #include "windowing/GraphicContext.h"
51 #include "guilib/TextureManager.h"
52 #include "storage/MediaManager.h"
54 #include "utils/CharsetConverter.h"
55 #include "WIN32Util.h"
57 #if defined(TARGET_DARWIN)
58 #include "CompileInfo.h"
59 #include "platform/darwin/DarwinUtils.h"
62 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleStream.h"
63 #include "cores/VideoPlayer/DVDSubtitles/DVDSubtitleTagSami.h"
64 #include "filesystem/File.h"
65 #include "guilib/LocalizeStrings.h"
66 #include "platform/Environment.h"
67 #include "settings/AdvancedSettings.h"
68 #include "settings/MediaSettings.h"
69 #include "settings/Settings.h"
70 #include "settings/SettingsComponent.h"
71 #include "utils/Digest.h"
72 #include "utils/FileExtensionProvider.h"
73 #include "utils/FontUtils.h"
74 #include "utils/LangCodeExpander.h"
75 #include "utils/StringUtils.h"
76 #include "utils/TimeUtils.h"
77 #include "utils/URIUtils.h"
78 #include "utils/log.h"
79 #include "video/VideoDatabase.h"
80 #include "video/VideoInfoTag.h"
82 #include <sys/capability.h>
85 #include "cores/VideoPlayer/DVDDemuxers/DVDDemux.h"
90 using namespace MEDIA_DETECT
;
93 using namespace XFILE
;
94 using namespace PLAYLIST
;
95 using KODI::UTILITY::CDigest
;
97 #if !defined(TARGET_WINDOWS)
98 unsigned int CUtil::s_randomSeed
= time(NULL
);
103 #ifdef TARGET_WINDOWS
104 bool IsDirectoryValidRoot(std::wstring path
)
106 path
+= L
"\\system\\settings\\settings.xml";
107 #if defined(TARGET_WINDOWS_STORE)
108 auto h
= CreateFile2(path
.c_str(), GENERIC_READ
, 0, OPEN_EXISTING
, NULL
);
110 auto h
= CreateFileW(path
.c_str(), GENERIC_READ
, 0, nullptr,
111 OPEN_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, nullptr);
113 if (h
!= INVALID_HANDLE_VALUE
)
122 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
124 std::wstring strPathW
;
126 // Environment variable was set and we have a path
127 // Let's make sure it's not relative and test it
128 // so it's actually pointing to a directory containing
130 if (strPath
.find("..") != std::string::npos
)
132 //expand potential relative path to full path
133 g_charsetConverter
.utf8ToW(strPath
, strPathW
, false);
134 CWIN32Util::AddExtraLongPathPrefix(strPathW
);
135 auto bufSize
= GetFullPathNameW(strPathW
.c_str(), 0, nullptr, nullptr);
138 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
139 if (GetFullPathNameW(strPathW
.c_str(), bufSize
, buf
.get(), nullptr) <= bufSize
- 1)
141 strPathW
= buf
.get();
142 CWIN32Util::RemoveExtraLongPathPrefix(strPathW
);
144 if (IsDirectoryValidRoot(strPathW
))
146 g_charsetConverter
.wToUTF8(strPathW
, strPath
);
153 // Okay se no environment variable is set, let's
154 // grab the executable path and check if it's being
155 // run from a directory containing our stuff
156 strPath
= CUtil::ResolveExecutablePath();
157 auto last_sep
= strPath
.find_last_of(PATH_SEPARATOR_CHAR
);
158 if (last_sep
!= std::string::npos
)
159 strPath
= strPath
.substr(0, last_sep
);
161 g_charsetConverter
.utf8ToW(strPath
, strPathW
);
162 if (IsDirectoryValidRoot(strPathW
))
165 // Still nothing, let's check the current working
166 // directory and see if it points to a directory
167 // with our stuff in it. This bit should never be
168 // needed when running on a users system, it's intended
169 // to make our dev environment easier.
170 auto bufSize
= GetCurrentDirectoryW(0, nullptr);
173 auto buf
= std::make_unique
<wchar_t[]>(bufSize
);
174 if (0 != GetCurrentDirectoryW(bufSize
, buf
.get()))
176 std::string currentDirectory
;
177 std::wstring
currentDirectoryW(buf
.get());
178 CWIN32Util::RemoveExtraLongPathPrefix(currentDirectoryW
);
180 if (IsDirectoryValidRoot(currentDirectoryW
))
182 g_charsetConverter
.wToUTF8(currentDirectoryW
, currentDirectory
);
183 return currentDirectory
;
188 // If we ended up here we're most likely screwed
189 // we will crash in a few seconds
193 #if defined(TARGET_DARWIN)
194 #if !defined(TARGET_DARWIN_EMBEDDED)
195 bool IsDirectoryValidRoot(std::string path
)
197 path
+= "/system/settings/settings.xml";
198 return CFile::Exists(path
);
202 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
206 auto strHomePath
= CUtil::ResolveExecutablePath();
208 char given_path
[2 * MAXPATHLEN
];
209 size_t path_size
= 2 * MAXPATHLEN
;
211 result
= CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
214 // Move backwards to last /.
215 for (int n
= strlen(given_path
) - 1; given_path
[n
] != '/'; n
--)
216 given_path
[n
] = '\0';
218 #if defined(TARGET_DARWIN_EMBEDDED)
219 strcat(given_path
, "/AppData/AppHome/");
221 // Assume local path inside application bundle.
222 strcat(given_path
, "../Resources/");
223 strcat(given_path
, CCompileInfo::GetAppName());
224 strcat(given_path
, "/");
226 // if this path doesn't exist we
227 // might not be started from the app bundle
228 // but from the debugger/xcode. Lets
229 // see if this assumption is valid
230 if (!CDirectory::Exists(given_path
))
232 std::string given_path_stdstr
= CUtil::ResolveExecutablePath();
233 // try to find the correct folder by going back
234 // in the executable path until settings.xml was found
235 bool validRoot
= false;
238 given_path_stdstr
= URIUtils::GetParentPath(given_path_stdstr
);
239 validRoot
= IsDirectoryValidRoot(given_path_stdstr
);
241 while(given_path_stdstr
.length() > 0 && !validRoot
);
242 strncpy(given_path
, given_path_stdstr
.c_str(), sizeof(given_path
)-1);
247 // Convert to real path.
248 char real_path
[2 * MAXPATHLEN
];
249 if (realpath(given_path
, real_path
) != NULL
)
255 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
256 if (last_sep
!= std::string::npos
)
257 strPath
= strHomePath
.substr(0, last_sep
);
259 strPath
= strHomePath
;
265 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
266 std::string
GetHomePath(const std::string
& strTarget
, std::string strPath
)
270 auto strHomePath
= CUtil::ResolveExecutablePath();
271 size_t last_sep
= strHomePath
.find_last_of(PATH_SEPARATOR_CHAR
);
272 if (last_sep
!= std::string::npos
)
273 strPath
= strHomePath
.substr(0, last_sep
);
275 strPath
= strHomePath
;
277 /* Change strPath accordingly when target is KODI_HOME and when INSTALL_PATH
278 * and BIN_INSTALL_PATH differ
280 std::string installPath
= INSTALL_PATH
;
281 std::string binInstallPath
= BIN_INSTALL_PATH
;
283 if (strTarget
.empty() && installPath
.compare(binInstallPath
))
285 int pos
= strPath
.length() - binInstallPath
.length();
286 std::string tmp
= strPath
;
288 if (!tmp
.compare(binInstallPath
))
290 strPath
.erase(pos
, strPath
.length());
291 strPath
.append(installPath
);
300 std::string
CUtil::GetTitleFromPath(const std::string
& strFileNameAndPath
, bool bIsFolder
/* = false */)
302 CURL
pathToUrl(strFileNameAndPath
);
303 return GetTitleFromPath(pathToUrl
, bIsFolder
);
306 std::string
CUtil::GetTitleFromPath(const CURL
& url
, bool bIsFolder
/* = false */)
308 // use above to get the filename
309 std::string
path(url
.Get());
310 URIUtils::RemoveSlashAtEnd(path
);
311 std::string strFilename
= URIUtils::GetFileName(path
);
315 if (url
.IsProtocol("upnp"))
316 strFilename
= CUPnPDirectory::GetFriendlyName(url
);
319 if (url
.IsProtocol("rss") || url
.IsProtocol("rsss"))
323 if(dir
.GetDirectory(url
, items
) && !items
.m_strTitle
.empty())
324 return items
.m_strTitle
;
328 else if (url
.IsProtocol("shout"))
330 const std::string strFileNameAndPath
= url
.Get();
331 const size_t genre
= strFileNameAndPath
.find_first_of('=');
332 if(genre
== std::string::npos
)
333 strFilename
= g_localizeStrings
.Get(260);
335 strFilename
= g_localizeStrings
.Get(260) + " - " + strFileNameAndPath
.substr(genre
+1).c_str();
338 // Windows SMB Network (SMB)
339 else if (url
.IsProtocol("smb") && strFilename
.empty())
341 if (url
.GetHostName().empty())
343 strFilename
= g_localizeStrings
.Get(20171);
347 strFilename
= url
.GetHostName();
352 else if (url
.IsProtocol("sources"))
353 strFilename
= g_localizeStrings
.Get(744);
356 else if (StringUtils::StartsWith(path
, "special://musicplaylists"))
357 strFilename
= g_localizeStrings
.Get(136);
360 else if (StringUtils::StartsWith(path
, "special://videoplaylists"))
361 strFilename
= g_localizeStrings
.Get(136);
363 else if (URIUtils::HasParentInHostname(url
) && strFilename
.empty())
364 strFilename
= URIUtils::GetFileName(url
.GetHostName());
366 // now remove the extension if needed
367 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWEXTENSIONS
) && !bIsFolder
)
369 URIUtils::RemoveExtension(strFilename
);
373 // URLDecode since the original path may be an URL
374 strFilename
= CURL::Decode(strFilename
);
378 void CUtil::CleanString(const std::string
& strFileName
,
379 std::string
& strTitle
,
380 std::string
& strTitleAndYear
,
381 std::string
& strYear
,
382 bool bRemoveExtension
/* = false */,
383 bool bCleanChars
/* = true */)
385 strTitleAndYear
= strFileName
;
387 if (strFileName
== "..")
390 const std::shared_ptr
<CAdvancedSettings
> advancedSettings
= CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
391 const std::vector
<std::string
> ®exps
= advancedSettings
->m_videoCleanStringRegExps
;
393 CRegExp
reTags(true, CRegExp::autoUtf8
);
394 CRegExp
reYear(false, CRegExp::autoUtf8
);
396 if (!reYear
.RegComp(advancedSettings
->m_videoCleanDateTimeRegExp
))
398 CLog::Log(LOGERROR
, "{}: Invalid datetime clean RegExp:'{}'", __FUNCTION__
,
399 advancedSettings
->m_videoCleanDateTimeRegExp
);
403 if (reYear
.RegFind(strTitleAndYear
.c_str()) >= 0)
405 strTitleAndYear
= reYear
.GetMatch(1);
406 strYear
= reYear
.GetMatch(2);
410 URIUtils::RemoveExtension(strTitleAndYear
);
412 for (const auto ®exp
: regexps
)
414 if (!reTags
.RegComp(regexp
.c_str()))
415 { // invalid regexp - complain in logs
416 CLog::Log(LOGERROR
, "{}: Invalid string clean RegExp:'{}'", __FUNCTION__
, regexp
);
420 if ((j
=reTags
.RegFind(strTitleAndYear
.c_str())) > 0)
421 strTitleAndYear
= strTitleAndYear
.substr(0, j
);
424 // final cleanup - special characters used instead of spaces:
425 // all '_' tokens should be replaced by spaces
426 // if the file contains no spaces, all '.' tokens should be replaced by
427 // spaces - one possibility of a mistake here could be something like:
428 // "Dr..StrangeLove" - hopefully no one would have anything like this.
431 bool initialDots
= true;
432 bool alreadyContainsSpace
= (strTitleAndYear
.find(' ') != std::string::npos
);
434 for (char &c
: strTitleAndYear
)
439 if ((c
== '_') || ((!alreadyContainsSpace
) && !initialDots
&& (c
== '.')))
446 StringUtils::Trim(strTitleAndYear
);
447 strTitle
= strTitleAndYear
;
450 if (!strYear
.empty())
451 strTitleAndYear
= strTitle
+ " (" + strYear
+ ")";
453 // restore extension if needed
454 if (!bRemoveExtension
)
455 strTitleAndYear
+= URIUtils::GetExtension(strFileName
);
458 void CUtil::GetQualifiedFilename(const std::string
&strBasePath
, std::string
&strFilename
)
460 // Check if the filename is a fully qualified URL such as protocol://path/to/file
461 CURL
plItemUrl(strFilename
);
462 if (!plItemUrl
.GetProtocol().empty())
465 // If the filename starts "x:", "\\" or "/" it's already fully qualified so return
466 if (strFilename
.size() > 1)
468 if ( (strFilename
[1] == ':') || (strFilename
[0] == '/') )
470 if ( strFilename
[1] == ':' || (strFilename
[0] == '\\' && strFilename
[1] == '\\'))
474 // add to base path and then clean
475 strFilename
= URIUtils::AddFileToFolder(strBasePath
, strFilename
);
477 // get rid of any /./ or \.\ that happen to be there
478 StringUtils::Replace(strFilename
, "\\.\\", "\\");
479 StringUtils::Replace(strFilename
, "/./", "/");
481 // now find any "\\..\\" and remove them via GetParentPath
483 while ((pos
= strFilename
.find("/../")) != std::string::npos
)
485 std::string basePath
= strFilename
.substr(0, pos
+ 1);
486 strFilename
.erase(0, pos
+ 4);
487 basePath
= URIUtils::GetParentPath(basePath
);
488 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
490 while ((pos
= strFilename
.find("\\..\\")) != std::string::npos
)
492 std::string basePath
= strFilename
.substr(0, pos
+ 1);
493 strFilename
.erase(0, pos
+ 4);
494 basePath
= URIUtils::GetParentPath(basePath
);
495 strFilename
= URIUtils::AddFileToFolder(basePath
, strFilename
);
499 void CUtil::RunShortcut(const char* szShortcutPath
)
503 std::string
CUtil::GetHomePath(const std::string
& strTarget
)
505 auto strPath
= CEnvironment::getenv(strTarget
);
507 return ::GetHomePath(strTarget
, strPath
);
510 bool CUtil::IsPicture(const std::string
& strFile
)
512 return URIUtils::HasExtension(strFile
,
513 CServiceBroker::GetFileExtensionProvider().GetPictureExtensions()+ "|.tbn|.dds");
516 std::string
CUtil::GetSplashPath()
518 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" }};
519 auto it
= std::find_if(candidates
.begin(), candidates
.end(), [](std::string
const& file
) { return XFILE::CFile::Exists(file
); });
520 if (it
== candidates
.end())
521 throw std::runtime_error("No splash image found");
522 return CSpecialProtocol::TranslatePathConvertCase(*it
);
525 bool CUtil::ExcludeFileOrFolder(const std::string
& strFileOrFolder
, const std::vector
<std::string
>& regexps
)
527 if (strFileOrFolder
.empty())
530 CRegExp
regExExcludes(true, CRegExp::autoUtf8
); // case insensitive regex
532 for (const auto ®exp
: regexps
)
534 if (!regExExcludes
.RegComp(regexp
.c_str()))
535 { // invalid regexp - complain in logs
536 CLog::Log(LOGERROR
, "{}: Invalid exclude RegExp:'{}'", __FUNCTION__
, regexp
);
539 if (regExExcludes
.RegFind(strFileOrFolder
) > -1)
541 CLog::LogF(LOGDEBUG
, "File '{}' excluded. (Matches exclude rule RegExp: '{}')", CURL::GetRedacted(strFileOrFolder
), regexp
);
548 void CUtil::GetFileAndProtocol(const std::string
& strURL
, std::string
& strDir
)
551 if (!URIUtils::IsRemote(strURL
)) return ;
552 if (URIUtils::IsDVD(strURL
)) return ;
555 strDir
= StringUtils::Format("{}://{}", url
.GetProtocol(), url
.GetFileName());
558 int CUtil::GetDVDIfoTitle(const std::string
& strFile
)
560 std::string strFilename
= URIUtils::GetFileName(strFile
);
561 if (StringUtils::EqualsNoCase(strFilename
, "video_ts.ifo")) return 0;
563 return atoi(strFilename
.substr(4, 2).c_str());
566 std::string
CUtil::GetFileDigest(const std::string
& strPath
, KODI::UTILITY::CDigest::Type type
)
570 if (file
.Open(strPath
))
572 CDigest digest
{type
};
576 ssize_t read
= file
.Read(temp
,1024);
579 digest
.Update(temp
,read
);
581 result
= digest
.Finalize();
588 bool CUtil::GetDirectoryName(const std::string
& strFileName
, std::string
& strDescription
)
590 std::string strFName
= URIUtils::GetFileName(strFileName
);
591 strDescription
= URIUtils::GetDirectory(strFileName
);
592 URIUtils::RemoveSlashAtEnd(strDescription
);
594 size_t iPos
= strDescription
.find_last_of("/\\");
595 if (iPos
!= std::string::npos
)
596 strDescription
= strDescription
.substr(iPos
+ 1);
597 else if (strDescription
.size() <= 0)
598 strDescription
= strFName
;
602 void CUtil::GetDVDDriveIcon(const std::string
& strPath
, std::string
& strIcon
)
604 if (!CServiceBroker::GetMediaManager().IsDiscInDrive(strPath
))
606 strIcon
= "DefaultDVDEmpty.png";
610 CFileItem item
= CFileItem(strPath
, false);
614 strIcon
= "DefaultBluray.png";
618 if ( URIUtils::IsDVD(strPath
) )
620 strIcon
= "DefaultDVDFull.png";
624 if ( URIUtils::IsISO9660(strPath
) )
627 CCdInfo
* pInfo
= CServiceBroker::GetMediaManager().GetCdInfo();
628 if ( pInfo
!= NULL
&& pInfo
->IsVideoCd( 1 ) )
630 strIcon
= "DefaultVCD.png";
634 strIcon
= "DefaultDVDRom.png";
638 if ( URIUtils::IsCDDA(strPath
) )
640 strIcon
= "DefaultCDDA.png";
645 void CUtil::RemoveTempFiles()
647 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
649 std::string searchPath
= profileManager
->GetDatabaseFolder();
651 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".tmp", DIR_FLAG_NO_FILE_DIRS
))
654 for (const auto &item
: items
)
656 if (item
->m_bIsFolder
)
658 XFILE::CFile::Delete(item
->GetPath());
662 void CUtil::ClearSubtitles()
666 CDirectory::GetDirectory("special://temp/",items
, "", DIR_FLAG_DEFAULTS
);
667 for (const auto &item
: items
)
669 if (!item
->m_bIsFolder
)
671 if (item
->GetPath().find("subtitle") != std::string::npos
||
672 item
->GetPath().find("vobsub_queue") != std::string::npos
)
674 CLog::Log(LOGDEBUG
, "{} - Deleting temporary subtitle {}", __FUNCTION__
, item
->GetPath());
675 CFile::Delete(item
->GetPath());
681 int64_t CUtil::ToInt64(uint32_t high
, uint32_t low
)
691 \brief Finds next unused filename that matches padded int format identifier provided
692 \param[in] fn_template filename template consisting of a padded int format identifier (eg screenshot%03d)
693 \param[in] max maximum number to search for available name
694 \return "" on failure, string next available name matching format identifier on success
697 std::string
CUtil::GetNextFilename(const std::string
&fn_template
, int max
)
699 std::string searchPath
= URIUtils::GetDirectory(fn_template
);
700 std::string mask
= URIUtils::GetExtension(fn_template
);
701 std::string name
= StringUtils::Format(fn_template
, 0);
704 if (!CDirectory::GetDirectory(searchPath
, items
, mask
, DIR_FLAG_NO_FILE_DIRS
))
707 items
.SetFastLookup(true);
708 for (int i
= 0; i
<= max
; i
++)
710 std::string name
= StringUtils::Format(fn_template
, i
);
711 if (!items
.Get(name
))
717 std::string
CUtil::GetNextPathname(const std::string
&path_template
, int max
)
719 if (path_template
.find("%04d") == std::string::npos
)
722 for (int i
= 0; i
<= max
; i
++)
724 std::string name
= StringUtils::Format(path_template
, i
);
725 if (!CFile::Exists(name
) && !CDirectory::Exists(name
))
731 void CUtil::StatToStatI64(struct _stati64
*result
, struct stat
*stat
)
733 result
->st_dev
= stat
->st_dev
;
734 result
->st_ino
= stat
->st_ino
;
735 result
->st_mode
= stat
->st_mode
;
736 result
->st_nlink
= stat
->st_nlink
;
737 result
->st_uid
= stat
->st_uid
;
738 result
->st_gid
= stat
->st_gid
;
739 result
->st_rdev
= stat
->st_rdev
;
740 result
->st_size
= (int64_t)stat
->st_size
;
743 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
744 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
745 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
747 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
748 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
749 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
753 void CUtil::Stat64ToStatI64(struct _stati64
*result
, struct __stat64
*stat
)
755 result
->st_dev
= stat
->st_dev
;
756 result
->st_ino
= stat
->st_ino
;
757 result
->st_mode
= stat
->st_mode
;
758 result
->st_nlink
= stat
->st_nlink
;
759 result
->st_uid
= stat
->st_uid
;
760 result
->st_gid
= stat
->st_gid
;
761 result
->st_rdev
= stat
->st_rdev
;
762 result
->st_size
= stat
->st_size
;
764 result
->st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
765 result
->st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
766 result
->st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
768 result
->_st_atime
= (long)(stat
->st_atime
& 0xFFFFFFFF);
769 result
->_st_mtime
= (long)(stat
->st_mtime
& 0xFFFFFFFF);
770 result
->_st_ctime
= (long)(stat
->st_ctime
& 0xFFFFFFFF);
774 void CUtil::StatI64ToStat64(struct __stat64
*result
, struct _stati64
*stat
)
776 result
->st_dev
= stat
->st_dev
;
777 result
->st_ino
= stat
->st_ino
;
778 result
->st_mode
= stat
->st_mode
;
779 result
->st_nlink
= stat
->st_nlink
;
780 result
->st_uid
= stat
->st_uid
;
781 result
->st_gid
= stat
->st_gid
;
782 result
->st_rdev
= stat
->st_rdev
;
783 result
->st_size
= stat
->st_size
;
785 result
->st_atime
= stat
->st_atime
;
786 result
->st_mtime
= stat
->st_mtime
;
787 result
->st_ctime
= stat
->st_ctime
;
789 result
->st_atime
= stat
->_st_atime
;
790 result
->st_mtime
= stat
->_st_mtime
;
791 result
->st_ctime
= stat
->_st_ctime
;
795 void CUtil::StatToStat64(struct __stat64
*result
, const struct stat
*stat
)
797 memset(result
, 0, sizeof(*result
));
798 result
->st_dev
= stat
->st_dev
;
799 result
->st_ino
= stat
->st_ino
;
800 result
->st_mode
= stat
->st_mode
;
801 result
->st_nlink
= stat
->st_nlink
;
802 result
->st_uid
= stat
->st_uid
;
803 result
->st_gid
= stat
->st_gid
;
804 result
->st_rdev
= stat
->st_rdev
;
805 result
->st_size
= stat
->st_size
;
806 result
->st_atime
= stat
->st_atime
;
807 result
->st_mtime
= stat
->st_mtime
;
808 result
->st_ctime
= stat
->st_ctime
;
811 void CUtil::Stat64ToStat(struct stat
*result
, struct __stat64
*stat
)
813 result
->st_dev
= stat
->st_dev
;
814 result
->st_ino
= stat
->st_ino
;
815 result
->st_mode
= stat
->st_mode
;
816 result
->st_nlink
= stat
->st_nlink
;
817 result
->st_uid
= stat
->st_uid
;
818 result
->st_gid
= stat
->st_gid
;
819 result
->st_rdev
= stat
->st_rdev
;
821 if (stat
->st_size
<= LONG_MAX
)
822 result
->st_size
= (_off_t
)stat
->st_size
;
824 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
825 result
->st_size
= stat
->st_size
;
830 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
832 result
->st_atime
= (time_t)(stat
->st_atime
& 0xFFFFFFFF);
833 result
->st_mtime
= (time_t)(stat
->st_mtime
& 0xFFFFFFFF);
834 result
->st_ctime
= (time_t)(stat
->st_ctime
& 0xFFFFFFFF);
837 #ifdef TARGET_WINDOWS
838 void CUtil::Stat64ToStat64i32(struct _stat64i32
*result
, struct __stat64
*stat
)
840 result
->st_dev
= stat
->st_dev
;
841 result
->st_ino
= stat
->st_ino
;
842 result
->st_mode
= stat
->st_mode
;
843 result
->st_nlink
= stat
->st_nlink
;
844 result
->st_uid
= stat
->st_uid
;
845 result
->st_gid
= stat
->st_gid
;
846 result
->st_rdev
= stat
->st_rdev
;
848 if (stat
->st_size
<= LONG_MAX
)
849 result
->st_size
= (_off_t
)stat
->st_size
;
851 if (sizeof(stat
->st_size
) <= sizeof(result
->st_size
) )
852 result
->st_size
= stat
->st_size
;
857 CLog::Log(LOGWARNING
, "WARNING: File is larger than 32bit stat can handle, file size will be reported as 0 bytes");
860 result
->st_atime
= stat
->st_atime
;
861 result
->st_mtime
= stat
->st_mtime
;
862 result
->st_ctime
= stat
->st_ctime
;
864 result
->st_atime
= stat
->_st_atime
;
865 result
->st_mtime
= stat
->_st_mtime
;
866 result
->st_ctime
= stat
->_st_ctime
;
871 bool CUtil::CreateDirectoryEx(const std::string
& strPath
)
873 // Function to create all directories at once instead
874 // of calling CreateDirectory for every subdir.
875 // Creates the directory and subdirectories if needed.
877 // return true if directory already exist
878 if (CDirectory::Exists(strPath
)) return true;
880 // we currently only allow HD and smb and nfs paths
881 if (!URIUtils::IsHD(strPath
) && !URIUtils::IsSmb(strPath
) && !URIUtils::IsNfs(strPath
))
883 CLog::Log(LOGERROR
, "{} called with an unsupported path: {}", __FUNCTION__
, strPath
);
887 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPath
);
890 std::string
dir(dirs
.front());
891 URIUtils::AddSlashAtEnd(dir
);
892 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
894 dir
= URIUtils::AddFileToFolder(dir
, *it
);
895 CDirectory::Create(dir
);
898 // was the final destination directory successfully created ?
899 return CDirectory::Exists(strPath
);
902 std::string
CUtil::MakeLegalFileName(const std::string
&strFile
, int LegalType
)
904 std::string result
= strFile
;
906 StringUtils::Replace(result
, '/', '_');
907 StringUtils::Replace(result
, '\\', '_');
908 StringUtils::Replace(result
, '?', '_');
910 if (LegalType
== LEGAL_WIN32_COMPAT
)
912 // just filter out some illegal characters on windows
913 StringUtils::Replace(result
, ':', '_');
914 StringUtils::Replace(result
, '*', '_');
915 StringUtils::Replace(result
, '?', '_');
916 StringUtils::Replace(result
, '\"', '_');
917 StringUtils::Replace(result
, '<', '_');
918 StringUtils::Replace(result
, '>', '_');
919 StringUtils::Replace(result
, '|', '_');
920 StringUtils::TrimRight(result
, ". ");
925 // legalize entire path
926 std::string
CUtil::MakeLegalPath(const std::string
&strPathAndFile
, int LegalType
)
928 if (URIUtils::IsStack(strPathAndFile
))
929 return MakeLegalPath(CStackDirectory::GetFirstStackedFile(strPathAndFile
));
930 if (URIUtils::IsMultiPath(strPathAndFile
))
931 return MakeLegalPath(CMultiPathDirectory::GetFirstPath(strPathAndFile
));
932 if (!URIUtils::IsHD(strPathAndFile
) && !URIUtils::IsSmb(strPathAndFile
) && !URIUtils::IsNfs(strPathAndFile
))
933 return strPathAndFile
; // we don't support writing anywhere except HD, SMB and NFS - no need to legalize path
935 bool trailingSlash
= URIUtils::HasSlashAtEnd(strPathAndFile
);
936 std::vector
<std::string
> dirs
= URIUtils::SplitPath(strPathAndFile
);
938 return strPathAndFile
;
939 // we just add first token to path and don't legalize it - possible values:
940 // "X:" (local win32), "" (local unix - empty string before '/') or
941 // "protocol://domain"
942 std::string
dir(dirs
.front());
943 URIUtils::AddSlashAtEnd(dir
);
944 for (std::vector
<std::string
>::const_iterator it
= dirs
.begin() + 1; it
!= dirs
.end(); ++it
)
945 dir
= URIUtils::AddFileToFolder(dir
, MakeLegalFileName(*it
, LegalType
));
946 if (trailingSlash
) URIUtils::AddSlashAtEnd(dir
);
950 std::string
CUtil::ValidatePath(const std::string
&path
, bool bFixDoubleSlashes
/* = false */)
952 std::string result
= path
;
954 // Don't do any stuff on URLs containing %-characters or protocols that embed
955 // filenames. NOTE: Don't use IsInZip or IsInRar here since it will infinitely
956 // recurse and crash XBMC
957 if (URIUtils::IsURL(path
) &&
958 (path
.find('%') != std::string::npos
||
959 StringUtils::StartsWithNoCase(path
, "apk:") ||
960 StringUtils::StartsWithNoCase(path
, "zip:") ||
961 StringUtils::StartsWithNoCase(path
, "rar:") ||
962 StringUtils::StartsWithNoCase(path
, "stack:") ||
963 StringUtils::StartsWithNoCase(path
, "bluray:") ||
964 StringUtils::StartsWithNoCase(path
, "multipath:") ))
967 // check the path for incorrect slashes
968 #ifdef TARGET_WINDOWS
969 if (URIUtils::IsDOSPath(path
))
971 StringUtils::Replace(result
, '/', '\\');
972 /* The double slash correction should only be used when *absolutely*
973 necessary! This applies to certain DLLs or use from Python DLLs/scripts
974 that incorrectly generate double (back) slashes.
976 if (bFixDoubleSlashes
&& !result
.empty())
978 // Fixup for double back slashes (but ignore the \\ of unc-paths)
979 for (size_t x
= 1; x
< result
.size() - 1; x
++)
981 if (result
[x
] == '\\' && result
[x
+1] == '\\')
986 else if (path
.find("://") != std::string::npos
|| path
.find(":\\\\") != std::string::npos
)
989 StringUtils::Replace(result
, '\\', '/');
990 /* The double slash correction should only be used when *absolutely*
991 necessary! This applies to certain DLLs or use from Python DLLs/scripts
992 that incorrectly generate double (back) slashes.
994 if (bFixDoubleSlashes
&& !result
.empty())
996 // Fixup for double forward slashes(/) but don't touch the :// of URLs
997 for (size_t x
= 2; x
< result
.size() - 1; x
++)
999 if ( result
[x
] == '/' && result
[x
+ 1] == '/' && !(result
[x
- 1] == ':' || (result
[x
- 1] == '/' && result
[x
- 2] == ':')) )
1007 void CUtil::SplitParams(const std::string
¶mString
, std::vector
<std::string
> ¶meters
)
1009 bool inQuotes
= false;
1010 bool lastEscaped
= false; // only every second character can be escaped
1012 size_t whiteSpacePos
= 0;
1013 std::string parameter
;
1015 for (size_t pos
= 0; pos
< paramString
.size(); pos
++)
1017 char ch
= paramString
[pos
];
1018 bool escaped
= (pos
> 0 && paramString
[pos
- 1] == '\\' && !lastEscaped
);
1019 lastEscaped
= escaped
;
1021 { // if we're in a quote, we accept everything until the closing quote
1022 if (ch
== '"' && !escaped
)
1023 { // finished a quote - no need to add the end quote to our string
1028 { // not in a quote, so check if we should be starting one
1029 if (ch
== '"' && !escaped
)
1030 { // start of quote - no need to add the quote to our string
1033 if (inFunction
&& ch
== ')')
1034 { // end of a function
1038 { // start of function
1041 if (!inFunction
&& ch
== ',')
1042 { // not in a function, so a comma signifies the end of this parameter
1044 parameter
= parameter
.substr(0, whiteSpacePos
);
1045 // trim off start and end quotes
1046 if (parameter
.length() > 1 && parameter
[0] == '"' && parameter
[parameter
.length() - 1] == '"')
1047 parameter
= parameter
.substr(1, parameter
.length() - 2);
1048 else if (parameter
.length() > 3 && parameter
[parameter
.length() - 1] == '"')
1050 // check name="value" style param.
1051 size_t quotaPos
= parameter
.find('"');
1052 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1054 parameter
.erase(parameter
.length() - 1);
1055 parameter
.erase(quotaPos
);
1058 parameters
.push_back(parameter
);
1064 if ((ch
== '"' || ch
== '\\') && escaped
)
1065 { // escaped quote or backslash
1066 parameter
[parameter
.size()-1] = ch
;
1069 // whitespace handling - we skip any whitespace at the left or right of an unquoted parameter
1070 if (ch
== ' ' && !inQuotes
)
1072 if (parameter
.empty()) // skip whitespace on left
1074 if (!whiteSpacePos
) // make a note of where whitespace starts on the right
1075 whiteSpacePos
= parameter
.size();
1081 if (inFunction
|| inQuotes
)
1082 CLog::Log(LOGWARNING
, "{}({}) - end of string while searching for ) or \"", __FUNCTION__
,
1085 parameter
.erase(whiteSpacePos
);
1086 // trim off start and end quotes
1087 if (parameter
.size() > 1 && parameter
[0] == '"' && parameter
[parameter
.size() - 1] == '"')
1088 parameter
= parameter
.substr(1,parameter
.size() - 2);
1089 else if (parameter
.size() > 3 && parameter
[parameter
.size() - 1] == '"')
1091 // check name="value" style param.
1092 size_t quotaPos
= parameter
.find('"');
1093 if (quotaPos
> 1 && quotaPos
< parameter
.length() - 1 && parameter
[quotaPos
- 1] == '=')
1095 parameter
.erase(parameter
.length() - 1);
1096 parameter
.erase(quotaPos
);
1099 if (!parameter
.empty() || parameters
.size())
1100 parameters
.push_back(parameter
);
1103 int CUtil::GetMatchingSource(const std::string
& strPath1
, VECSOURCES
& VECSOURCES
, bool& bIsSourceName
)
1105 if (strPath1
.empty())
1108 // copy as we may change strPath
1109 std::string strPath
= strPath1
;
1111 // Check for special protocols
1112 CURL
checkURL(strPath
);
1114 if (StringUtils::StartsWith(strPath
, "special://skin/"))
1117 // do not return early if URL protocol is "plugin"
1118 // since video- and/or audio-plugins can be configured as mediasource
1121 if (checkURL
.IsProtocol("stack"))
1122 strPath
.erase(0, 8); // remove the stack protocol
1124 if (checkURL
.IsProtocol("shout"))
1125 strPath
= checkURL
.GetHostName();
1127 if (checkURL
.IsProtocol("multipath"))
1128 strPath
= CMultiPathDirectory::GetFirstPath(strPath
);
1130 bIsSourceName
= false;
1133 // we first test the NAME of a source
1134 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1136 const CMediaSource
&share
= VECSOURCES
[i
];
1137 std::string strName
= share
.strName
;
1139 // special cases for dvds
1140 if (URIUtils::IsOnDVD(share
.strPath
))
1142 if (URIUtils::IsOnDVD(strPath
))
1145 // not a path, so we need to modify the source name
1146 // since we add the drive status and disc name to the source
1147 // "Name (Drive Status/Disc Name)"
1148 size_t iPos
= strName
.rfind('(');
1149 if (iPos
!= std::string::npos
&& iPos
> 1)
1150 strName
= strName
.substr(0, iPos
- 1);
1152 if (StringUtils::EqualsNoCase(strPath
, strName
))
1154 bIsSourceName
= true;
1159 // now test the paths
1161 // remove user details, and ensure path only uses forward slashes
1162 // and ends with a trailing slash so as not to match a substring
1163 CURL
urlDest(strPath
);
1164 urlDest
.SetOptions("");
1165 urlDest
.SetProtocolOptions("");
1166 std::string strDest
= urlDest
.GetWithoutUserDetails();
1167 ForceForwardSlashes(strDest
);
1168 if (!URIUtils::HasSlashAtEnd(strDest
))
1172 size_t iLenPath
= strDest
.size();
1173 for (int i
= 0; i
< (int)VECSOURCES
.size(); ++i
)
1175 const CMediaSource
&share
= VECSOURCES
[i
];
1177 // does it match a source name?
1178 if (share
.strPath
.substr(0,8) == "shout://")
1180 CURL
url(share
.strPath
);
1181 if (strPath
== url
.GetHostName())
1185 // doesn't match a name, so try the source path
1186 std::vector
<std::string
> vecPaths
;
1188 // add any concatenated paths if they exist
1189 if (!share
.vecPaths
.empty())
1190 vecPaths
= share
.vecPaths
;
1192 // add the actual share path at the front of the vector
1193 vecPaths
.insert(vecPaths
.begin(), share
.strPath
);
1196 for (const auto &path
: vecPaths
)
1198 // remove user details, and ensure path only uses forward slashes
1199 // and ends with a trailing slash so as not to match a substring
1200 CURL
urlShare(path
);
1201 urlShare
.SetOptions("");
1202 urlShare
.SetProtocolOptions("");
1203 std::string strShare
= urlShare
.GetWithoutUserDetails();
1204 ForceForwardSlashes(strShare
);
1205 if (!URIUtils::HasSlashAtEnd(strShare
))
1207 size_t iLenShare
= strShare
.size();
1209 if ((iLenPath
>= iLenShare
) && StringUtils::StartsWithNoCase(strDest
, strShare
) && (iLenShare
> iLength
))
1211 // if exact match, return it immediately
1212 if (iLenPath
== iLenShare
)
1214 // if the path EXACTLY matches an item in a concatenated path
1215 // set source name to true to load the full virtualpath
1216 bIsSourceName
= false;
1217 if (vecPaths
.size() > 1)
1218 bIsSourceName
= true;
1222 iLength
= iLenShare
;
1227 // return the index of the share with the longest match
1231 // rar:// and zip://
1232 // if archive wasn't mounted, look for a matching share for the archive instead
1233 if( StringUtils::StartsWithNoCase(strPath
, "rar://") || StringUtils::StartsWithNoCase(strPath
, "zip://") )
1235 // get the hostname portion of the url since it contains the archive file
1236 strPath
= checkURL
.GetHostName();
1238 bIsSourceName
= false;
1240 return GetMatchingSource(strPath
, VECSOURCES
, bDummy
);
1243 CLog::Log(LOGDEBUG
, "CUtil::GetMatchingSource: no matching source found for [{}]", strPath1
);
1248 std::string
CUtil::TranslateSpecialSource(const std::string
&strSpecial
)
1250 if (!strSpecial
.empty() && strSpecial
[0] == '$')
1252 if (StringUtils::StartsWithNoCase(strSpecial
, "$home"))
1253 return URIUtils::AddFileToFolder("special://home/", strSpecial
.substr(5));
1254 else if (StringUtils::StartsWithNoCase(strSpecial
, "$subtitles"))
1255 return URIUtils::AddFileToFolder("special://subtitles/", strSpecial
.substr(10));
1256 else if (StringUtils::StartsWithNoCase(strSpecial
, "$userdata"))
1257 return URIUtils::AddFileToFolder("special://userdata/", strSpecial
.substr(9));
1258 else if (StringUtils::StartsWithNoCase(strSpecial
, "$database"))
1259 return URIUtils::AddFileToFolder("special://database/", strSpecial
.substr(9));
1260 else if (StringUtils::StartsWithNoCase(strSpecial
, "$thumbnails"))
1261 return URIUtils::AddFileToFolder("special://thumbnails/", strSpecial
.substr(11));
1262 else if (StringUtils::StartsWithNoCase(strSpecial
, "$recordings"))
1263 return URIUtils::AddFileToFolder("special://recordings/", strSpecial
.substr(11));
1264 else if (StringUtils::StartsWithNoCase(strSpecial
, "$screenshots"))
1265 return URIUtils::AddFileToFolder("special://screenshots/", strSpecial
.substr(12));
1266 else if (StringUtils::StartsWithNoCase(strSpecial
, "$musicplaylists"))
1267 return URIUtils::AddFileToFolder("special://musicplaylists/", strSpecial
.substr(15));
1268 else if (StringUtils::StartsWithNoCase(strSpecial
, "$videoplaylists"))
1269 return URIUtils::AddFileToFolder("special://videoplaylists/", strSpecial
.substr(15));
1270 else if (StringUtils::StartsWithNoCase(strSpecial
, "$cdrips"))
1271 return URIUtils::AddFileToFolder("special://cdrips/", strSpecial
.substr(7));
1272 // this one will be removed post 2.0
1273 else if (StringUtils::StartsWithNoCase(strSpecial
, "$playlists"))
1274 return URIUtils::AddFileToFolder(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
), strSpecial
.substr(10));
1279 std::string
CUtil::MusicPlaylistsLocation()
1281 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1282 std::vector
<std::string
> vec
;
1283 vec
.push_back(URIUtils::AddFileToFolder(path
, "music"));
1284 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1285 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1288 std::string
CUtil::VideoPlaylistsLocation()
1290 const std::string path
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
1291 std::vector
<std::string
> vec
;
1292 vec
.push_back(URIUtils::AddFileToFolder(path
, "video"));
1293 vec
.push_back(URIUtils::AddFileToFolder(path
, "mixed"));
1294 return XFILE::CMultiPathDirectory::ConstructMultiPath(vec
);
1297 void CUtil::DeleteMusicDatabaseDirectoryCache()
1299 CUtil::DeleteDirectoryCache("mdb-");
1300 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete video smartplaylists, but as we can't differentiate based on URL...
1303 void CUtil::DeleteVideoDatabaseDirectoryCache()
1305 CUtil::DeleteDirectoryCache("vdb-");
1306 CUtil::DeleteDirectoryCache("sp-"); // overkill as it will delete music smartplaylists, but as we can't differentiate based on URL...
1309 void CUtil::DeleteDirectoryCache(const std::string
&prefix
)
1311 std::string searchPath
= "special://temp/";
1312 CFileItemList items
;
1313 if (!XFILE::CDirectory::GetDirectory(searchPath
, items
, ".fi", DIR_FLAG_NO_FILE_DIRS
))
1316 for (const auto &item
: items
)
1318 if (item
->m_bIsFolder
)
1320 std::string fileName
= URIUtils::GetFileName(item
->GetPath());
1321 if (StringUtils::StartsWith(fileName
, prefix
))
1322 XFILE::CFile::Delete(item
->GetPath());
1327 void CUtil::GetRecursiveListing(const std::string
& strPath
, CFileItemList
& items
, const std::string
& strMask
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1329 CFileItemList myItems
;
1330 CDirectory::GetDirectory(strPath
,myItems
,strMask
,flags
);
1331 for (const auto &item
: myItems
)
1333 if (item
->m_bIsFolder
)
1334 CUtil::GetRecursiveListing(item
->GetPath(),items
,strMask
,flags
);
1340 void CUtil::GetRecursiveDirsListing(const std::string
& strPath
, CFileItemList
& item
, unsigned int flags
/* = DIR_FLAG_DEFAULTS */)
1342 CFileItemList myItems
;
1343 CDirectory::GetDirectory(strPath
,myItems
,"",flags
);
1344 for (const auto &i
: myItems
)
1346 if (i
->m_bIsFolder
&& !i
->IsPath(".."))
1349 CUtil::GetRecursiveDirsListing(i
->GetPath(),item
,flags
);
1354 void CUtil::ForceForwardSlashes(std::string
& strPath
)
1356 size_t iPos
= strPath
.rfind('\\');
1357 while (iPos
!= std::string::npos
)
1359 strPath
.at(iPos
) = '/';
1360 iPos
= strPath
.rfind('\\');
1364 double CUtil::AlbumRelevance(const std::string
& strAlbumTemp1
, const std::string
& strAlbum1
, const std::string
& strArtistTemp1
, const std::string
& strArtist1
)
1366 // case-insensitive fuzzy string comparison on the album and artist for relevance
1367 // weighting is identical, both album and artist are 50% of the total relevance
1368 // a missing artist means the maximum relevance can only be 0.50
1369 std::string strAlbumTemp
= strAlbumTemp1
;
1370 StringUtils::ToLower(strAlbumTemp
);
1371 std::string strAlbum
= strAlbum1
;
1372 StringUtils::ToLower(strAlbum
);
1373 double fAlbumPercentage
= fstrcmp(strAlbumTemp
.c_str(), strAlbum
.c_str());
1374 double fArtistPercentage
= 0.0;
1375 if (!strArtist1
.empty())
1377 std::string strArtistTemp
= strArtistTemp1
;
1378 StringUtils::ToLower(strArtistTemp
);
1379 std::string strArtist
= strArtist1
;
1380 StringUtils::ToLower(strArtist
);
1381 fArtistPercentage
= fstrcmp(strArtistTemp
.c_str(), strArtist
.c_str());
1383 double fRelevance
= fAlbumPercentage
* 0.5 + fArtistPercentage
* 0.5;
1387 bool CUtil::MakeShortenPath(std::string StrInput
, std::string
& StrOutput
, size_t iTextMaxLength
)
1389 size_t iStrInputSize
= StrInput
.size();
1390 if(iStrInputSize
<= 0 || iTextMaxLength
>= iStrInputSize
)
1392 StrOutput
= StrInput
;
1397 size_t nGreaterDelim
, nPos
;
1399 nPos
= StrInput
.find_last_of( '\\' );
1400 if (nPos
!= std::string::npos
)
1404 nPos
= StrInput
.find_last_of( '/' );
1405 if (nPos
!= std::string::npos
)
1408 if ( cDelim
== '\0' )
1411 if (nPos
== StrInput
.size() - 1)
1413 StrInput
.erase(StrInput
.size() - 1);
1414 nPos
= StrInput
.find_last_of(cDelim
);
1416 while( iTextMaxLength
< iStrInputSize
)
1418 nPos
= StrInput
.find_last_of( cDelim
, nPos
);
1419 nGreaterDelim
= nPos
;
1421 if (nPos
== std::string::npos
|| nPos
== 0)
1424 nPos
= StrInput
.find_last_of( cDelim
, nPos
- 1 );
1426 if ( nPos
== std::string::npos
)
1428 if ( nGreaterDelim
> nPos
)
1429 StrInput
.replace( nPos
+ 1, nGreaterDelim
- nPos
- 1, ".." );
1430 iStrInputSize
= StrInput
.size();
1432 // replace any additional /../../ with just /../ if necessary
1433 std::string replaceDots
= StringUtils::Format("..{}..", cDelim
);
1434 while (StrInput
.size() > (unsigned int)iTextMaxLength
)
1435 if (!StringUtils::Replace(StrInput
, replaceDots
, ".."))
1437 // finally, truncate our string to force inside our max text length,
1438 // replacing the last 2 characters with ".."
1441 // "smb://../Playboy Swimsuit Cal.."
1442 if (iTextMaxLength
> 2 && StrInput
.size() > (unsigned int)iTextMaxLength
)
1444 StrInput
.erase(iTextMaxLength
- 2);
1447 StrOutput
= StrInput
;
1451 bool CUtil::SupportsWriteFileOperations(const std::string
& strPath
)
1453 // currently only hd, smb, nfs and dav support delete and rename
1454 if (URIUtils::IsHD(strPath
))
1456 if (URIUtils::IsSmb(strPath
))
1458 if (URIUtils::IsPVRRecording(strPath
))
1459 return CPVRDirectory::SupportsWriteFileOperations(strPath
);
1460 if (URIUtils::IsNfs(strPath
))
1462 if (URIUtils::IsDAV(strPath
))
1464 if (URIUtils::IsStack(strPath
))
1465 return SupportsWriteFileOperations(CStackDirectory::GetFirstStackedFile(strPath
));
1466 if (URIUtils::IsMultiPath(strPath
))
1467 return CMultiPathDirectory::SupportsWriteFileOperations(strPath
);
1469 if (CServiceBroker::IsAddonInterfaceUp())
1472 for (const auto& addon
: CServiceBroker::GetVFSAddonCache().GetAddonInstances())
1474 const auto& info
= addon
->GetProtocolInfo();
1475 auto prots
= StringUtils::Split(info
.type
, "|");
1476 if (info
.supportWrite
&&
1477 std::find(prots
.begin(), prots
.end(), url
.GetProtocol()) != prots
.end())
1485 bool CUtil::SupportsReadFileOperations(const std::string
& strPath
)
1487 return !URIUtils::IsVideoDb(strPath
);
1490 std::string
CUtil::GetDefaultFolderThumb(const std::string
&folderThumb
)
1492 if (CServiceBroker::GetGUI()->GetTextureManager().HasTexture(folderThumb
))
1497 void CUtil::GetSkinThemes(std::vector
<std::string
>& vecTheme
)
1499 static const std::string TexturesXbt
= "Textures.xbt";
1501 std::string strPath
= URIUtils::AddFileToFolder(CServiceBroker::GetWinSystem()->GetGfxContext().GetMediaDir(), "media");
1502 CFileItemList items
;
1503 CDirectory::GetDirectory(strPath
, items
, "", DIR_FLAG_DEFAULTS
);
1504 // Search for Themes in the Current skin!
1505 for (const auto &pItem
: items
)
1507 if (!pItem
->m_bIsFolder
)
1509 std::string strExtension
= URIUtils::GetExtension(pItem
->GetPath());
1510 std::string strLabel
= pItem
->GetLabel();
1511 if ((strExtension
== ".xbt" && !StringUtils::EqualsNoCase(strLabel
, TexturesXbt
)))
1512 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - strExtension
.size()));
1516 // check if this is an xbt:// VFS path
1517 CURL
itemUrl(pItem
->GetPath());
1518 if (!itemUrl
.IsProtocol("xbt") || !itemUrl
.GetFileName().empty())
1521 std::string strLabel
= URIUtils::GetFileName(itemUrl
.GetHostName());
1522 if (!StringUtils::EqualsNoCase(strLabel
, TexturesXbt
))
1523 vecTheme
.push_back(StringUtils::Left(strLabel
, strLabel
.size() - URIUtils::GetExtension(strLabel
).size()));
1526 std::sort(vecTheme
.begin(), vecTheme
.end(), sortstringbyname());
1529 void CUtil::InitRandomSeed()
1532 auto now
= std::chrono::steady_clock::now();
1533 auto seed
= now
.time_since_epoch();
1535 srand(static_cast<unsigned int>(seed
.count()));
1538 #if defined(TARGET_POSIX) && !defined(TARGET_DARWIN_TVOS)
1539 bool CUtil::RunCommandLine(const std::string
& cmdLine
, bool waitExit
)
1541 std::vector
<std::string
> args
= StringUtils::Split(cmdLine
, ",");
1543 // Strip quotes and whitespace around the arguments, or exec will fail.
1544 // This allows the python invocation to be written more naturally with any amount of whitespace around the args.
1545 // But it's still limited, for example quotes inside the strings are not expanded, etc.
1546 //! @todo Maybe some python library routine can parse this more properly ?
1547 for (std::vector
<std::string
>::iterator it
= args
.begin(); it
!= args
.end(); ++it
)
1550 pos
= it
->find_first_not_of(" \t\n\"'");
1551 if (pos
!= std::string::npos
)
1556 pos
= it
->find_last_not_of(" \t\n\"'"); // if it returns npos we'll end up with an empty string which is OK
1558 it
->erase(++pos
, it
->size());
1562 return Command(args
, waitExit
);
1565 bool CUtil::Command(const std::vector
<std::string
>& arrArgs
, bool waitExit
)
1568 printf("Executing: ");
1569 for (const auto &arg
: arrArgs
)
1570 printf("%s ", arg
.c_str());
1574 pid_t child
= fork();
1580 // fork again in order not to leave a zombie process
1584 else if (child
!= 0)
1590 if (!arrArgs
.empty())
1592 char **args
= (char **)alloca(sizeof(char *) * (arrArgs
.size() + 3));
1593 memset(args
, 0, (sizeof(char *) * (arrArgs
.size() + 3)));
1594 for (size_t i
=0; i
<arrArgs
.size(); i
++)
1595 args
[i
] = const_cast<char *>(arrArgs
[i
].c_str());
1596 execvp(args
[0], args
);
1601 waitpid(child
, &n
, 0);
1604 return (waitExit
) ? (WEXITSTATUS(n
) == 0) : true;
1608 int CUtil::LookupRomanDigit(char roman_digit
)
1610 switch (roman_digit
)
1638 int CUtil::TranslateRomanNumeral(const char* roman_numeral
)
1643 if (roman_numeral
&& roman_numeral
[0])
1650 while (*roman_numeral
)
1652 int digit
= CUtil::LookupRomanDigit(*roman_numeral
);
1655 // General sanity checks
1657 // numeral not in LUT
1664 // N = 10^n may not precede (N+1) > 10^(N+1)
1665 if (test
== 1 && digit
> last
* 10)
1668 // N = 5*10^n may not precede (N+1) >= N
1669 if (test
== 5 && digit
>= last
)
1672 // End general sanity checks
1676 // smaller numerals may not repeat before a larger one
1685 else if (last
== digit
)
1694 decimal
+= 2 * last
- temp_sum
;
1696 decimal
+= temp_sum
;
1703 // Post general sanity checks
1705 // numerals may not repeat more than thrice
1714 decimal
+= temp_sum
;
1716 decimal
+= 2 * last
- temp_sum
;
1721 std::string
CUtil::ResolveExecutablePath()
1723 std::string strExecutablePath
;
1724 #ifdef TARGET_WINDOWS
1725 static const size_t bufSize
= MAX_PATH
* 2;
1726 wchar_t* buf
= new wchar_t[bufSize
];
1728 ::GetModuleFileNameW(0, buf
, bufSize
);
1730 g_charsetConverter
.wToUTF8(buf
,strExecutablePath
);
1732 #elif defined(TARGET_DARWIN)
1733 char given_path
[2*MAXPATHLEN
];
1734 size_t path_size
=2*MAXPATHLEN
;
1736 CDarwinUtils::GetExecutablePath(given_path
, &path_size
);
1737 strExecutablePath
= given_path
;
1738 #elif defined(TARGET_FREEBSD)
1745 mib
[2] = KERN_PROC_PATHNAME
;
1748 buflen
= sizeof(buf
) - 1;
1749 if(sysctl(mib
, 4, buf
, &buflen
, NULL
, 0) < 0)
1750 strExecutablePath
= "";
1752 strExecutablePath
= buf
;
1753 #elif defined(TARGET_ANDROID)
1754 strExecutablePath
= CXBMCApp::getApplicationInfo().nativeLibraryDir
;
1756 std::string appName
= CCompileInfo::GetAppName();
1757 std::string libName
= "lib" + appName
+ ".so";
1758 StringUtils::ToLower(libName
);
1759 strExecutablePath
+= "/" + libName
;
1761 /* Get our PID and build the name of the link in /proc */
1762 pid_t pid
= getpid();
1763 char linkname
[64]; /* /proc/<pid>/exe */
1764 snprintf(linkname
, sizeof(linkname
), "/proc/%i/exe", pid
);
1766 /* Now read the symbolic link */
1767 char buf
[PATH_MAX
+ 1];
1770 int ret
= readlink(linkname
, buf
, sizeof(buf
) - 1);
1774 strExecutablePath
= buf
;
1776 return strExecutablePath
;
1779 std::string
CUtil::GetFrameworksPath(bool forPython
)
1781 std::string strFrameworksPath
;
1782 #if defined(TARGET_DARWIN)
1783 strFrameworksPath
= CDarwinUtils::GetFrameworkPath(forPython
);
1785 return strFrameworksPath
;
1788 void CUtil::GetVideoBasePathAndFileName(const std::string
& videoPath
, std::string
& basePath
, std::string
& videoFileName
)
1790 CFileItem
item(videoPath
, false);
1791 videoFileName
= URIUtils::ReplaceExtension(URIUtils::GetFileName(videoPath
), "");
1793 if (item
.HasVideoInfoTag())
1794 basePath
= item
.GetVideoInfoTag()->m_basePath
;
1796 if (basePath
.empty() && item
.IsOpticalMediaFile())
1797 basePath
= item
.GetLocalMetadataPath();
1799 CURL
url(videoPath
);
1800 if (basePath
.empty() && url
.IsProtocol("bluray"))
1802 basePath
= url
.GetHostName();
1803 videoFileName
= URIUtils::ReplaceExtension(GetTitleFromPath(url
.GetHostName()), "");
1805 url
= CURL(url
.GetHostName());
1806 if (url
.IsProtocol("udf"))
1807 basePath
= URIUtils::GetParentPath(url
.GetHostName());
1810 if (basePath
.empty())
1811 basePath
= URIUtils::GetBasePath(videoPath
);
1814 void CUtil::GetItemsToScan(const std::string
& videoPath
,
1815 const std::string
& item_exts
,
1816 const std::vector
<std::string
>& sub_dirs
,
1817 CFileItemList
& items
)
1819 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
1821 if (!videoPath
.empty())
1822 CDirectory::GetDirectory(videoPath
, items
, item_exts
, flags
);
1824 std::vector
<std::string
> additionalPaths
;
1825 for (const auto &item
: items
)
1827 for (const auto& subdir
: sub_dirs
)
1829 if (StringUtils::EqualsNoCase(item
->GetLabel(), subdir
))
1830 additionalPaths
.push_back(item
->GetPath());
1834 for (std::vector
<std::string
>::const_iterator it
= additionalPaths
.begin(); it
!= additionalPaths
.end(); ++it
)
1836 CFileItemList moreItems
;
1837 CDirectory::GetDirectory(*it
, moreItems
, item_exts
, flags
);
1838 items
.Append(moreItems
);
1843 void CUtil::ScanPathsForAssociatedItems(const std::string
& videoName
,
1844 const CFileItemList
& items
,
1845 const std::vector
<std::string
>& item_exts
,
1846 std::vector
<std::string
>& associatedFiles
)
1848 for (const auto &pItem
: items
)
1850 if (pItem
->m_bIsFolder
)
1853 std::string strCandidate
= URIUtils::GetFileName(pItem
->GetPath());
1856 if (std::find(associatedFiles
.begin(), associatedFiles
.end(), pItem
->GetPath()) != associatedFiles
.end())
1859 URIUtils::RemoveExtension(strCandidate
);
1860 // NOTE: We don't know if one of videoName or strCandidate is URL-encoded and the other is not, so try both
1861 if (StringUtils::StartsWithNoCase(strCandidate
, videoName
) || (StringUtils::StartsWithNoCase(strCandidate
, CURL::Decode(videoName
))))
1863 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1864 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), "", item_exts
, associatedFiles
);
1867 associatedFiles
.push_back(pItem
->GetPath());
1868 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
1869 CURL::GetRedacted(pItem
->GetPath()));
1874 if (URIUtils::IsRAR(pItem
->GetPath()) || URIUtils::IsZIP(pItem
->GetPath()))
1875 CUtil::ScanArchiveForAssociatedItems(pItem
->GetPath(), videoName
, item_exts
, associatedFiles
);
1880 int CUtil::ScanArchiveForAssociatedItems(const std::string
& strArchivePath
,
1881 const std::string
& videoNameNoExt
,
1882 const std::vector
<std::string
>& item_exts
,
1883 std::vector
<std::string
>& associatedFiles
)
1885 CLog::LogF(LOGDEBUG
, "Scanning archive {}", CURL::GetRedacted(strArchivePath
));
1886 int nItemsAdded
= 0;
1887 CFileItemList ItemList
;
1889 // zip only gets the root dir
1890 if (URIUtils::HasExtension(strArchivePath
, ".zip"))
1892 CURL
pathToUrl(strArchivePath
);
1893 CURL zipURL
= URIUtils::CreateArchivePath("zip", pathToUrl
, "");
1894 if (!CDirectory::GetDirectory(zipURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
1897 else if (URIUtils::HasExtension(strArchivePath
, ".rar"))
1899 CURL
pathToUrl(strArchivePath
);
1900 CURL rarURL
= URIUtils::CreateArchivePath("rar", pathToUrl
, "");
1901 if (!CDirectory::GetDirectory(rarURL
, ItemList
, "", DIR_FLAG_NO_FILE_DIRS
))
1904 for (const auto &item
: ItemList
)
1906 std::string strPathInRar
= item
->GetPath();
1907 std::string strExt
= URIUtils::GetExtension(strPathInRar
);
1909 // Check another archive in archive
1910 if (strExt
== ".zip" || strExt
== ".rar")
1913 ScanArchiveForAssociatedItems(strPathInRar
, videoNameNoExt
, item_exts
, associatedFiles
);
1917 // check that the found filename matches the movie filename
1918 size_t fnl
= videoNameNoExt
.size();
1919 // NOTE: We don't know if videoNameNoExt is URL-encoded, so try both
1921 !(StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), videoNameNoExt
) ||
1922 StringUtils::StartsWithNoCase(URIUtils::GetFileName(strPathInRar
), CURL::Decode(videoNameNoExt
))))
1925 for (const auto& ext
: item_exts
)
1927 if (StringUtils::EqualsNoCase(strExt
, ext
))
1929 CLog::Log(LOGINFO
, "{}: found associated file {}", __FUNCTION__
,
1930 CURL::GetRedacted(strPathInRar
));
1931 associatedFiles
.push_back(strPathInRar
);
1941 void CUtil::ScanForExternalSubtitles(const std::string
& strMovie
, std::vector
<std::string
>& vecSubtitles
)
1943 auto start
= std::chrono::steady_clock::now();
1945 CFileItem
item(strMovie
, false);
1946 if ((item
.IsInternetStream() && !URIUtils::IsOnLAN(item
.GetDynPath()))
1947 || item
.IsPlayList()
1952 CLog::Log(LOGDEBUG
, "{}: Searching for subtitles...", __FUNCTION__
);
1954 std::string strBasePath
;
1955 std::string strSubtitle
;
1957 GetVideoBasePathAndFileName(strMovie
, strBasePath
, strSubtitle
);
1959 CFileItemList items
;
1960 const std::vector
<std::string
> common_sub_dirs
= { "subs", "subtitles", "vobsubs", "sub", "vobsub", "subtitle" };
1961 const std::string subtitleExtensions
= CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions();
1962 GetItemsToScan(strBasePath
, subtitleExtensions
, common_sub_dirs
, items
);
1964 const std::string customPath
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SUBTITLES_CUSTOMPATH
);
1966 if (!CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() && !customPath
.empty()) // to avoid checking non-existent directories (network) every time..
1968 if (!CServiceBroker::GetNetwork().IsAvailable() && !URIUtils::IsHD(customPath
))
1970 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's inaccessible");
1971 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1973 else if (!CDirectory::Exists(customPath
))
1975 CLog::Log(LOGINFO
, "CUtil::CacheSubtitles: disabling alternate subtitle directory for this session, it's nonexistent");
1976 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(-1); // disabled
1979 CMediaSettings::GetInstance().SetAdditionalSubtitleDirectoryChecked(1);
1982 std::vector
<std::string
> strLookInPaths
;
1983 // this is last because we dont want to check any common subdirs or cd-dirs in the alternate <subtitles> dir.
1984 if (CMediaSettings::GetInstance().GetAdditionalSubtitleDirectoryChecked() == 1)
1986 std::string strPath2
= customPath
;
1987 URIUtils::AddSlashAtEnd(strPath2
);
1988 strLookInPaths
.push_back(strPath2
);
1991 int flags
= DIR_FLAG_NO_FILE_DIRS
| DIR_FLAG_NO_FILE_INFO
;
1992 for (const std::string
& path
: strLookInPaths
)
1994 CFileItemList moreItems
;
1995 CDirectory::GetDirectory(path
, moreItems
, subtitleExtensions
, flags
);
1996 items
.Append(moreItems
);
1999 std::vector
<std::string
> exts
= StringUtils::Split(subtitleExtensions
, '|');
2000 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".zip"), exts
.end());
2001 exts
.erase(std::remove(exts
.begin(), exts
.end(), ".rar"), exts
.end());
2003 ScanPathsForAssociatedItems(strSubtitle
, items
, exts
, vecSubtitles
);
2005 size_t iSize
= vecSubtitles
.size();
2006 for (size_t i
= 0; i
< iSize
; i
++)
2008 if (URIUtils::HasExtension(vecSubtitles
[i
], ".smi"))
2010 //Cache multi-language sami subtitle
2011 CDVDSubtitleStream stream
;
2012 if (stream
.Open(vecSubtitles
[i
]))
2014 CDVDSubtitleTagSami TagConv
;
2015 TagConv
.LoadHead(&stream
);
2016 if (TagConv
.m_Langclass
.size() >= 2)
2018 for (const auto &lang
: TagConv
.m_Langclass
)
2020 std::string strDest
=
2021 StringUtils::Format("special://temp/subtitle.{}.{}.smi", lang
.Name
, i
);
2022 if (CFile::Copy(vecSubtitles
[i
], strDest
))
2024 CLog::Log(LOGINFO
, " cached subtitle {}->{}", CURL::GetRedacted(vecSubtitles
[i
]),
2026 vecSubtitles
.push_back(strDest
);
2034 auto end
= std::chrono::steady_clock::now();
2035 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
2036 CLog::Log(LOGDEBUG
, "{}: END (total time: {} ms)", __FUNCTION__
, duration
.count());
2039 ExternalStreamInfo
CUtil::GetExternalStreamDetailsFromFilename(const std::string
& videoPath
, const std::string
& associatedFile
)
2041 ExternalStreamInfo info
;
2043 std::string videoBaseName
= URIUtils::GetFileName(videoPath
);
2044 URIUtils::RemoveExtension(videoBaseName
);
2046 std::string toParse
= URIUtils::GetFileName(associatedFile
);
2047 URIUtils::RemoveExtension(toParse
);
2049 // we check left part - if it's same as video base name - strip it
2050 if (StringUtils::StartsWithNoCase(toParse
, videoBaseName
))
2051 toParse
= toParse
.substr(videoBaseName
.length());
2052 else if (URIUtils::GetExtension(associatedFile
) == ".sub" && URIUtils::IsInArchive(associatedFile
))
2054 // exclude parsing of vobsub file names that are embedded in an archive
2055 CLog::Log(LOGDEBUG
, "{} - skipping archived vobsub filename parsing: {}", __FUNCTION__
,
2056 CURL::GetRedacted(associatedFile
));
2060 // trim any non-alphanumeric char in the beginning
2061 std::string::iterator result
= std::find_if(toParse
.begin(), toParse
.end(), StringUtils::isasciialphanum
);
2064 if (result
!= toParse
.end()) // if we have anything to parse
2066 std::string
inputString(result
, toParse
.end());
2067 std::string
delimiters(" .-");
2068 std::vector
<std::string
> tokens
;
2069 StringUtils::Tokenize(inputString
, tokens
, delimiters
);
2071 for (auto it
= tokens
.rbegin(); it
!= tokens
.rend(); ++it
)
2073 // try to recognize a flag
2074 std::string
flag_tmp(*it
);
2075 StringUtils::ToLower(flag_tmp
);
2076 if (!flag_tmp
.compare("none"))
2078 info
.flag
|= StreamFlags::FLAG_NONE
;
2081 else if (!flag_tmp
.compare("default"))
2083 info
.flag
|= StreamFlags::FLAG_DEFAULT
;
2086 else if (!flag_tmp
.compare("forced"))
2088 info
.flag
|= StreamFlags::FLAG_FORCED
;
2092 if (info
.language
.empty())
2094 std::string langCode
;
2095 // try to recognize language
2096 if (g_LangCodeExpander
.ConvertToISO6392B(*it
, langCode
))
2098 info
.language
= langCode
;
2103 name
= (*it
) + " " + name
;
2107 name
+= g_localizeStrings
.Get(21602); // External
2108 StringUtils::Trim(name
);
2109 info
.name
= StringUtils::RemoveDuplicatedSpacesAndTabs(name
);
2111 info
.flag
= StreamFlags::FLAG_NONE
;
2113 CLog::Log(LOGDEBUG
, "{} - Language = '{}' / Name = '{}' / Flag = '{}' from {}", __FUNCTION__
,
2114 info
.language
, info
.name
, info
.flag
, CURL::GetRedacted(associatedFile
));
2119 /*! \brief in a vector of subtitles finds the corresponding .sub file for a given .idx file
2121 bool CUtil::FindVobSubPair(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strIdxPath
, std::string
& strSubPath
)
2123 if (URIUtils::HasExtension(strIdxPath
, ".idx"))
2125 std::string strIdxFile
;
2126 std::string strIdxDirectory
;
2127 URIUtils::Split(strIdxPath
, strIdxDirectory
, strIdxFile
);
2128 for (const auto &subtitlePath
: vecSubtitles
)
2130 std::string strSubFile
;
2131 std::string strSubDirectory
;
2132 URIUtils::Split(subtitlePath
, strSubDirectory
, strSubFile
);
2133 if (URIUtils::IsInArchive(subtitlePath
))
2134 strSubDirectory
= CURL::Decode(strSubDirectory
);
2135 if (URIUtils::HasExtension(strSubFile
, ".sub") &&
2136 (URIUtils::PathEquals(URIUtils::ReplaceExtension(strIdxPath
,""),
2137 URIUtils::ReplaceExtension(subtitlePath
,"")) ||
2138 (strSubDirectory
.size() >= 11 &&
2139 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(strIdxPath
,"")))))
2141 strSubPath
= subtitlePath
;
2149 /*! \brief checks if in the vector of subtitles the given .sub file has a corresponding idx and hence is a vobsub file
2151 bool CUtil::IsVobSub(const std::vector
<std::string
>& vecSubtitles
, const std::string
& strSubPath
)
2153 if (URIUtils::HasExtension(strSubPath
, ".sub"))
2155 std::string strSubFile
;
2156 std::string strSubDirectory
;
2157 URIUtils::Split(strSubPath
, strSubDirectory
, strSubFile
);
2158 if (URIUtils::IsInArchive(strSubPath
))
2159 strSubDirectory
= CURL::Decode(strSubDirectory
);
2160 for (const auto &subtitlePath
: vecSubtitles
)
2162 std::string strIdxFile
;
2163 std::string strIdxDirectory
;
2164 URIUtils::Split(subtitlePath
, strIdxDirectory
, strIdxFile
);
2165 if (URIUtils::HasExtension(strIdxFile
, ".idx") &&
2166 (URIUtils::PathEquals(URIUtils::ReplaceExtension(subtitlePath
,""),
2167 URIUtils::ReplaceExtension(strSubPath
,"")) ||
2168 (strSubDirectory
.size() >= 11 &&
2169 StringUtils::EqualsNoCase(strSubDirectory
.substr(6, strSubDirectory
.length()-11), URIUtils::ReplaceExtension(subtitlePath
,"")))))
2176 /*! \brief find a plain or archived vobsub .sub file corresponding to an .idx file
2178 std::string
CUtil::GetVobSubSubFromIdx(const std::string
& vobSubIdx
)
2180 std::string vobSub
= URIUtils::ReplaceExtension(vobSubIdx
, ".sub");
2182 // check if a .sub file exists in the same directory
2183 if (CFile::Exists(vobSub
))
2188 // look inside a .rar or .zip in the same directory
2189 const std::string archTypes
[] = { "rar", "zip" };
2190 std::string vobSubFilename
= URIUtils::GetFileName(vobSub
);
2191 for (const std::string
& archType
: archTypes
)
2193 vobSub
= URIUtils::CreateArchivePath(archType
,
2194 CURL(URIUtils::ReplaceExtension(vobSubIdx
, std::string(".") + archType
)),
2195 vobSubFilename
).Get();
2196 if (CFile::Exists(vobSub
))
2200 return std::string();
2203 /*! \brief find a .idx file from a path of a plain or archived vobsub .sub file
2205 std::string
CUtil::GetVobSubIdxFromSub(const std::string
& vobSub
)
2207 std::string vobSubIdx
= URIUtils::ReplaceExtension(vobSub
, ".idx");
2209 // check if a .idx file exists in the same directory
2210 if (CFile::Exists(vobSubIdx
))
2215 // look outside archive (usually .rar) if the .sub is inside one
2216 if (URIUtils::IsInArchive(vobSub
))
2219 std::string archiveFile
= URIUtils::GetDirectory(vobSub
);
2220 std::string vobSubIdxDir
= URIUtils::GetParentPath(archiveFile
);
2222 if (!vobSubIdxDir
.empty())
2224 std::string vobSubIdxFilename
= URIUtils::GetFileName(vobSubIdx
);
2225 std::string vobSubIdx
= URIUtils::AddFileToFolder(vobSubIdxDir
, vobSubIdxFilename
);
2227 if (CFile::Exists(vobSubIdx
))
2232 return std::string();
2235 void CUtil::ScanForExternalAudio(const std::string
& videoPath
, std::vector
<std::string
>& vecAudio
)
2237 CFileItem
item(videoPath
, false);
2238 if ( item
.IsInternetStream()
2239 || item
.IsPlayList()
2245 std::string strBasePath
;
2246 std::string strAudio
;
2248 GetVideoBasePathAndFileName(videoPath
, strBasePath
, strAudio
);
2250 CFileItemList items
;
2251 const std::vector
<std::string
> common_sub_dirs
= { "audio", "tracks"};
2252 GetItemsToScan(strBasePath
, CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), common_sub_dirs
, items
);
2254 std::vector
<std::string
> exts
= StringUtils::Split(CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(), "|");
2256 CVideoDatabase database
;
2258 bool useAllExternalAudio
= database
.GetUseAllExternalAudioForVideo(videoPath
);
2260 if (useAllExternalAudio
)
2262 for (const auto& audioItem
: items
.GetList())
2264 vecAudio
.push_back(audioItem
.get()->GetPath());
2268 ScanPathsForAssociatedItems(strAudio
, items
, exts
, vecAudio
);
2271 bool CUtil::CanBindPrivileged()
2276 return true; //root user can always bind to privileged ports
2280 //check if CAP_NET_BIND_SERVICE is enabled, this allows non-root users to bind to privileged ports
2281 bool canbind
= false;
2282 cap_t capabilities
= cap_get_proc();
2285 cap_flag_value_t value
;
2286 if (cap_get_flag(capabilities
, CAP_NET_BIND_SERVICE
, CAP_EFFECTIVE
, &value
) == 0)
2289 cap_free(capabilities
);
2298 #endif //HAVE_LIBCAP
2300 #else //TARGET_POSIX
2304 #endif //TARGET_POSIX
2307 bool CUtil::ValidatePort(int port
)
2309 // check that it's a valid port
2311 if (!CUtil::CanBindPrivileged() && (port
< 1024 || port
> 65535))
2315 if (port
<= 0 || port
> 65535)
2321 int CUtil::GetRandomNumber()
2323 #if !defined(TARGET_WINDOWS)
2324 return rand_r(&s_randomSeed
);
2326 unsigned int number
;
2327 if (rand_s(&number
) == 0)
2334 void CUtil::CopyUserDataIfNeeded(const std::string
& strPath
,
2335 const std::string
& file
,
2336 const std::string
& destname
)
2338 std::string destPath
;
2339 if (destname
.empty())
2340 destPath
= URIUtils::AddFileToFolder(strPath
, file
);
2342 destPath
= URIUtils::AddFileToFolder(strPath
, destname
);
2344 if (!CFile::Exists(destPath
))
2346 // need to copy it across
2347 std::string srcPath
= URIUtils::AddFileToFolder("special://xbmc/userdata/", file
);
2348 CFile::Copy(srcPath
, destPath
);