1 /* LibraryPluginHandler.cpp */
2 
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4  *
5  * This file is part of sayonara player
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "LibraryPluginHandler.h"
22 #include "Components/LibraryManagement/AbstractLibraryContainer.h"
23 
24 #include "Utils/Utils.h"
25 #include "Utils/Algorithm.h"
26 #include "Utils/Settings/Settings.h"
27 #include "Utils/Logger/Logger.h"
28 
29 #include <QDir>
30 #include <QPluginLoader>
31 #include <QJsonObject>
32 #include <QVariantMap>
33 #include <QWidget>
34 
35 using Library::PluginHandler;
36 using Library::AbstractContainer;
37 
38 using ContainerList=QList<AbstractContainer*>;
39 
40 namespace Algorithm=Util::Algorithm;
41 
42 struct PluginHandler::Private
43 {
44 	AbstractContainer*			emptyLibrary=nullptr;
45 	AbstractContainer*			currentLibrary=nullptr;
46 	ContainerList		libraryContainers;
47 
find_libraryPluginHandler::Private48 	AbstractContainer* find_library(const QString& name)
49 	{
50 		spLog(Log::Debug, this) << "Searching for Library " << name;
51 
52 		auto it = Algorithm::find(libraryContainers, [&name](AbstractContainer* c){
53 			return (c->name() == name);
54 		});
55 
56 		if (it != libraryContainers.end())
57 		{
58 			return *it;
59 		}
60 
61 		return nullptr;
62 	}
63 };
64 
convert_display_name(const QString & display_name)65 static QString convert_display_name(const QString& display_name)
66 {
67 	QString ret = display_name.toLower().trimmed();
68 	ret.replace(" ", "-");
69 
70 	return ret;
71 }
72 
73 
74 /*************************************************************************/
75 
PluginHandler()76 PluginHandler::PluginHandler() :
77 	QObject(nullptr)
78 {
79 	m = Pimpl::make<Private>();
80 }
81 
82 PluginHandler::~PluginHandler() = default;
83 
init(const ContainerList & containers,AbstractContainer * fallback_library)84 void PluginHandler::init(const ContainerList& containers, AbstractContainer* fallback_library)
85 {
86 	m->emptyLibrary = fallback_library;
87 
88 	QString last_library = GetSetting(Set::Lib_CurPlugin);
89 	initLibraries(containers);
90 	initDllLibraries();
91 
92 	{ // First startup handling
93 		bool has_local_library = Util::Algorithm::contains(m->libraryContainers, [](AbstractContainer* container){
94 			return container->isLocal();
95 		});
96 
97 		AbstractContainer* c = m->find_library(last_library);
98 		if(!has_local_library && (c == nullptr))
99 		{
100 			setCurrentLibrary(-1);
101 		}
102 
103 		else
104 		{
105 			setCurrentLibrary(last_library);
106 		}
107 	}
108 }
109 
shutdown()110 void PluginHandler::shutdown()
111 {
112 	for(const auto& container : m->libraryContainers)
113 	{
114 		delete container;
115 	}
116 
117 	m->libraryContainers.clear();
118 }
119 
initLibraries(const QList<Library::AbstractContainer * > & containers)120 void PluginHandler::initLibraries(const QList<Library::AbstractContainer*>& containers)
121 {
122 	for(AbstractContainer* container : containers)
123 	{
124 		if(!container) {
125 			continue;
126 		}
127 
128 		spLog(Log::Debug, this) << "Add library " << container->displayName();
129 
130 		m->libraryContainers << container;
131 	}
132 }
133 
initDllLibraries()134 void PluginHandler::initDllLibraries()
135 {
136 //	QDir plugin_dir = QDir(Util::libPath());
137 //	QStringList dll_filenames = plugin_dir.entryList(QDir::Files);
138 
139 //	for(const QString& filename : dll_filenames)
140 //	{
141 //		QString absolute_path = plugin_dir.absoluteFilePath(filename);
142 //		QPluginLoader loader(absolute_path);
143 //		QJsonObject metadata = loader.metaData();
144 //		QVariantMap map = metadata.toVariantMap();
145 //		const auto keys = map.keys();
146 
147 //		bool has_correct_iid = false;
148 //		for(const QString& key : keys)
149 //		{
150 //			if(key.trimmed() != "IID"){
151 //				continue;
152 //			}
153 
154 //			QString value = map[key].toString();
155 
156 //			if(value.startsWith("com.sayonara-player.apiv2."))
157 //			{
158 //				spLog(Log::Debug, this) << "Have found valid plugin with iid = " << value;
159 //				has_correct_iid = true;
160 //			}
161 
162 //			else if(value.startsWith("com.sayonara-player."))
163 //			{
164 //				spLog(Log::Info, this) << "Ignoring *outdated* plugin with iid = " << value << " at " << absolute_path;
165 //				spLog(Log::Info, this) << "You can delete this file";
166 //			}
167 
168 //			else
169 //			{
170 //				spLog(Log::Warning, this) << "Ignoring *invalid* plugin with iid = " << value << " at " << absolute_path;
171 //			}
172 
173 //			break;
174 //		}
175 
176 //		if(!has_correct_iid)
177 //		{
178 //			continue;
179 //		}
180 
181 //		QObject* raw_plugin = loader.instance();
182 //		if(!raw_plugin)
183 //		{
184 //			spLog(Log::Warning, this) << "Cannot load plugin: " << filename << ": " << loader.errorString();
185 //			loader.unload();
186 //			continue;
187 //		}
188 
189 //		AbstractContainer* container = dynamic_cast<AbstractContainer*>(raw_plugin);
190 //		if(!container)
191 //		{
192 //			loader.unload();
193 //			continue;
194 //		}
195 
196 //		spLog(Log::Info, this) << "Found library plugin " << container->displayName();
197 
198 //		m->libraryContainers << container;
199 //	}
200 }
201 
setCurrentLibrary(const QString & name)202 void PluginHandler::setCurrentLibrary(const QString& name)
203 {
204 	setCurrentLibrary( m->find_library(name) );
205 }
206 
setCurrentLibrary(int index)207 void PluginHandler::setCurrentLibrary(int index)
208 {
209 	AbstractContainer* ret = m->emptyLibrary;
210 
211 	if(!m->libraryContainers.isEmpty() && index >= 0)
212 	{
213 		index = std::min(index, m->libraryContainers.size() - 1);
214 		ret = m->libraryContainers[index];
215 	}
216 
217 	setCurrentLibrary(ret);
218 }
219 
setCurrentLibrary(AbstractContainer * cur_library)220 void PluginHandler::setCurrentLibrary(AbstractContainer* cur_library)
221 {
222 	if(!cur_library)
223 	{
224 		if(m->libraryContainers.isEmpty())
225 		{
226 			cur_library	= m->emptyLibrary;
227 		}
228 
229 		else {
230 			cur_library = m->libraryContainers.first();
231 		}
232 	}
233 
234 	m->currentLibrary = cur_library;
235 
236 	if(m->currentLibrary)
237 	{
238 		m->currentLibrary->init();
239 	}
240 
241 	SetSetting(Set::Lib_CurPlugin, cur_library->name());
242 
243 	emit sigCurrentLibraryChanged();
244 }
245 
currentLibrary() const246 AbstractContainer* PluginHandler::currentLibrary() const
247 {
248 	return m->currentLibrary;
249 }
250 
currentLibraryWidget() const251 QWidget* PluginHandler::currentLibraryWidget() const
252 {
253 	if(!m->currentLibrary){
254 		return nullptr;
255 	}
256 
257 	return m->currentLibrary->widget();
258 }
259 
addLocalLibrary(Library::AbstractContainer * container)260 void PluginHandler::addLocalLibrary(Library::AbstractContainer* container)
261 {
262 	if(container == nullptr) {
263 		return;
264 	}
265 
266 	int idx = Algorithm::indexOf(m->libraryContainers, [=](AbstractContainer* c){
267 		return (c->isLocal() == false && c != m->emptyLibrary);
268 	});
269 
270 	idx = std::max(idx, 0);
271 	m->libraryContainers.insert(idx, container);
272 
273 	emit sigLibrariesChanged();
274 
275 	setCurrentLibrary(idx);
276 }
277 
renameLocalLibrary(const QString & old_name,const QString & new_name)278 void PluginHandler::renameLocalLibrary(const QString& old_name, const QString& new_name)
279 {
280 	AbstractContainer* c = m->find_library(convert_display_name(old_name));
281 	if(c && c->isLocal())
282 	{
283 		c->rename(new_name);
284 		emit sigLibrariesChanged();
285 	}
286 }
287 
removeLocalLibrary(const QString & name)288 void PluginHandler::removeLocalLibrary(const QString& name)
289 {
290 	AbstractContainer* c = m->find_library(convert_display_name(name));
291 	if(c && c->isLocal())
292 	{
293 		m->libraryContainers.removeAll(c);
294 
295 		if(m->currentLibrary == c)
296 		{
297 			setCurrentLibrary(0);
298 		}
299 
300 		emit sigLibrariesChanged();
301 	}
302 }
303 
moveLocalLibrary(int old_index,int new_index)304 void PluginHandler::moveLocalLibrary(int old_index, int new_index)
305 {
306 	// first index is empty library
307 	if( !Util::between(old_index, m->libraryContainers) ||
308 		!Util::between(new_index, m->libraryContainers))
309 	{
310 		return;
311 	}
312 
313 	m->libraryContainers.move(old_index, new_index);
314 
315 	emit sigLibrariesChanged();
316 }
317 
libraries(bool also_empty) const318 QList<Library::AbstractContainer*> PluginHandler::libraries(bool also_empty) const
319 {
320 	QList<AbstractContainer*> containers = m->libraryContainers;
321 	if(also_empty) {
322 		containers.push_front(m->emptyLibrary);
323 	}
324 
325 	return containers;
326 }
327