Projects
home:sagiben
kodi-next
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 101
View file
_service:download_files:master.tar.gz/addons/xbmc.addon/metadata.xsd
Changed
@@ -16,6 +16,7 @@ <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="broken" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="news" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="reuselanguageinvoker" type="xs:boolean" minOccurs="0" maxOccurs="1"/> <xs:element name="assets" type="assetsList" minOccurs="0" maxOccurs="1"/> </xs:choice> <xs:attribute name="point" type="xs:string" use="required"/>
View file
_service:download_files:master.tar.gz/cmake/scripts/freebsd/ArchSetup.cmake
Changed
@@ -1,6 +1,6 @@ set(ARCH_DEFINES -DTARGET_POSIX -DTARGET_FREEBSD) set(SYSTEM_DEFINES -D__STDC_CONSTANT_MACROS -D_LARGEFILE64_SOURCE - -D_FILE_OFFSET_BITS=64) + -D_FILE_OFFSET_BITS=64 -DHAS_OSS) set(PLATFORM_DIR platform/linux) set(PLATFORMDEFS_DIR platform/posix) set(SYSTEM_LDFLAGS -L/usr/local/lib)
View file
_service:download_files:master.tar.gz/docs/CODE_GUIDELINES.md
Changed
@@ -244,6 +244,26 @@ void Test(void); ``` +### 3.7. Exceptions to the Formating Rules For Beter Readability +There are some special situations where vertical alignment and longer lines does greatly aid readability, for example the initialization of some table-like multiple row structures. In these **rare** cases exceptions can be made to the formatting rules on vertical alignment, and the defined line length can be exceeded. + +The layout can be protected from being reformatted when `clang-format` is applied by adding `// clang-format off` and `// clang-format on` statements either side of the lines of code. +For example +``` +// clang-format off +static const CGUIDialogMediaFilter::Filter filterList[] = { + { "movies", FieldTitle, 556, SettingType::String, "edit", "string", CDatabaseQueryRule::OPERATOR_CONTAINS }, + { "movies", FieldRating, 563, SettingType::Number, "range", "number", CDatabaseQueryRule::OPERATOR_BETWEEN }, + { "movies", FieldUserRating, 38018, SettingType::Integer, "range", "integer", CDatabaseQueryRule::OPERATOR_BETWEEN }, + ... + { "songs", FieldSource, 39030, SettingType::List, "list", "string", CDatabaseQueryRule::OPERATOR_EQUALS }, +}; +// clang-format on + ``` +The other code guidelines will still need to be applied within the delimited lines of code, but with `clang-format` off care will be needed to check these manually. Using vertical alignment means that sometimes the entire block of code may need to be realigned, good judgement should be used in each case with the objective of preserving readability yet minimising impact. + +This is to be used with discretion, marking large amounts of code to be left unformatted by `clang-format` without reasonable justification will be rejected. + **[back to top](#table-of-contents)** ## 4. Statements
View file
_service:download_files:master.tar.gz/system/settings/freebsd.xml
Changed
@@ -101,6 +101,42 @@ <default>true</default> <control type="toggle" /> </setting> + <setting id="videoplayer.usevaapivp8" type="boolean" parent="videoplayer.usevaapi" label="13453" help="13454"> + <requirement>HAVE_LIBVA</requirement> + <visible>false</visible> + <dependencies> + <dependency type="enable"> + <condition setting="videoplayer.usevaapi" operator="is">true</condition> + </dependency> + </dependencies> + <level>3</level> + <default>true</default> + <control type="toggle" /> + </setting> + <setting id="videoplayer.usevaapivp9" type="boolean" parent="videoplayer.usevaapi" label="13455" help="13456"> + <requirement>HAVE_LIBVA</requirement> + <visible>false</visible> + <dependencies> + <dependency type="enable"> + <condition setting="videoplayer.usevaapi" operator="is">true</condition> + </dependency> + </dependencies> + <level>3</level> + <default>true</default> + <control type="toggle" /> + </setting> + <setting id="videoplayer.usevaapihevc" type="boolean" parent="videoplayer.usevaapi" label="13460" help="13461"> + <requirement>HAVE_LIBVA</requirement> + <visible>false</visible> + <dependencies> + <dependency type="enable"> + <condition setting="videoplayer.usevaapi" operator="is">true</condition> + </dependency> + </dependencies> + <level>3</level> + <default>true</default> + <control type="toggle" /> + </setting> <setting id="videoplayer.prefervaapirender" type="boolean" parent="videoplayer.usevaapi" label="13457" help="36433"> <requirement>HAVE_LIBVA</requirement> <visible>false</visible>
View file
_service:download_files:master.tar.gz/tools/android/packaging/xbmc/build.gradle.in
Changed
@@ -46,6 +46,6 @@ dependencies { // New support library to for channels/programs development. - implementation 'com.android.support:support-tv-provider:28.0.0' - implementation 'com.google.code.gson:gson:2.8.0' + implementation 'androidx.tvprovider:tvprovider:1.0.0' + implementation 'com.google.code.gson:gson:2.8.5' }
View file
_service:download_files:master.tar.gz/tools/android/packaging/xbmc/src/channels/SyncChannelJobService.java.in
Changed
@@ -24,8 +24,8 @@ import android.content.Context; import android.database.Cursor; import android.os.AsyncTask; -import android.support.media.tv.TvContractCompat; import android.util.Log; +import androidx.tvprovider.media.tv.TvContractCompat; import @APP_PACKAGE@.R; import @APP_PACKAGE@.XBMCJsonRPC;
View file
_service:download_files:master.tar.gz/tools/android/packaging/xbmc/src/channels/SyncProgramsJobService.java.in
Changed
@@ -22,11 +22,11 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.PersistableBundle; -import android.support.annotation.NonNull; -import android.support.media.tv.Channel; -import android.support.media.tv.PreviewProgram; -import android.support.media.tv.TvContractCompat; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.tvprovider.media.tv.Channel; +import androidx.tvprovider.media.tv.PreviewProgram; +import androidx.tvprovider.media.tv.TvContractCompat; import @APP_PACKAGE@.Splash; import @APP_PACKAGE@.XBMCJsonRPC;
View file
_service:download_files:master.tar.gz/tools/android/packaging/xbmc/src/channels/model/XBMCDatabase.java.in
Changed
@@ -16,8 +16,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; -import android.support.annotation.DrawableRes; -import android.support.annotation.Nullable; +import androidx.annotation.DrawableRes; +import androidx.annotation.Nullable; import @APP_PACKAGE@.R; import @APP_PACKAGE@.channels.util.SharedPreferencesHelper;
View file
_service:download_files:master.tar.gz/tools/android/packaging/xbmc/src/channels/util/TvUtil.java.in
Changed
@@ -30,12 +30,12 @@ import android.media.tv.TvContract; import android.net.Uri; import android.os.PersistableBundle; -import android.support.annotation.NonNull; -import android.support.annotation.WorkerThread; -import android.support.media.tv.Channel; -import android.support.media.tv.ChannelLogoUtils; -import android.support.media.tv.TvContractCompat; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; +import androidx.tvprovider.media.tv.Channel; +import androidx.tvprovider.media.tv.ChannelLogoUtils; +import androidx.tvprovider.media.tv.TvContractCompat; import @APP_PACKAGE@.Splash; import @APP_PACKAGE@.channels.SyncChannelJobService;
View file
_service:download_files:master.tar.gz/xbmc/GUIInfoManager.cpp
Changed
@@ -8528,6 +8528,14 @@ /// @skinning_v19 **[New Boolean Condition]** \link Library_HasContent_Boxsets `Library.HasContent(boxsets)`\endlink /// <p> /// } +/// \table_row3{ <b>`Library.HasNode(path)`</b>, +/// \anchor Library_HasNode +/// _boolean_, +/// @return **True** if there the node is present in the library. +/// <p><hr> +/// @skinning_v19 **[New Boolean Condition]** \link Library_HasNode `Library.HasNode(path)`\endlink +/// <p> +/// } /// \table_end /// /// ----------------------------------------------------------------------------- @@ -8947,6 +8955,12 @@ else if (cat == "role" && prop.num_params() > 1) return AddMultiInfo(CGUIInfo(LIBRARY_HAS_ROLE, prop.param(1), 0)); } + else if (prop.name == "hasnode" && prop.num_params()) + { + std::string node = prop.param(0); + StringUtils::ToLower(node); + return AddMultiInfo(CGUIInfo(LIBRARY_HAS_NODE, prop.param(), 0)); + } } else if (cat.name == "musicplayer") {
View file
_service:download_files:master.tar.gz/xbmc/addons/addoninfo/AddonInfoBuilder.cpp
Changed
@@ -318,6 +318,11 @@ if (element && element->GetText() != nullptr) addon->AddExtraInfo("language", element->GetText()); + /* Parse addon.xml "<reuselanguageinvoker">...</reuselanguageinvoker>" */ + element = child->FirstChildElement("reuselanguageinvoker"); + if (element && element->GetText() != nullptr) + addon->AddExtraInfo("reuselanguageinvoker", element->GetText()); + /* Parse addon.xml "<noicon">...</noicon>" */ if (addon->m_icon.empty()) {
View file
_service:download_files:master.tar.gz/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h
Changed
@@ -12,6 +12,7 @@ #include <stdarg.h> /* va_list, va_start, va_arg, va_end */ #include <cstdlib> #include <cstring> +#include <ctime> #include <memory> #include <stdexcept> #include <string>
View file
_service:download_files:master.tar.gz/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
Changed
@@ -8,9 +8,12 @@ #include "AESinkAUDIOTRACK.h" +#include "ServiceBroker.h" #include "cores/AudioEngine/AESinkFactory.h" #include "cores/AudioEngine/Utils/AEUtil.h" +#include "settings/AdvancedSettings.h" #include "settings/Settings.h" +#include "settings/SettingsComponent.h" #include "utils/StringUtils.h" #include "utils/TimeUtils.h" #include "utils/log.h" @@ -37,7 +40,9 @@ const uint64_t UINT64_LOWER_BYTES = 0x00000000FFFFFFFF; const uint64_t UINT64_UPPER_BYTES = 0xFFFFFFFF00000000; -static const AEChannel KnownChannels[] = { AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_BC, AE_CH_BLOC, AE_CH_BROC, AE_CH_NULL }; +static const AEChannel KnownChannels[] = {AE_CH_FL, AE_CH_FR, AE_CH_FC, AE_CH_LFE, + AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, + AE_CH_BC, AE_CH_BLOC, AE_CH_BROC, AE_CH_NULL}; static int AEStreamFormatToATFormat(const CAEStreamInfo::DataType& dt) { @@ -240,6 +245,7 @@ m_at_jni = NULL; m_duration_written = 0; m_headPos = 0; + m_timestampPos = 0; m_volume = -1; m_sink_sampleRate = 0; m_passthrough = false; @@ -292,6 +298,7 @@ m_format = format; m_volume = -1; m_headPos = 0; + m_timestampPos = 0; m_linearmovingaverage.clear(); m_pause_ms = 0.0; CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::Initialize requested: sampleRate %u; format: %s; channels: %d", format.m_sampleRate, CAEUtil::DataFormatToStr(format.m_dataFormat), format.m_channelLayout.Count()); @@ -554,12 +561,15 @@ m_duration_written = 0; m_headPos = 0; + m_timestampPos = 0; + m_stampTimer.SetExpired(); m_linearmovingaverage.clear(); delete m_at_jni; m_at_jni = NULL; m_delay = 0.0; + m_hw_delay = 0.0; } bool CAESinkAUDIOTRACK::IsInitialized() @@ -607,12 +617,89 @@ // track delay in local member m_delay = d; + if (m_stampTimer.IsTimePast()) + { + if (!m_at_jni->getTimestamp(m_timestamp)) + { + CLog::Log(LOGDEBUG, "Could not acquire timestamp"); + m_stampTimer.Set(100); + } + else + { + // check if frameposition is valid and nano timer less than 50 ms outdated + if (m_timestamp.get_framePosition() > 0 && + (CurrentHostCounter() - m_timestamp.get_nanoTime()) < 50 * 1000 * 1000) + m_stampTimer.Set(1000); + else + m_stampTimer.Set(100); + } + } + else + { + // check if last value was received less than 2 seconds ago + if (m_timestamp.get_framePosition() > 0 && + (CurrentHostCounter() - m_timestamp.get_nanoTime()) < 2 * 1000 * 1000 * 1000) + { + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(LOGAUDIO)) + { + CLog::Log(LOGDEBUG, "Framecounter: {} Time: {} Current-Time: {}", + (m_timestamp.get_framePosition() & UINT64_LOWER_BYTES), + m_timestamp.get_nanoTime(), CurrentHostCounter()); + } + uint64_t delta = static_cast<uint64_t>(CurrentHostCounter() - m_timestamp.get_nanoTime()); + uint64_t stamphead = + static_cast<uint64_t>(m_timestamp.get_framePosition() & UINT64_LOWER_BYTES) + + delta * m_sink_sampleRate / 1000000000.0; + + // wrap around + // e.g. 0xFFFFFFFFFFFF0123 -> 0x0000000000002478 + // because we only query each second the simple smaller comparison won't suffice + // as delay can fluctuate minimally + if (stamphead < m_timestampPos && (m_timestampPos - stamphead) > 0x7FFFFFFFFFFFFFFFULL) + { + uint64_t stamp = m_timestampPos; + stamp += (1ULL << 32); + stamphead = (stamp & UINT64_UPPER_BYTES) | stamphead; + CLog::Log(LOGDEBUG, "Wraparound happend old: {} new: {}", m_timestampPos, stamphead); + } + m_timestampPos = stamphead; + + double playtime = m_timestampPos / static_cast<double>(m_sink_sampleRate); + + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(LOGAUDIO)) + { + CLog::Log(LOGDEBUG, + "Delay - Timestamp: {} (ms) delta: {} (ms) playtime: {} (ms) Duration: {} ms", + 1000.0 * (m_duration_written - playtime), delta / 1000000.0, playtime * 1000, + m_duration_written * 1000); + CLog::Log(LOGDEBUG, "Head-Position {} Timestamp Position {} Delay-Offset: {} ms", m_headPos, + m_timestampPos, 1000.0 * (m_headPos - m_timestampPos) / m_sink_sampleRate); + } + double hw_delay = + m_duration_written - m_timestampPos / static_cast<double>(m_sink_sampleRate); + // sadly we smooth the delay, so only compensate here what we did not yet smooth away + hw_delay -= d; + // sometimes at the beginning of the stream m_timestampPos is more accurate and ahead of + // m_headPos - don't use the computed value then and wait + if (hw_delay >= 0.0 && hw_delay < 1.0) + m_hw_delay = hw_delay; + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(LOGAUDIO)) + { + CLog::Log(LOGDEBUG, "HW-Delay (1): {}", hw_delay); + } + } + } + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(LOGAUDIO)) + { + CLog::Log(LOGDEBUG, "Delay Current: %lf", d * 1000); + } + status.SetDelay(d); } double CAESinkAUDIOTRACK::GetLatency() { - return 0.0; + return m_hw_delay; } double CAESinkAUDIOTRACK::GetCacheTotal() @@ -745,7 +832,8 @@ else { // waiting should only be done if sink is not run dry - if (m_delay > (m_audiotrackbuffer_sec * 0.8)) + double period_time = m_format.m_frames / static_cast<double>(m_sink_sampleRate); + if (m_delay >= (m_audiotrackbuffer_sec - period_time)) { double time_should_ms = 1000.0 * written_frames / m_format.m_sampleRate; double time_off = time_should_ms - time_to_add_ms; @@ -786,7 +874,9 @@ m_at_jni->pause(); m_duration_written = 0; m_headPos = 0; + m_timestampPos = 0; m_linearmovingaverage.clear(); + m_stampTimer.SetExpired(); m_pause_ms = 0.0; }
View file
_service:download_files:master.tar.gz/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
Changed
@@ -12,6 +12,7 @@ #include "cores/AudioEngine/Utils/AEDeviceInfo.h" #include "cores/AudioEngine/Utils/AEUtil.h" #include "threads/CriticalSection.h" +#include "threads/SystemClock.h" #include "threads/Thread.h" #include <deque> @@ -61,6 +62,7 @@ double m_duration_written; unsigned int m_min_buffer_size; uint64_t m_headPos; + uint64_t m_timestampPos = 0; // Moving Average computes the weighted average delay over // a fixed size of delay values - current size: 20 values double GetMovingAverageDelay(double newestdelay); @@ -85,6 +87,9 @@ int m_encoding; double m_pause_ms = 0.0; double m_delay = 0.0; + double m_hw_delay = 0.0; + CJNIAudioTimestamp m_timestamp; + XbmcThreads::EndTime m_stampTimer; std::vector<float> m_floatbuf; std::vector<int16_t> m_shortbuf;
View file
_service:download_files:master.tar.gz/xbmc/cores/VideoPlayer/DVDDemuxers/DVDDemuxClient.cpp
Changed
@@ -586,6 +586,7 @@ toStream->cryptoSession = stream->cryptoSession; toStream->externalInterfaces = stream->externalInterfaces; toStream->language = stream->language; + toStream->name = stream->name; CLog::Log(LOGDEBUG,"CDVDDemuxClient::RequestStream(): added/updated stream %d with codec_id %d", toStream->uniqueId,
View file
_service:download_files:master.tar.gz/xbmc/dbwrappers/DatabaseQuery.cpp
Changed
@@ -334,6 +334,22 @@ if (m_operator == OPERATOR_FALSE || m_operator == OPERATOR_TRUE) return GetBooleanQuery(negate, strType); + // Process boolean field with (not) EQUAL/CONTAINS "true"/"false" parameter too + if (GetFieldType(m_field) == BOOLEAN_FIELD && + (m_parameter[0] == "true" || m_parameter[0] == "false") && + (op == OPERATOR_CONTAINS || op == OPERATOR_EQUALS || op == OPERATOR_DOES_NOT_CONTAIN || + op == OPERATOR_DOES_NOT_EQUAL)) + { + if (m_parameter[0] == "false") + { + if (!negate.empty()) + negate.clear(); + else + negate = " NOT "; + } + return GetBooleanQuery(negate, strType); + } + // The BETWEEN operator is handled special if (op == OPERATOR_BETWEEN) {
View file
_service:download_files:master.tar.gz/xbmc/filesystem/MusicDatabaseDirectory.cpp
Changed
@@ -15,6 +15,8 @@ #include "guilib/LocalizeStrings.h" #include "guilib/TextureManager.h" #include "music/MusicDatabase.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" #include "utils/Crc32.h" #include "utils/LegacyPathTranslation.h" #include "utils/StringUtils.h" @@ -30,6 +32,45 @@ bool CMusicDatabaseDirectory::GetDirectory(const CURL& url, CFileItemList &items) { std::string path = CLegacyPathTranslation::TranslateMusicDbPath(url); + + // Adjust path to control navigation from albums to discs or directly to songs + CQueryParams params; + NODE_TYPE type; + NODE_TYPE childtype; + GetDirectoryNodeInfo(path, type, childtype, params); + if (childtype == NODE_TYPE_DISC) + { + bool bFlatten = false; + if (params.GetAlbumId() < 0) + bFlatten = true; // Showing *all albums next always songs + else + { + // Option to show discs for ordinary albums (not just boxed sets) + bFlatten = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool( + CSettings::SETTING_MUSICLIBRARY_SHOWDISCS); + CMusicDatabase musicdatabase; + if (musicdatabase.Open()) + { + if (bFlatten) // Check for boxed set + bFlatten = !musicdatabase.IsAlbumBoxset(params.GetAlbumId()); + if (!bFlatten) + { // Check we will get more than 1 disc when path filter options applied + int iDiscTotal = musicdatabase.GetDiscsCount(path); + bFlatten = iDiscTotal <= 1; + } + } + musicdatabase.Close(); + } + if (bFlatten) + { // Skip discs level and go directly to songs + CMusicDbUrl musicUrl; + if (!musicUrl.FromString(path)) + return false; + musicUrl.AppendPath("-2/"); // Flattened so adjust list label etc. + path = musicUrl.ToString(); + } + } + items.SetPath(path); items.m_dwSize = -1; // No size
View file
_service:download_files:master.tar.gz/xbmc/filesystem/PluginDirectory.cpp
Changed
@@ -86,6 +86,12 @@ return handle; } +void CPluginDirectory::reuseHandle(int handle, CPluginDirectory* cp) +{ + CSingleLock lock(m_handleLock); + globalHandles[handle] = cp; +} + void CPluginDirectory::removeHandle(int handle) { CSingleLock lock(m_handleLock); @@ -124,7 +130,12 @@ std::string basePath(url.Get()); // reset our wait event, and grab a new handle m_fetchComplete.Reset(); - int handle = getNewHandle(this); + int handle = CScriptInvocationManager::GetInstance().GetReusablePluginHandle(m_addon->LibPath()); + + if (handle < 0) + handle = getNewHandle(this); + else + reuseHandle(handle, this); // clear out our status variables m_fileResult->Reset(); @@ -151,7 +162,12 @@ CLog::Log(LOGDEBUG, "%s - calling plugin %s('%s','%s','%s','%s')", __FUNCTION__, m_addon->Name().c_str(), argv[0].c_str(), argv[1].c_str(), argv[2].c_str(), argv[3].c_str()); bool success = false; std::string file = m_addon->LibPath(); - int id = CScriptInvocationManager::GetInstance().ExecuteAsync(file, m_addon, argv); + bool reuseLanguageInvoker = false; + if (m_addon->ExtraInfo().find("reuselanguageinvoker") != m_addon->ExtraInfo().end()) + reuseLanguageInvoker = m_addon->ExtraInfo().at("reuselanguageinvoker") == "true"; + + int id = CScriptInvocationManager::GetInstance().ExecuteAsync(file, m_addon, argv, + reuseLanguageInvoker, handle); if (id >= 0) { // wait for our script to finish std::string scriptName = m_addon->Name();
View file
_service:download_files:master.tar.gz/xbmc/filesystem/PluginDirectory.h
Changed
@@ -75,6 +75,8 @@ static std::map<int,CPluginDirectory*> globalHandles; static int getNewHandle(CPluginDirectory *cp); + static void reuseHandle(int handle, CPluginDirectory* cp); + static void removeHandle(int handle); static CPluginDirectory *dirFromHandle(int handle); static CCriticalSection m_handleLock;
View file
_service:download_files:master.tar.gz/xbmc/guilib/guiinfo/GUIInfoLabels.h
Changed
@@ -420,6 +420,7 @@ #define LIBRARY_IS_SCANNING_MUSIC 730 #define LIBRARY_HAS_ROLE 735 #define LIBRARY_HAS_BOXSETS 736 +#define LIBRARY_HAS_NODE 737 #define SYSTEM_PLATFORM_LINUX 741 #define SYSTEM_PLATFORM_WINDOWS 742
View file
_service:download_files:master.tar.gz/xbmc/guilib/guiinfo/LibraryGUIInfo.cpp
Changed
@@ -9,10 +9,16 @@ #include "guilib/guiinfo/LibraryGUIInfo.h" #include "Application.h" +#include "ServiceBroker.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" #include "guilib/guiinfo/GUIInfo.h" #include "guilib/guiinfo/GUIInfoLabels.h" #include "music/MusicDatabase.h" +#include "profiles/ProfileManager.h" +#include "settings/SettingsComponent.h" #include "utils/StringUtils.h" +#include "utils/URIUtils.h" #include "video/VideoDatabase.h" using namespace KODI::GUILIB::GUIINFO; @@ -242,6 +248,23 @@ value = artistcount > 0; return true; } + case LIBRARY_HAS_NODE: + { + const CURL url(info.GetData3()); + const std::shared_ptr<CProfileManager> profileManager = + CServiceBroker::GetSettingsComponent()->GetProfileManager(); + CFileItemList items; + + std::string libDir = profileManager->GetLibraryFolder(); + XFILE::CDirectory::GetDirectory(libDir, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS); + if (items.Size() == 0) + libDir = "special://xbmc/system/library/"; + + std::string nodePath = URIUtils::AddFileToFolder(libDir, url.GetHostName() + "/"); + nodePath = URIUtils::AddFileToFolder(nodePath, url.GetFileName()); + value = XFILE::CFile::Exists(nodePath); + return true; + } case LIBRARY_IS_SCANNING: { value = (g_application.IsMusicScanning() || g_application.IsVideoScanning());
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/ILanguageInvocationHandler.h
Changed
@@ -24,7 +24,7 @@ virtual bool OnScriptInitialized(ILanguageInvoker *invoker) { return true; } virtual void OnScriptStarted(ILanguageInvoker *invoker) { } virtual void OnScriptAbortRequested(ILanguageInvoker *invoker) { } - virtual void OnScriptEnded(ILanguageInvoker *invoker) { } + virtual void OnExecutionEnded(ILanguageInvoker* invoker) {} virtual void OnScriptFinalized(ILanguageInvoker *invoker) { } virtual ILanguageInvoker* CreateInvoker() = 0;
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/ILanguageInvoker.cpp
Changed
@@ -36,7 +36,7 @@ bool ILanguageInvoker::IsActive() const { - return GetState() > InvokerStateUninitialized && GetState() < InvokerStateDone; + return GetState() > InvokerStateUninitialized && GetState() < InvokerStateScriptDone; } bool ILanguageInvoker::IsRunning() const @@ -72,13 +72,13 @@ void ILanguageInvoker::onExecutionFailed() { if (m_invocationHandler) - m_invocationHandler->OnScriptEnded(this); + m_invocationHandler->OnExecutionEnded(this); } void ILanguageInvoker::onExecutionDone() { if (m_invocationHandler) - m_invocationHandler->OnScriptEnded(this); + m_invocationHandler->OnExecutionEnded(this); } void ILanguageInvoker::onExecutionFinalized()
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/ILanguageInvoker.h
Changed
@@ -17,12 +17,14 @@ class CLanguageInvokerThread; class ILanguageInvocationHandler; -typedef enum { +typedef enum +{ InvokerStateUninitialized, InvokerStateInitialized, InvokerStateRunning, InvokerStateStopping, - InvokerStateDone, + InvokerStateScriptDone, + InvokerStateExecutionDone, InvokerStateFailed } InvokerState; @@ -43,6 +45,7 @@ InvokerState GetState() const { return m_state; } bool IsActive() const; bool IsRunning() const; + void Reset() { m_state = InvokerStateUninitialized; }; protected: friend class CLanguageInvokerThread;
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/LanguageInvokerThread.cpp
Changed
@@ -10,11 +10,14 @@ #include "ScriptInvocationManager.h" -CLanguageInvokerThread::CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager* invocationManager) +CLanguageInvokerThread::CLanguageInvokerThread(LanguageInvokerPtr invoker, + CScriptInvocationManager* invocationManager, + bool reuseable) : ILanguageInvoker(NULL), CThread("LanguageInvoker"), m_invoker(invoker), - m_invocationManager(invocationManager) + m_invocationManager(invocationManager), + m_reusable(reuseable) { } CLanguageInvokerThread::~CLanguageInvokerThread() @@ -22,7 +25,7 @@ Stop(true); } -InvokerState CLanguageInvokerThread::GetState() +InvokerState CLanguageInvokerThread::GetState() const { if (m_invoker == NULL) return InvokerStateFailed; @@ -30,6 +33,12 @@ return m_invoker->GetState(); } +void CLanguageInvokerThread::Release() +{ + m_bStop = true; + m_condition.notify_one(); +} + bool CLanguageInvokerThread::execute(const std::string &script, const std::vector<std::string> &arguments) { if (m_invoker == NULL || script.empty()) @@ -38,7 +47,17 @@ m_script = script; m_args = arguments; - Create(); + if (CThread::IsRunning()) + { + std::unique_lock<std::mutex> lck(m_mutex); + m_restart = true; + m_condition.notify_one(); + } + else + Create(); + + //Todo wait until running + return true; } @@ -50,14 +69,16 @@ if (!CThread::IsRunning()) return false; + Release(); + bool result = true; - if (m_invoker->GetState() < InvokerStateDone) + if (m_invoker->GetState() < InvokerStateExecutionDone) { // stop the language-specific invoker result = m_invoker->Stop(wait); - // stop the thread - CThread::StopThread(wait); } + // stop the thread + CThread::StopThread(wait); return result; } @@ -77,7 +98,18 @@ if (m_invoker == NULL) return; - m_invoker->Execute(m_script, m_args); + std::unique_lock<std::mutex> lckdl(m_mutex); + do + { + m_restart = false; + m_invoker->Execute(m_script, m_args); + + if (m_invoker->GetState() != InvokerStateScriptDone) + m_reusable = false; + + m_condition.wait(lckdl, [this] { return m_bStop || m_restart || !m_reusable; }); + + } while (m_reusable && !m_bStop); } void CLanguageInvokerThread::OnExit() @@ -86,7 +118,7 @@ return; m_invoker->onExecutionDone(); - m_invocationManager->OnScriptEnded(GetId()); + m_invocationManager->OnExecutionDone(GetId()); } void CLanguageInvokerThread::OnException() @@ -95,5 +127,5 @@ return; m_invoker->onExecutionFailed(); - m_invocationManager->OnScriptEnded(GetId()); + m_invocationManager->OnExecutionDone(GetId()); } \ No newline at end of file
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/LanguageInvokerThread.h
Changed
@@ -19,10 +19,20 @@ class CLanguageInvokerThread : public ILanguageInvoker, protected CThread { public: - CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager* invocationManager); + CLanguageInvokerThread(LanguageInvokerPtr invoker, + CScriptInvocationManager* invocationManager, + bool reuseable); ~CLanguageInvokerThread() override; - virtual InvokerState GetState(); + virtual InvokerState GetState() const; + + const std::string& GetScript() const { return m_script; }; + LanguageInvokerPtr GetInvoker() const { return m_invoker; }; + bool Reuseable(const std::string& script) const + { + return !m_bStop && m_reusable && GetState() == InvokerStateScriptDone && m_script == script; + }; + virtual void Release(); protected: bool execute(const std::string &script, const std::vector<std::string> &arguments) override; @@ -38,4 +48,9 @@ CScriptInvocationManager *m_invocationManager; std::string m_script; std::vector<std::string> m_args; + + std::mutex m_mutex; + std::condition_variable m_condition; + bool m_restart = false; + bool m_reusable = false; };
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/ScriptInvocationManager.cpp
Changed
@@ -77,6 +77,9 @@ // execute Process() once more to handle the remaining scripts Process(); + // it is safe to relese early, thread must be in m_scripts too + m_lastInvokerThread = nullptr; + // make sure all scripts are done std::vector<LanguageInvokerThread> tempList; for (const auto& script : m_scripts) @@ -96,9 +99,11 @@ if (!it.done) it.thread->Stop(true); } - tempList.clear(); lock.Enter(); + + tempList.clear(); + // uninitialize all invocation handlers and then remove them for (auto& it : m_invocationHandlers) it.second->Uninitialize(); @@ -175,12 +180,40 @@ return it != m_invocationHandlers.end() && it->second != NULL; } -LanguageInvokerPtr CScriptInvocationManager::GetLanguageInvoker(const std::string& script) const +int CScriptInvocationManager::GetReusablePluginHandle(const std::string& script) { + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread) + { + if (m_lastInvokerThread->Reuseable(script)) + return m_lastPluginHandle; + m_lastInvokerThread->Release(); + m_lastInvokerThread = nullptr; + } + return -1; +} + +LanguageInvokerPtr CScriptInvocationManager::GetLanguageInvoker(const std::string& script) +{ + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread) + { + if (m_lastInvokerThread->Reuseable(script)) + { + CLog::Log(LOGDEBUG, "%s - Reusing LanguageInvokerThread %d for script %s", __FUNCTION__, + m_lastInvokerThread->GetId(), script.c_str()); + m_lastInvokerThread->GetInvoker()->Reset(); + return m_lastInvokerThread->GetInvoker(); + } + m_lastInvokerThread->Release(); + m_lastInvokerThread = nullptr; + } + std::string extension = URIUtils::GetExtension(script); StringUtils::ToLower(extension); - CSingleLock lock(m_critSection); std::map<std::string, ILanguageInvocationHandler*>::const_iterator it = m_invocationHandlers.find(extension); if (it != m_invocationHandlers.end() && it->second != NULL) return LanguageInvokerPtr(it->second->CreateInvoker()); @@ -188,7 +221,12 @@ return LanguageInvokerPtr(); } -int CScriptInvocationManager::ExecuteAsync(const std::string &script, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */) +int CScriptInvocationManager::ExecuteAsync( + const std::string& script, + const ADDON::AddonPtr& addon /* = ADDON::AddonPtr() */, + const std::vector<std::string>& arguments /* = std::vector<std::string>() */, + bool reuseable /* = false */, + int pluginHandle /* = -1 */) { if (script.empty()) return -1; @@ -200,10 +238,16 @@ } LanguageInvokerPtr invoker = GetLanguageInvoker(script); - return ExecuteAsync(script, invoker, addon, arguments); + return ExecuteAsync(script, invoker, addon, arguments, reuseable, pluginHandle); } -int CScriptInvocationManager::ExecuteAsync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */) +int CScriptInvocationManager::ExecuteAsync( + const std::string& script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr& addon /* = ADDON::AddonPtr() */, + const std::vector<std::string>& arguments /* = std::vector<std::string>() */, + bool reuseable /* = false */, + int pluginHandle /* = -1 */) { if (script.empty() || languageInvoker == NULL) return -1; @@ -214,26 +258,49 @@ return -1; } - CLanguageInvokerThreadPtr invokerThread = CLanguageInvokerThreadPtr(new CLanguageInvokerThread(languageInvoker, this)); - if (invokerThread == NULL) + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread && m_lastInvokerThread->GetInvoker() == languageInvoker) + { + if (addon != NULL) + m_lastInvokerThread->SetAddon(addon); + + // After we leave the lock, m_lastInvokerThread can be released -> copy! + CLanguageInvokerThreadPtr invokerThread = m_lastInvokerThread; + lock.Leave(); + invokerThread->Execute(script, arguments); + + return invokerThread->GetId(); + } + + m_lastInvokerThread = + CLanguageInvokerThreadPtr(new CLanguageInvokerThread(languageInvoker, this, reuseable)); + if (m_lastInvokerThread == NULL) return -1; if (addon != NULL) - invokerThread->SetAddon(addon); + m_lastInvokerThread->SetAddon(addon); - CSingleLock lock(m_critSection); - invokerThread->SetId(m_nextId++); + m_lastInvokerThread->SetId(m_nextId++); + m_lastPluginHandle = pluginHandle; - LanguageInvokerThread thread = {invokerThread, script, false}; - m_scripts.insert(std::make_pair(invokerThread->GetId(), thread)); - m_scriptPaths.insert(std::make_pair(script, invokerThread->GetId())); + LanguageInvokerThread thread = {m_lastInvokerThread, script, false}; + m_scripts.insert(std::make_pair(m_lastInvokerThread->GetId(), thread)); + m_scriptPaths.insert(std::make_pair(script, m_lastInvokerThread->GetId())); + // After we leave the lock, m_lastInvokerThread can be released -> copy! + CLanguageInvokerThreadPtr invokerThread = m_lastInvokerThread; lock.Leave(); invokerThread->Execute(script, arguments); return invokerThread->GetId(); } -int CScriptInvocationManager::ExecuteSync(const std::string &script, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */, uint32_t timeoutMs /* = 0 */, bool waitShutdown /* = false */) +int CScriptInvocationManager::ExecuteSync( + const std::string& script, + const ADDON::AddonPtr& addon /* = ADDON::AddonPtr() */, + const std::vector<std::string>& arguments /* = std::vector<std::string>() */, + uint32_t timeoutMs /* = 0 */, + bool waitShutdown /* = false */) { if (script.empty()) return -1; @@ -248,7 +315,13 @@ return ExecuteSync(script, invoker, addon, arguments, timeoutMs, waitShutdown); } -int CScriptInvocationManager::ExecuteSync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */, uint32_t timeoutMs /* = 0 */, bool waitShutdown /* = false */) +int CScriptInvocationManager::ExecuteSync( + const std::string& script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr& addon /* = ADDON::AddonPtr() */, + const std::vector<std::string>& arguments /* = std::vector<std::string>() */, + uint32_t timeoutMs /* = 0 */, + bool waitShutdown /* = false */) { int scriptId = ExecuteAsync(script, languageInvoker, addon, arguments); if (scriptId < 0) @@ -322,7 +395,7 @@ return IsRunning(it->second); } -void CScriptInvocationManager::OnScriptEnded(int scriptId) +void CScriptInvocationManager::OnExecutionDone(int scriptId) { if (scriptId < 0) return;
View file
_service:download_files:master.tar.gz/xbmc/interfaces/generic/ScriptInvocationManager.h
Changed
@@ -32,7 +32,12 @@ void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::set<std::string> &extensions); void UnregisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler); bool HasLanguageInvoker(const std::string &script) const; - LanguageInvokerPtr GetLanguageInvoker(const std::string& script) const; + LanguageInvokerPtr GetLanguageInvoker(const std::string& script); + + /*! + * \brief Returns addon_handle if last reusable invoker is ready to use. + */ + int GetReusablePluginHandle(const std::string& script); /*! * \brief Executes the given script asynchronously in a separate thread. @@ -42,7 +47,11 @@ * \param arguments (Optional) List of arguments passed to the script * \return -1 if an error occurred, otherwise the ID of the script */ - int ExecuteAsync(const std::string &script, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>()); + int ExecuteAsync(const std::string& script, + const ADDON::AddonPtr& addon = ADDON::AddonPtr(), + const std::vector<std::string>& arguments = std::vector<std::string>(), + bool reuseable = false, + int pluginHandle = -1); /*! * \brief Executes the given script asynchronously in a separate thread. * @@ -52,7 +61,12 @@ * \param arguments (Optional) List of arguments passed to the script * \return -1 if an error occurred, otherwise the ID of the script */ - int ExecuteAsync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>()); + int ExecuteAsync(const std::string& script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr& addon = ADDON::AddonPtr(), + const std::vector<std::string>& arguments = std::vector<std::string>(), + bool reuseable = false, + int pluginHandle = -1); /*! * \brief Executes the given script synchronously. @@ -70,7 +84,11 @@ * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not. * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired */ - int ExecuteSync(const std::string &script, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>(), uint32_t timeoutMs = 0, bool waitShutdown = false); + int ExecuteSync(const std::string& script, + const ADDON::AddonPtr& addon = ADDON::AddonPtr(), + const std::vector<std::string>& arguments = std::vector<std::string>(), + uint32_t timeoutMs = 0, + bool waitShutdown = false); /*! * \brief Executes the given script synchronously. * @@ -88,7 +106,12 @@ * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not. * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired */ - int ExecuteSync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>(), uint32_t timeoutMs = 0, bool waitShutdown = false); + int ExecuteSync(const std::string& script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr& addon = ADDON::AddonPtr(), + const std::vector<std::string>& arguments = std::vector<std::string>(), + uint32_t timeoutMs = 0, + bool waitShutdown = false); bool Stop(int scriptId, bool wait = false); bool Stop(const std::string &scriptPath, bool wait = false); @@ -98,7 +121,7 @@ protected: friend class CLanguageInvokerThread; - void OnScriptEnded(int scriptId); + void OnExecutionDone(int scriptId); private: CScriptInvocationManager() = default; @@ -118,6 +141,9 @@ LanguageInvocationHandlerMap m_invocationHandlers; LanguageInvokerThreadMap m_scripts; + CLanguageInvokerThreadPtr m_lastInvokerThread; + int m_lastPluginHandle; + std::map<std::string, int> m_scriptPaths; int m_nextId = 0; mutable CCriticalSection m_critSection;
View file
_service:download_files:master.tar.gz/xbmc/interfaces/json-rpc/PlayerOperations.cpp
Changed
@@ -45,6 +45,25 @@ using namespace PVR; using namespace KODI::MESSAGING; +namespace +{ + +void AppendAudioStreamFlagsAsBooleans(CVariant& list, StreamFlags flags) +{ + list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0); + list["isoriginal"] = ((flags & StreamFlags::FLAG_ORIGINAL) != 0); + list["isimpaired"] = ((flags & StreamFlags::FLAG_VISUAL_IMPAIRED) != 0); +} + +void AppendSubtitleStreamFlagsAsBooleans(CVariant& list, StreamFlags flags) +{ + list["isdefault"] = ((flags & StreamFlags::FLAG_DEFAULT) != 0); + list["isforced"] = ((flags & StreamFlags::FLAG_FORCED) != 0); + list["isimpaired"] = ((flags & StreamFlags::FLAG_HEARING_IMPAIRED) != 0); +} + +} // namespace + JSONRPC_STATUS CPlayerOperations::GetActivePlayers(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { int activePlayers = GetActivePlayers(); @@ -1678,6 +1697,7 @@ result["codec"] = info.codecName; result["bitrate"] = info.bitrate; result["channels"] = info.channels; + AppendAudioStreamFlagsAsBooleans(result, info.flags); } } else @@ -1710,6 +1730,7 @@ audioStream["codec"] = info.codecName; audioStream["bitrate"] = info.bitrate; audioStream["channels"] = info.channels; + AppendAudioStreamFlagsAsBooleans(audioStream, info.flags); result.append(audioStream); } @@ -1819,6 +1840,7 @@ result["index"] = index; result["name"] = info.name; result["language"] = info.language; + AppendSubtitleStreamFlagsAsBooleans(result, info.flags); } } else @@ -1849,6 +1871,7 @@ subtitle["index"] = index; subtitle["name"] = info.name; subtitle["language"] = info.language; + AppendSubtitleStreamFlagsAsBooleans(subtitle, info.flags); result.append(subtitle); }
View file
_service:download_files:master.tar.gz/xbmc/interfaces/json-rpc/schema/types.json
Changed
@@ -230,7 +230,10 @@ "language": { "type": "string", "required": true }, "codec": { "type": "string", "required": true }, "bitrate": { "type": "integer", "required": true }, - "channels": { "type": "integer", "required": true } + "channels": { "type": "integer", "required": true }, + "isdefault": { "type": "boolean", "required": true }, + "isoriginal": { "type": "boolean", "required": true }, + "isimpaired": { "type": "boolean", "required": true } } }, "Player.Video.Stream": { @@ -249,7 +252,10 @@ "properties": { "index": { "type": "integer", "minimum": 0, "required": true }, "name": { "type": "string", "required": true }, - "language": { "type": "string", "required": true } + "language": { "type": "string", "required": true }, + "isdefault": { "type": "boolean", "required": true }, + "isforced": { "type": "boolean", "required": true }, + "isimpaired": { "type": "boolean", "required": true } } }, "Player.Property.Name": {
View file
_service:download_files:master.tar.gz/xbmc/interfaces/json-rpc/schema/version.txt
Changed
@@ -1,1 +1,1 @@ -JSONRPC_VERSION 10.6.1 +JSONRPC_VERSION 11.0.0
View file
_service:download_files:master.tar.gz/xbmc/interfaces/python/PythonInvoker.cpp
Changed
@@ -11,6 +11,17 @@ #include <iterator> #include <osdefs.h> +// This is a workaround to compile Kodi against python 3.8 +//! @todo implement a compliant way to get access to the chain of thread states +#if PY_VERSION_HEX >= 0x03080000 +# define Py_BUILD_CORE +# undef HAVE_STD_ATOMIC +/* for access to the fields of PyInterpreterState */ +# include <internal/pycore_pystate.h> +# undef Py_BUILD_CORE +# define HAVE_STD_ATOMIC +#endif + #include "PythonInvoker.h" #include "Application.h" #include "ServiceBroker.h" @@ -23,8 +34,6 @@ #include "windowing/GraphicContext.h" #include "guilib/GUIWindowManager.h" #include "guilib/LocalizeStrings.h" -#include "interfaces/legacy/Addon.h" -#include "interfaces/python/LanguageHook.h" #include "interfaces/python/PyContext.h" #include "interfaces/python/pythreadstate.h" #include "interfaces/python/swig.h" @@ -110,7 +119,7 @@ if (GetId() < 0) return; - if (GetState() < InvokerStateDone) + if (GetState() < InvokerStateExecutionDone) CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread \"%s\" to stop", GetId(), (!m_sourceFile.empty() ? m_sourceFile.c_str() : "unknown script")); Stop(true); @@ -152,6 +161,7 @@ { // copy the code/script into a local string buffer m_sourceFile = script; + m_pythonPath.clear(); // copy the arguments into a local buffer unsigned int argc = arguments.size(); @@ -160,113 +170,140 @@ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): start processing", GetId(), m_sourceFile.c_str()); - // get the global lock - extern PyThreadState* savestate; - PyEval_RestoreThread(savestate); - PyThreadState* state = Py_NewInterpreter(); - if (state == NULL) - { - PyEval_ReleaseLock(); - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread state!", GetId(), m_sourceFile.c_str()); - return false; - } - // swap in my thread state - PyThreadState_Swap(state); - - XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> languageHook(new XBMCAddon::Python::PythonLanguageHook(state->interp)); - languageHook->RegisterMe(); - - onInitialization(); - setState(InvokerStateInitialized); - std::string realFilename(CSpecialProtocol::TranslatePath(m_sourceFile)); - if (realFilename == m_sourceFile) - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\"", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str()); - else - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\" (\"%s\")", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), realFilename.c_str()); - - // get path from script file name and add python path's - // this is used for python so it will search modules from script path first std::string scriptDir = URIUtils::GetDirectory(realFilename); URIUtils::RemoveSlashAtEnd(scriptDir); - addPath(scriptDir); - // add all addon module dependencies to path - if (m_addon) + // set m_threadState if it's not set. + PyThreadState* l_threadState = nullptr; + bool newInterp = false; { - std::set<std::string> paths; - getAddonModuleDeps(m_addon, paths); - for (const auto& it : paths) - addPath(it); - } - else - { // for backwards compatibility. - // we don't have any addon so just add all addon modules installed - CLog::Log(LOGWARNING, "CPythonInvoker(%d): Script invoked without an addon. Adding all addon " - "modules installed to python path as fallback. This behaviour will be removed in future " - "version.", GetId()); - ADDON::VECADDONS addons; - CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::ADDON_SCRIPT_MODULE); - for (const auto& addon : addons) - addPath(CSpecialProtocol::TranslatePath(addon->LibPath())); + if (!m_threadState) + { + // TODO: Re-write everything. + // this is a TOTAL hack. We need the GIL but we need to borrow a PyThreadState in order to get it + // as of Python 3.2 since PyEval_AcquireLock is deprecated + extern PyThreadState* savestate; + PyEval_RestoreThread(savestate); + l_threadState = Py_NewInterpreter(); + PyEval_ReleaseThread(l_threadState); + if (l_threadState == NULL) + { + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread m_threadState!", GetId(), + m_sourceFile.c_str()); + return false; + } + newInterp = true; + } + else + l_threadState = m_threadState; } - // we want to use sys.path so it includes site-packages - // if this fails, default to using Py_GetPath - PyObject* sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF when finished - PyObject* sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete - PyObject* pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete - - if (pathObj != NULL && PyList_Check(pathObj)) + // get the GIL + PyEval_RestoreThread(l_threadState); + if (newInterp) { - for (int i = 0; i < PyList_Size(pathObj); i++) + m_languageHook = new XBMCAddon::Python::PythonLanguageHook(l_threadState->interp); + m_languageHook->RegisterMe(); + + onInitialization(); + setState(InvokerStateInitialized); + + if (realFilename == m_sourceFile) + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\"", GetId(), + m_sourceFile.c_str(), m_sourceFile.c_str()); + else + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\" (\"%s\")", + GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), realFilename.c_str()); + + // get path from script file name and add python path's + // this is used for python so it will search modules from script path first + addPath(scriptDir); + + // add all addon module dependencies to path + if (m_addon) { - PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete - if (e != NULL && PyUnicode_Check(e)) - addPath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete or modify + std::set<std::string> paths; + getAddonModuleDeps(m_addon, paths); + for (const auto& it : paths) + addPath(it); + } + else + { // for backwards compatibility. + // we don't have any addon so just add all addon modules installed + CLog::Log( + LOGWARNING, + "CPythonInvoker(%d): Script invoked without an addon. Adding all addon " + "modules installed to python path as fallback. This behaviour will be removed in future " + "version.", + GetId()); + ADDON::VECADDONS addons; + CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::ADDON_SCRIPT_MODULE); + for (unsigned int i = 0; i < addons.size(); ++i) + addPath(CSpecialProtocol::TranslatePath(addons[i]->LibPath())); } - } - else - { - std::string GetPath; - g_charsetConverter.wToUTF8(Py_GetPath(), GetPath); - addPath(GetPath); - } - Py_DECREF(sysMod); // release ref to sysMod + // we want to use sys.path so it includes site-packages + // if this fails, default to using Py_GetPath + PyObject* sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF when finished + PyObject* sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete + PyObject* pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete - // set current directory and python's path. - PySys_SetArgv(argc, &argv[0]); + if (pathObj != NULL && PyList_Check(pathObj)) + { + for (int i = 0; i < PyList_Size(pathObj); i++) + { + PyObject* e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete + if (e != NULL && PyUnicode_Check(e)) + addPath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete or modify + } + } + else + { + std::string GetPath; + g_charsetConverter.wToUTF8(Py_GetPath(), GetPath); + addPath(GetPath); + } + + Py_DECREF(sysMod); // release ref to sysMod #ifdef TARGET_WINDOWS - std::string pyPathUtf8; - g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false); - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), pyPathUtf8.c_str()); + std::string pyPathUtf8; + g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false); + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), + m_sourceFile.c_str(), pyPathUtf8.c_str()); #else // ! TARGET_WINDOWS - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), m_pythonPath.c_str()); + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), + m_sourceFile.c_str(), m_pythonPath.c_str()); #endif // ! TARGET_WINDOWS - std::wstring pypath; - g_charsetConverter.utf8ToW(m_pythonPath, pypath); - PySys_SetPath(pypath.c_str()); + std::wstring pypath; + g_charsetConverter.utf8ToW(m_pythonPath, pypath); + PySys_SetPath(pypath.c_str()); + + { // set the m_threadState to this new interp + CSingleLock lockMe(m_critical); + m_threadState = l_threadState; + } + } + else + // swap in my thread m_threadState + PyThreadState_Swap(m_threadState); + + // set current directory and python's path. + PySys_SetArgv(argc, &argv[0]); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_sourceFile.c_str(), scriptDir.c_str()); PyObject* module = PyImport_AddModule("__main__"); PyObject* moduleDict = PyModule_GetDict(module); - // when we are done initing we store thread state so we can be aborted - PyThreadState_Swap(NULL); - PyEval_ReleaseLock(); - // we need to check if we was asked to abort before we had inited bool stopping = false; - { CSingleLock lock(m_critical); - m_threadState = state; + { + GilSafeSingleLock lock(m_critical); stopping = m_stop; } - PyEval_AcquireThread(state); - bool failed = false; std::string exceptionType, exceptionValue, exceptionTraceback; if (!stopping) @@ -316,17 +353,17 @@ } } - bool systemExitThrown = false; + m_systemExitThrown = false; InvokerState stateToSet; if (!failed && !PyErr_Occurred()) { CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_sourceFile.c_str()); - stateToSet = InvokerStateDone; + stateToSet = InvokerStateScriptDone; onSuccess(); } else if (PyErr_ExceptionMatches(PyExc_SystemExit)) { - systemExitThrown = true; + m_systemExitThrown = true; CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_sourceFile.c_str()); stateToSet = InvokerStateFailed; onAbort(); @@ -351,6 +388,7 @@ onError(exceptionType, exceptionValue, exceptionTraceback); } + CSingleLock lock(m_critical); // no need to do anything else because the script has already stopped if (failed) { @@ -358,74 +396,42 @@ return true; } - PyObject* m = PyImport_AddModule("xbmc"); - if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); - - // make sure all sub threads have finished - for (PyThreadState* s = state->interp->tstate_head, *old = NULL; s;) + if (m_threadState) { - if (s == state) - { - s = s->next; - continue; - } - if (old != s) - { - CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %" PRIu64, GetId(), m_sourceFile.c_str(), (uint64_t)s->thread_id); - old = s; - } - - CPyThreadState pyState; - Sleep(100); - pyState.Restore(); - - s = state->interp->tstate_head; - } + PyObject* m = PyImport_AddModule("xbmc"); + if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), + m_sourceFile.c_str()); - // pending calls must be cleared out - XBMCAddon::RetardedAsyncCallbackHandler::clearPendingCalls(state); + // make sure all sub threads have finished + for (PyThreadState* old = nullptr; m_threadState != nullptr;) + { + PyThreadState* s = m_threadState->interp->tstate_head; + for (; s && s == m_threadState;) + s = s->next; - PyThreadState_Swap(NULL); - PyEval_ReleaseLock(); + if (!s) + break; - // set stopped event - this allows ::stop to run and kill remaining threads - // this event has to be fired without holding m_critical - // also the GIL (PyEval_RestoreThread) must not be held - // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) - m_stoppedEvent.Set(); + if (old != s) + { + CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %" PRIu64, GetId(), + m_sourceFile.c_str(), (uint64_t)s->thread_id); + old = s; + } - { CSingleLock lock(m_critical); - m_threadState = NULL; + lock.Leave(); + CPyThreadState pyState; + Sleep(100); + pyState.Restore(); + lock.Enter(); + } } - PyEval_RestoreThread(state); - - onDeinitialization(); - - // run the gc before finishing - // - // if the script exited by throwing a SystemExit exception then going back - // into the interpreter causes this python bug to get hit: - // http://bugs.python.org/issue10582 - // and that causes major failures. So we are not going to go back in - // to run the GC if that's the case. - if (!m_stop && languageHook->HasRegisteredAddonClasses() && !systemExitThrown && - PyRun_SimpleString(GC_SCRIPT) == -1) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_sourceFile.c_str()); - - Py_EndInterpreter(state); - - // If we still have objects left around, produce an error message detailing what's been left behind - if (languageHook->HasRegisteredAddonClasses()) - CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several " - "classes in memory that we couldn't clean up. The classes include: %s", - GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), getListOfAddonClassesAsString(languageHook).c_str()); - - // unregister the language hook - languageHook->UnregisterMe(); + // pending calls must be cleared out + XBMCAddon::RetardedAsyncCallbackHandler::clearPendingCalls(m_threadState); - PyEval_ReleaseLock(); + PyEval_ReleaseThread(m_threadState); setState(stateToSet); @@ -467,28 +473,37 @@ CSingleLock lock(m_critical); m_stop = true; - if (!IsRunning()) + if (!IsRunning() && !m_threadState) return false; - setState(InvokerStateStopping); - if (m_threadState != NULL) { - PyEval_RestoreThread((PyThreadState*)m_threadState); - PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState); + if (IsRunning()) + { + setState(InvokerStateStopping); + lock.Leave(); - //tell xbmc.Monitor to call onAbortRequested() - if (m_addon != NULL) - onAbortRequested(); + PyEval_RestoreThread((PyThreadState*)m_threadState); - PyObject* m; - m = PyImport_AddModule("xbmc"); - if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); + //tell xbmc.Monitor to call onAbortRequested() + if (m_addon) + { + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): trigger Monitor abort request", GetId(), + m_sourceFile.c_str()); + onAbortRequested(); + } - PyThreadState_Swap(old); - old = NULL; - PyEval_ReleaseLock(); + PyObject* m; + m = PyImport_AddModule("xbmc"); + if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), + m_sourceFile.c_str()); + + PyEval_ReleaseThread(m_threadState); + } + else + //Release the lock while waiting for threads to finish + lock.Leave(); XbmcThreads::EndTime timeout(PYTHON_SCRIPT_TIMEOUT); while (!m_stoppedEvent.WaitMSec(15)) @@ -508,22 +523,24 @@ } } + lock.Enter(); + + setState(InvokerStateExecutionDone); + // Useful for add-on performance metrics if (!timeout.IsTimePast()) CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took %dms", GetId(), m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft()); - // everything which didn't exit by now gets killed - { - // grabbing the PyLock while holding the m_critical is asking for a deadlock - CSingleExit ex2(m_critical); - PyEval_RestoreThread((PyThreadState*)m_threadState); - } - // Since we released the m_critical it's possible that the state is cleaned up // so we need to recheck for m_threadState == NULL if (m_threadState != NULL) { - old = PyThreadState_Swap((PyThreadState*)m_threadState); + { + // grabbing the PyLock while holding the m_critical is asking for a deadlock + CSingleExit ex2(m_critical); + PyEval_RestoreThread((PyThreadState*)m_threadState); + } + for (PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next) { // Raise a SystemExit exception in python threads @@ -534,18 +551,72 @@ // If a dialog entered its doModal(), we need to wake it to see the exception pulseGlobalEvent(); - } - - if (old != NULL) - PyThreadState_Swap(old); + PyEval_ReleaseThread(m_threadState); + m_threadState = nullptr; + } lock.Leave(); - PyEval_ReleaseLock(); + + setState(InvokerStateFailed); } return true; } +// Always called from Invoker thread +void CPythonInvoker::onExecutionDone() +{ + CSingleLock lock(m_critical); + if (m_threadState != NULL) + { + CLog::Log(LOGDEBUG, "%s(%d, %s)", __FUNCTION__, GetId(), m_sourceFile.c_str()); + + PyEval_RestoreThread(m_threadState); + + onDeinitialization(); + + // run the gc before finishing + // + // if the script exited by throwing a SystemExit exception then going back + // into the interpreter causes this python bug to get hit: + // http://bugs.python.org/issue10582 + // and that causes major failures. So we are not going to go back in + // to run the GC if that's the case. + if (!m_stop && m_languageHook->HasRegisteredAddonClasses() && !m_systemExitThrown && + PyRun_SimpleString(GC_SCRIPT) == -1) + CLog::Log(LOGERROR, + "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to " + "shutting down the Interpreter", + GetId(), m_sourceFile.c_str()); + + Py_EndInterpreter(m_threadState); + + // If we still have objects left around, produce an error message detailing what's been left behind + if (m_languageHook->HasRegisteredAddonClasses()) + CLog::Log(LOGWARNING, + "CPythonInvoker(%d, %s): the python script \"%s\" has left several " + "classes in memory that we couldn't clean up. The classes include: %s", + GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), + getListOfAddonClassesAsString(m_languageHook).c_str()); + + // unregister the language hook + m_languageHook->UnregisterMe(); + + PyEval_ReleaseLock(); + + // set stopped event - this allows ::stop to run and kill remaining threads + // this event has to be fired without holding m_critical + // also the GIL (PyEval_AcquireLock) must not be held + // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) + m_stoppedEvent.Set(); + + m_threadState = nullptr; + + setState(InvokerStateExecutionDone); + } + ILanguageInvoker::onExecutionDone(); +} + void CPythonInvoker::onExecutionFailed() { PyThreadState_Swap(NULL);
View file
_service:download_files:master.tar.gz/xbmc/interfaces/python/PythonInvoker.h
Changed
@@ -9,6 +9,8 @@ #pragma once #include "interfaces/generic/ILanguageInvoker.h" +#include "interfaces/legacy/Addon.h" +#include "interfaces/python/LanguageHook.h" #include "threads/CriticalSection.h" #include "threads/Event.h" @@ -17,6 +19,7 @@ #include <vector> typedef struct _object PyObject; +struct _ts; class CPythonInvoker : public ILanguageInvoker { @@ -35,6 +38,7 @@ bool execute(const std::string &script, const std::vector<std::string> &arguments) override; virtual void executeScript(FILE* fp, const std::string& script, PyObject* moduleDict); bool stop(bool abort) override; + void onExecutionDone() override; void onExecutionFailed() override; // custom virtual methods @@ -61,9 +65,12 @@ FILE* PyFile_AsFileWithMode(PyObject* py_file, const char* mode); std::string m_pythonPath; - void* m_threadState; + _ts* m_threadState; bool m_stop; CEvent m_stoppedEvent; + XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> m_languageHook; + bool m_systemExitThrown = false; + static CCriticalSection s_critical; };
View file
_service:download_files:master.tar.gz/xbmc/interfaces/python/XBPython.cpp
Changed
@@ -647,7 +647,7 @@ } } -void XBPython::OnScriptEnded(ILanguageInvoker* invoker) +void XBPython::OnExecutionEnded(ILanguageInvoker* invoker) { CSingleLock lock(m_vecPyList); PyList::iterator it = m_vecPyList.begin(); @@ -656,9 +656,9 @@ if (it->id == invoker->GetId()) { if (it->pyThread->IsStopping()) - CLog::Log(LOGINFO, "Python script interrupted by user"); + CLog::Log(LOGINFO, "Python interpreter interrupted by user"); else - CLog::Log(LOGINFO, "Python script stopped"); + CLog::Log(LOGINFO, "Python interpreter stopped"); it->bDone = true; } ++it;
View file
_service:download_files:master.tar.gz/xbmc/interfaces/python/XBPython.h
Changed
@@ -91,7 +91,7 @@ bool OnScriptInitialized(ILanguageInvoker *invoker) override; void OnScriptStarted(ILanguageInvoker *invoker) override; void OnScriptAbortRequested(ILanguageInvoker *invoker) override; - void OnScriptEnded(ILanguageInvoker* invoker) override; + void OnExecutionEnded(ILanguageInvoker* invoker) override; void OnScriptFinalized(ILanguageInvoker *invoker) override; ILanguageInvoker* CreateInvoker() override;
View file
_service:download_files:master.tar.gz/xbmc/music/MusicDatabase.cpp
Changed
@@ -4849,6 +4849,14 @@ return false; } +int CMusicDatabase::GetDiscsCount(const std::string& baseDir, const Filter& filter /* = Filter() */) +{ + int iDiscTotal = -1; + CFileItemList itemscount; + if (GetDiscsByWhere(baseDir, filter, itemscount, SortDescription(), true)) + iDiscTotal = itemscount.GetProperty("total").asInteger(); + return iDiscTotal; +} bool CMusicDatabase::GetSongsFullByWhere(const std::string &baseDir, const Filter &filter, CFileItemList &items, const SortDescription &sortDescription /* = SortDescription() */, bool artistData /* = false*/) {
View file
_service:download_files:master.tar.gz/xbmc/music/MusicDatabase.h
Changed
@@ -478,6 +478,7 @@ const SortDescription& sortDescription = SortDescription(), bool countOnly = false); bool GetArtistsByWhere(const std::string& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription(), bool countOnly = false); + int GetDiscsCount(const std::string& baseDir, const Filter& filter = Filter()); int GetSongsCount(const Filter &filter = Filter()); bool GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription &sorting) override;
View file
_service:download_files:master.tar.gz/xbmc/music/windows/GUIWindowMusicNav.cpp
Changed
@@ -371,58 +371,7 @@ if (strDirectory.empty()) AddSearchFolder(); - bool bResult = false; - if (URIUtils::IsMusicDb(strDirectory)) - { - XFILE::CMusicDatabaseDirectory dir; - CQueryParams params; - MUSICDATABASEDIRECTORY::NODE_TYPE type; - MUSICDATABASEDIRECTORY::NODE_TYPE childtype; - dir.GetDirectoryNodeInfo(strDirectory, type, childtype, params); - - //Control navigation from albums to discs or directly to songs - if (childtype == NODE_TYPE_DISC) - { - bool bFlatten = false; - - if (params.GetAlbumId() < 0) - bFlatten = true; // Showing *all albums next always songs - else - { - // Option to show discs for ordinary albums (not just boxed sets) - bFlatten = !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool( - CSettings::SETTING_MUSICLIBRARY_SHOWDISCS); - CMusicDatabase musicdatabase; - if (musicdatabase.Open()) - { - if (bFlatten) // Check for boxed set - bFlatten = !musicdatabase.IsAlbumBoxset(params.GetAlbumId()); - if (!bFlatten) - { // Check we will get more than 1 disc when filter applied - int iDiscTotal = -1; - if (musicdatabase.GetDiscsByWhere(strDirectory, CDatabase::Filter(), items, - SortDescription(), true)) - iDiscTotal = items.GetProperty("total").asInteger(); - bFlatten = iDiscTotal <= 1; - } - } - musicdatabase.Close(); - } - if (bFlatten) - { // Skip discs level and show songs - CMusicDbUrl musicUrl; - if (!musicUrl.FromString(strDirectory)) - return false; - - musicUrl.AppendPath("-2/"); // Flattened so adjust list label etc. - bResult = CGUIWindowMusicBase::GetDirectory(musicUrl.ToString(), items); - } - } - } - - if (!bResult) - bResult = CGUIWindowMusicBase::GetDirectory(strDirectory, items); - + bool bResult = CGUIWindowMusicBase::GetDirectory(strDirectory, items); if (bResult) { if (items.IsPlayList())
View file
_service:download_files:master.tar.gz/xbmc/platform/freebsd/CMakeLists.txt
Changed
@@ -1,9 +1,11 @@ set(SOURCES CPUInfoFreebsd.cpp + OptionalsReg.cpp ../linux/OptionalsReg.cpp ../linux/TimeUtils.cpp MemUtils.cpp) set(HEADERS CPUInfoFreebsd.h + OptionalsReg.h ../linux/OptionalsReg.h ../linux/PlatformConstants.h ../linux/TimeUtils.h)
View file
_service:download_files:master.tar.gz/xbmc/platform/freebsd/OptionalsReg.cpp
Added
@@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "OptionalsReg.h" + + +//----------------------------------------------------------------------------- +// OSS +//----------------------------------------------------------------------------- + +#ifdef HAS_OSS +#include "cores/AudioEngine/Sinks/AESinkOSS.h" +bool OPTIONALS::OSSRegister() +{ + CAESinkOSS::Register(); + return true; +} +#endif
View file
_service:download_files:master.tar.gz/xbmc/platform/freebsd/OptionalsReg.h
Added
@@ -0,0 +1,18 @@ +/* + * Copyright (C) 2005-2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +//----------------------------------------------------------------------------- +// OSS +//----------------------------------------------------------------------------- + +namespace OPTIONALS +{ +bool OSSRegister(); +}
View file
_service:download_files:master.tar.gz/xbmc/platform/linux/OptionalsReg.cpp
Changed
@@ -27,10 +27,16 @@ } #endif +//----------------------------------------------------------------------------- +// OSS +//----------------------------------------------------------------------------- + +#ifdef TARGET_LINUX bool OPTIONALS::OSSRegister() { return false; } +#endif //----------------------------------------------------------------------------- // PulseAudio
View file
_service:download_files:master.tar.gz/xbmc/platform/linux/OptionalsReg.h
Changed
@@ -30,10 +30,12 @@ // OSS //----------------------------------------------------------------------------- +#ifdef TARGET_LINUX namespace OPTIONALS { bool OSSRegister(); } +#endif //----------------------------------------------------------------------------- // sndio
View file
_service:download_files:master.tar.gz/xbmc/profiles/ProfileManager.cpp
Changed
@@ -187,6 +187,15 @@ ret = false; } } + if (!ret) + { + CLog::Log(LOGERROR, + "Failed to load profile - might be corrupted - falling back to master profile"); + m_profiles.clear(); + CFile::Delete(file); + + ret = true; + } if (m_profiles.empty()) { // add the master user
View file
_service:download_files:master.tar.gz/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
Changed
@@ -31,6 +31,7 @@ #include "utils/Variant.h" #include "utils/log.h" +#include <cassert> #include <utility> #define SETTING_PROFILE_NAME "profile.name"
View file
_service:download_files:master.tar.gz/xbmc/pvr/filesystem/PVRGUIDirectory.cpp
Changed
@@ -202,6 +202,7 @@ item->SetLabel(strCurrent); item->SetLabelPreformatted(true); item->m_dateTime = recording->RecordingTimeAsLocalTime(); + item->SetProperty("unwatchedepisodes", 0); // Assume all folders are watched, we'll change the overlay later item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_WATCHED, false); @@ -215,7 +216,10 @@ } if (recording->GetPlayCount() == 0) + { unwatchedFolders.insert(item); + item->IncrementProperty("unwatchedepisodes", 1); + } } // Change the watched overlay to unwatched for folders containing unwatched entries
View file
_service:download_files:master.tar.gz/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
Changed
@@ -27,6 +27,7 @@ #include "settings/SettingsComponent.h" #include "threads/SingleLock.h" #include "utils/URIUtils.h" +#include "video/VideoLibraryQueue.h" #include "video/windows/GUIWindowVideoNav.h" #include <memory> @@ -99,6 +100,24 @@ return true; } } + else if (action.GetID() == ACTION_TOGGLE_WATCHED) + { + const std::shared_ptr<CFileItem> pItem = m_vecItems->Get(m_viewControl.GetSelectedItem()); + if (!pItem || pItem->IsParentFolder()) + return false; + + bool bUnWatched = false; + if (pItem->HasPVRRecordingInfoTag()) + bUnWatched = pItem->GetPVRRecordingInfoTag()->GetPlayCount() == 0; + else if (pItem->m_bIsFolder) + bUnWatched = pItem->GetProperty("unwatchedepisodes").asInteger() > 0; + else + return false; + + CVideoLibraryQueue::GetInstance().MarkAsWatched(pItem, bUnWatched); + return true; + } + return CGUIWindowPVRBase::OnAction(action); }
View file
_service:download_files:master.tar.gz/xbmc/settings/lib/Setting.cpp
Changed
@@ -851,6 +851,16 @@ entry.second = strtol(optionElement->FirstChild()->Value(), nullptr, 10); m_translatableOptions.push_back(entry); } + else + { + std::string label; + if (optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label) == + TIXML_SUCCESS) + { + int value = strtol(optionElement->FirstChild()->Value(), nullptr, 10); + m_options.emplace_back(label, value); + } + } optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION); }
View file
_service:download_files:master.tar.gz/xbmc/settings/windows/GUIControlSettings.cpp
Changed
@@ -36,6 +36,7 @@ #include "settings/lib/Setting.h" #include "settings/lib/SettingDefinitions.h" #include "storage/MediaManager.h" +#include "utils/FileExtensionProvider.h" #include "utils/StringUtils.h" #include "utils/Variant.h" #include "utils/log.h" @@ -887,7 +888,20 @@ shares, pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider()), heading, path, control->UseImageThumbs(), control->UseFileDirectories()); else if (control->GetFormat() == "image") - result = CGUIDialogFileBrowser::ShowAndGetImage(shares, heading, path); + { + /* Check setting contains own masking, to filter required image type. + * e.g. png only needed + * <constraints> + * <masking>*.png</masking> + * </constraints> + * <control type="button" format="image"> + * ... + */ + std::string ext = pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider()); + if (ext.empty()) + ext = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(); + result = CGUIDialogFileBrowser::ShowAndGetFile(shares, ext, heading, path, true); + } else result = CGUIDialogFileBrowser::ShowAndGetDirectory(shares, heading, path, pathSetting->Writable());
View file
_service:download_files:master.tar.gz/xbmc/utils/XBMCTinyXML.cpp
Changed
@@ -111,7 +111,11 @@ { TiXmlPrinter printer; Accept(&printer); - return file.Write(printer.CStr(), printer.Size()) == static_cast<ssize_t>(printer.Size()); + bool suc = file.Write(printer.CStr(), printer.Size()) == static_cast<ssize_t>(printer.Size()); + if (suc) + file.Flush(); + + return suc; } return false; }
View file
_service:download_files:master.tar.gz/xbmc/windowing/GraphicContext.cpp
Changed
@@ -25,6 +25,8 @@ #include "settings/lib/Setting.h" #include "utils/log.h" +#include <cassert> + using namespace KODI::MESSAGING; CGraphicContext::CGraphicContext(void) = default;
View file
_service:download_files:master.tar.gz/xbmc/windowing/X11/WinSystemX11GLContext.cpp
Changed
@@ -26,6 +26,7 @@ #include "utils/log.h" #include "windowing/GraphicContext.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include <vector>
View file
_service:download_files:master.tar.gz/xbmc/windowing/X11/WinSystemX11GLContext.h
Changed
@@ -11,6 +11,7 @@ #include "WinSystemX11.h" #include "rendering/gl/RenderSystemGL.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include <memory>
View file
_service:download_files:master.tar.gz/xbmc/windowing/gbm/WinSystemGbm.cpp
Changed
@@ -23,6 +23,7 @@ #include "utils/log.h" #include "windowing/GraphicContext.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include "platform/linux/powermanagement/LinuxPowerSyscall.h"
View file
_service:download_files:master.tar.gz/xbmc/windowing/gbm/WinSystemGbm.h
Changed
@@ -13,6 +13,7 @@ #include "threads/CriticalSection.h" #include "windowing/WinSystem.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include "platform/linux/input/LibInputHandler.h"
View file
_service:download_files:master.tar.gz/xbmc/windowing/osx/WinSystemOSX.mm
Changed
@@ -1540,7 +1540,8 @@ { std::string appName; if (CDarwinUtils::CFStringRefToUTF8String(ownerName, appName)) - CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str()); + CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures Kodi!", + appName.c_str()); obscureLogged = true; } m_obscured = true;
View file
_service:download_files:master.tar.gz/xbmc/windowing/rpi/WinSystemRpi.h
Changed
@@ -14,6 +14,7 @@ #include "threads/SystemClock.h" #include "windowing/WinSystem.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include "platform/linux/input/LibInputHandler.h"
View file
_service:download_files:master.tar.gz/xbmc/windowing/wayland/WinSystemWayland.cpp
Changed
@@ -21,6 +21,7 @@ #include "input/InputManager.h" #include "input/touch/generic/GenericTouchActionHandler.h" #include "input/touch/generic/GenericTouchInputHandler.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/powermanagement/LinuxPowerSyscall.h" #include "platform/linux/OptionalsReg.h" #include "platform/linux/PlatformConstants.h"
View file
_service:download_files:master.tar.gz/xbmc/windowing/wayland/WinSystemWayland.h
Changed
@@ -20,6 +20,7 @@ #include "utils/ActorProtocol.h" #include "windowing/WinSystem.h" +#include "platform/freebsd/OptionalsReg.h" #include "platform/linux/OptionalsReg.h" #include <atomic>
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.