Possible Design of App Store-like Plugin Manager
Note: you can view this passage in a better format at Github page
Hi! I'm a GSoC student candidate. These days I've been investigating the idea of App Store-like Plugin Manager. Briefly speaking, it would be a new plugin manager, from which it is possible to discover, (un-)install, (auto-)update plugins in an easy manner.
Now, I've worked out a rather complete feature list, corresponding UI demos, and some discussion of implementation approach. All of them are listed below.
Any feedback from anyone is much appreciated.
Overview of Design
A new app-store like Plugin Manager is planned to be implemented. This tool can reside in Resource Manager(find it in MuseScore menu: Preferences->General->Update translations, or Help->Resource Manager) as a new tab, or just replace the old plugin manager.
In short, two main facilities are covered in this new plugin manager:
- (Part I) Automatically install and manage plugin packages from MuseScore plugin repository.
- (Part II) Traditional facilities for local installed plugins(some of those plugins may be not published online).
> Here I refer to the stuff you download from the plugin repository as a "plugin package".
> I want to stress the difference between a plugin package and a qml file, since one plugin package(such as this one) may contain multiple qml files.
> In light of this, one qml plugin can have two meaningful names: the first is base name of the qml file(corresponding to one particular item in MuseScore Plugin's drop-down menu), and the second is the title from the web plugin page, i.e., the name of corresponding plugin package.
Conceptual UI demo I made by merely modifying the UI file:
All available plugins from the online plugin repository will be displayed in this table.
you can search for new plugin packages and filter them by category labels.
you can download new plugin packages and install them simply by clicking the "Install" button.
> For plugins that are incompatible for your MuseScore version, the manager should disable the install button.
For downloaded plugin packages, you can enable, disable or delete them by clicking corresponding buttons.
> These operation applies to all qmls that belong to the plugin package.
> In Part II, there are operations that apply to one single qml.
Conceptual UI demo I made by merely modifying the UI file:
All installed plugins, at least for qmls that you manually added, will be displayed in the left QListWidget.
The text of each item in QListWidget is currently base name of the qml file.
In addition to using Part I's facility, you can still manually download the qml file yourself or write your own plugin qmls locally, and copy them to plugin directory. Then they will appear after reloading.
If local plugins are checked to be incompatible, necessary prompts can be added.
The traditional plugin manager's facilities will be reserved in this tab, including:
- enabling/disabling each qml
- shortcut configuration
- displaying name, path, version and description.
These facilities are arranged as before in the right panel.
For plugin packages installed from repository, there're several choices for displaying:
- Just don't display them in this tab. Let users manage them completely in the first tab(Part I).
Then facilities like shortcut configuration are lost.
One plugin package corresponds to one item in the left QListWidget.
Like local plugins, one plugin qml corresponds to one item in the left QListWidget.
Personally I prefer the third choice, as it's flexible for users and relative simpler than the second choice.
But the second choice is also considerable if we implement a tree view, displaying all qmls of one package under that package item.
To make things consistent, Add "Check for new version of MuseScore plugins" in MuseScore->Preferences -> Update.
And add corresponding facility of update reminder.
Fetching from Web
When we launch the resource manager, the crawler should fetch a list of available plugin packages from
The map between MuseScore version and
> It's not hard to parse the raw HTML table using some libraries, though the parsing code would be simpler if there were a JSON file.
Each entry of the plugin list should contain:
> The title here should use the title from the plugin page, i.e., the name of plugin package.
API Compatibility(a tuple of
boolindicating compatibilities for 3 versions)
URL of the plugin page(string)
vectorof enum object)
Then when we click on
Install button of any entry of plugin, the crawler will fetch the plugin page and extract structured data, including:
- GitHub repo URL(if applicable)
- Direct links that are recognized to be suitable plugin distributions
- Attachment URLs that are recognized to be suitable plugin distributions
> This fetch procedure can also be considered to run automagically in advance and store in cache, as the total number of plugins is not huge.
If GitHub repo URL is available, we should use Github Release APIs to check if there's a release.
Else, look for direct links in the page or the attachments.
These steps would require sophisticated pattern recognizing algorithms to choose the correct version(2.x or 3.x, etc. ), which can be further discussed and optimized later.
Extract and Install
There are various types of downloaded plugin files. Some are zips, and others are just qmls.
> There are some code snippets used for unzipping language packages, which can be used similarly for plugin packages.
After the zips are extracted, we should remove irrelevant files such as README and others, and copy all qml files and folders that contain them to the plugin directory.
Warnings should be reported if there are conflicts when copying files, such as qml files with the same name already exist.
Maintaining the Local Plugin List
Not only the downloaded qmls are stored, necessary information about local plugins and plugin packages need to be stored.
Currently, MuseScore only stores some information for local plugins in
plugins.xml(located in C:\[your username]\AppData\Local\MuseScore\MuseScore3). The xml file only stores the qml file path and flag of whether loaded for each local plugin. After MuseScore is launched, these items will be read into
QList _pluginList in class
PluginManager. (See here)
Qml files of plugin packages should also be added into
plugins.xml. But beyond that, for each plugin package downloaded from repository, additional information should be maintained:
plugin package name or page URL. These are helpful to identify installed plugin package from the plugin list fetched from repository. (We cannot tell it merely from installed qml files.)
Paths of its qml files. This can be obtained after download/extracting the zip file. Those paths are useful when checking the integrity of local plugin packages and removing packages.
These information can be saved in a separate xml file.
Runtime Data Structure
Currently MuseScore uses
QList PluginManager::_pluginList to maintain local plugins. When MuseScore launches, it fills this QList with contents from
plugins.xml, and loads and registers plugins that are marked to load.
For plugin packages, there ought to be a new
QList to maintain installed plugin packages.
PluginPackage will be a structure that contains package identiity(name or URL), and an array of paths of its qml files.
This QList is also filled with contents from corresponding xml file. When the plugin manager is launched, integrities of each
PluginPackage are checked by verifying existence of each qml file.
Each plugin has its API compatibility specified in its web page.
As far as I know, plugins of compatibility 1.x, 2.x and 3.x can only be run on MuseScore 1.x, 2.x and 3.x respectively. Though some of 2.x plugins can be ported to 3.x with some code changes, this process cannot be done automagically yet.
Compatibility check should happen in two cases:
When installing plugins from repository, before the download begins, compatibility list specified in the plugin web page should be verified against current MuseScore version. If current MuseScore version is not in the list, download will be disabled.
When importing/reloading local plugins, check whether the plugin is imported successfully.
this can be done by analyzing the result of
QQmlComponent::create(). See example code, where the
errors()method contains related info.
Most plugins don't have their own version numbers currently. So how to detect updates of plugins seems to be a tough problem.
A stupid way is to download the whole plugin again and look for difference.