1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "FileExtensionProvider.h"
10
11 #include "ServiceBroker.h"
12 #include "addons/AddonManager.h"
13 #include "addons/AudioDecoder.h"
14 #include "settings/AdvancedSettings.h"
15 #include "settings/SettingsComponent.h"
16 #include "utils/URIUtils.h"
17
18 #include <string>
19 #include <vector>
20
21 using namespace ADDON;
22
23 const std::vector<TYPE> ADDON_TYPES = {
24 ADDON_VFS,
25 ADDON_IMAGEDECODER,
26 ADDON_AUDIODECODER
27 };
28
CFileExtensionProvider(ADDON::CAddonMgr & addonManager)29 CFileExtensionProvider::CFileExtensionProvider(ADDON::CAddonMgr& addonManager)
30 : m_advancedSettings(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()),
31 m_addonManager(addonManager)
32 {
33 SetAddonExtensions();
34
35 m_addonManager.Events().Subscribe(this, &CFileExtensionProvider::OnAddonEvent);
36 }
37
~CFileExtensionProvider()38 CFileExtensionProvider::~CFileExtensionProvider()
39 {
40 m_addonManager.Events().Unsubscribe(this);
41
42 m_advancedSettings.reset();
43 m_addonExtensions.clear();
44 }
45
GetDiscStubExtensions() const46 std::string CFileExtensionProvider::GetDiscStubExtensions() const
47 {
48 return m_advancedSettings->m_discStubExtensions;
49 }
50
GetMusicExtensions() const51 std::string CFileExtensionProvider::GetMusicExtensions() const
52 {
53 std::string extensions(m_advancedSettings->m_musicExtensions);
54 extensions += '|' + GetAddonExtensions(ADDON_VFS);
55 extensions += '|' + GetAddonExtensions(ADDON_AUDIODECODER);
56
57 return extensions;
58 }
59
GetPictureExtensions() const60 std::string CFileExtensionProvider::GetPictureExtensions() const
61 {
62 std::string extensions(m_advancedSettings->m_pictureExtensions);
63 extensions += '|' + GetAddonExtensions(ADDON_VFS);
64 extensions += '|' + GetAddonExtensions(ADDON_IMAGEDECODER);
65
66 return extensions;
67 }
68
GetSubtitleExtensions() const69 std::string CFileExtensionProvider::GetSubtitleExtensions() const
70 {
71 std::string extensions(m_advancedSettings->m_subtitlesExtensions);
72 extensions += '|' + GetAddonExtensions(ADDON_VFS);
73
74 return extensions;
75 }
76
GetVideoExtensions() const77 std::string CFileExtensionProvider::GetVideoExtensions() const
78 {
79 std::string extensions(m_advancedSettings->m_videoExtensions);
80 if (!extensions.empty())
81 extensions += '|';
82 extensions += GetAddonExtensions(ADDON_VFS);
83
84 return extensions;
85 }
86
GetFileFolderExtensions() const87 std::string CFileExtensionProvider::GetFileFolderExtensions() const
88 {
89 std::string extensions(GetAddonFileFolderExtensions(ADDON_VFS));
90 if (!extensions.empty())
91 extensions += '|';
92 extensions += GetAddonFileFolderExtensions(ADDON_AUDIODECODER);
93
94 return extensions;
95 }
96
CanOperateExtension(const std::string & path) const97 bool CFileExtensionProvider::CanOperateExtension(const std::string& path) const
98 {
99 /*!
100 * @todo Improve this function to support all cases and not only audio decoder
101 * with tracks inside.
102 */
103
104 // Get file extensions to find addon related to it.
105 std::string strExtension = URIUtils::GetExtension(path);
106 StringUtils::ToLower(strExtension);
107 if (!strExtension.empty() && CServiceBroker::IsBinaryAddonCacheUp())
108 {
109 std::vector<AddonInfoPtr> addonInfos;
110 m_addonManager.GetAddonInfos(addonInfos, true, ADDON_AUDIODECODER);
111 for (const auto& addonInfo : addonInfos)
112 {
113 if (CAudioDecoder::HasTracks(addonInfo))
114 {
115 const auto exts = StringUtils::Split(CAudioDecoder::GetExtensions(addonInfo), "|");
116 if (std::find(exts.begin(), exts.end(), strExtension) != exts.end())
117 {
118 /* Call addon to start a dir read about given file, if success, return
119 * as true.
120 */
121 CAudioDecoder result(addonInfo);
122 if (result.CreateDecoder() && result.ContainsFiles(CURL(path)))
123 return true;
124
125 /* If extension is supported and addon creation failed, we expect the
126 * file is not usable and return false here.
127 */
128 return false;
129 }
130 }
131 }
132
133 /*!
134 * We expect that VFS addons can support the file, and return true.
135 *
136 * @todo Check VFS addons can also be types in conflict with Kodi's
137 * supported parts!
138 */
139 return true;
140 }
141
142 /*!
143 * If no file extensions present, mark it as not supported.
144 */
145 return false;
146 }
147
GetAddonExtensions(const TYPE & type) const148 std::string CFileExtensionProvider::GetAddonExtensions(const TYPE &type) const
149 {
150 auto it = m_addonExtensions.find(type);
151 if (it != m_addonExtensions.end())
152 return it->second;
153
154 return "";
155 }
156
GetAddonFileFolderExtensions(const TYPE & type) const157 std::string CFileExtensionProvider::GetAddonFileFolderExtensions(const TYPE &type) const
158 {
159 auto it = m_addonExtensions.find(type);
160 if (it != m_addonExtensions.end())
161 return it->second;
162
163 return "";
164 }
165
SetAddonExtensions()166 void CFileExtensionProvider::SetAddonExtensions()
167 {
168 for (auto const type : ADDON_TYPES)
169 {
170 SetAddonExtensions(type);
171 }
172 }
173
SetAddonExtensions(const TYPE & type)174 void CFileExtensionProvider::SetAddonExtensions(const TYPE& type)
175 {
176 std::vector<std::string> extensions;
177 std::vector<std::string> fileFolderExtensions;
178 std::vector<AddonInfoPtr> addonInfos;
179 m_addonManager.GetAddonInfos(addonInfos, true, type);
180 for (const auto& addonInfo : addonInfos)
181 {
182 std::string info = ADDON_VFS == type ? "@extensions" : "@extension";
183 std::string ext = addonInfo->Type(type)->GetValue(info).asString();
184 if (!ext.empty())
185 {
186 extensions.push_back(ext);
187 if (type == ADDON_VFS || type == ADDON_AUDIODECODER)
188 {
189 std::string info2 = ADDON_VFS == type ? "@filedirectories" : "@tracks";
190 if (addonInfo->Type(type)->GetValue(info2).asBoolean())
191 fileFolderExtensions.push_back(ext);
192 }
193 if (type == ADDON_VFS)
194 {
195 if (addonInfo->Type(type)->GetValue("@encodedhostname").asBoolean())
196 {
197 std::string prot = addonInfo->Type(type)->GetValue("@protocols").asString();
198 auto prots = StringUtils::Split(prot, "|");
199 for (const std::string& it : prots)
200 m_encoded.push_back(it);
201 }
202 }
203 }
204 }
205
206 m_addonExtensions.insert(make_pair(type, StringUtils::Join(extensions, "|")));
207 if (!fileFolderExtensions.empty())
208 m_addonFileFolderExtensions.insert(make_pair(type, StringUtils::Join(fileFolderExtensions, "|")));
209 }
210
OnAddonEvent(const AddonEvent & event)211 void CFileExtensionProvider::OnAddonEvent(const AddonEvent& event)
212 {
213 if (typeid(event) == typeid(AddonEvents::Enabled) ||
214 typeid(event) == typeid(AddonEvents::Disabled) ||
215 typeid(event) == typeid(AddonEvents::ReInstalled))
216 {
217 for (auto &type : ADDON_TYPES)
218 {
219 if (m_addonManager.HasType(event.id, type))
220 {
221 SetAddonExtensions(type);
222 break;
223 }
224 }
225 }
226 else if (typeid(event) == typeid(AddonEvents::UnInstalled))
227 {
228 SetAddonExtensions();
229 }
230 }
231
EncodedHostName(const std::string & protocol) const232 bool CFileExtensionProvider::EncodedHostName(const std::string& protocol) const
233 {
234 return std::find(m_encoded.begin(),m_encoded.end(),protocol) != m_encoded.end();
235 }
236