Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project Feature - Base structure #209

Merged
merged 31 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b87378f
feat: add project package and basic ableton parsing logic
DropSnorz Oct 2, 2023
f7f2958
test: update junit to junit5 jupiter
DropSnorz Oct 8, 2023
997c597
feat: add projects base ui and persistence
DropSnorz Oct 9, 2023
c901d9b
feat: add project directory configuration
DropSnorz Oct 10, 2023
0d8889b
test: add ableton 11 schema 5 unit test
DropSnorz Oct 15, 2023
c15c8fe
feat: extract AU plugins from Ableton projects
DropSnorz Oct 15, 2023
4498492
feat: add project info view
DropSnorz Oct 16, 2023
74e5be4
feat: add project plugins table view
DropSnorz Oct 22, 2023
ad9cf10
feat: add plugin lookup task
DropSnorz Oct 23, 2023
3da4b91
fix: plugin lookup result displayed correctly in project table
DropSnorz Oct 25, 2023
7834c13
refactor: add classes to support different ableton live set format
DropSnorz Oct 25, 2023
a94b00d
feat: add project last modified date
DropSnorz Oct 27, 2023
ae4b2bf
feat: project plugin deduplication on sync
DropSnorz Oct 28, 2023
f12e44f
feat: add project createdAt field
DropSnorz Oct 29, 2023
d561a20
feat: open a project from owlplug ui
DropSnorz Oct 29, 2023
0e9ebb5
feat: add ableton project icon
DropSnorz Oct 30, 2023
d02c125
feat: display project with missing plugins
DropSnorz Oct 30, 2023
1c6dc8f
fix: project tree view display
DropSnorz Oct 31, 2023
7e16da7
feat: add project sync and plugin lookup task progress
DropSnorz Oct 31, 2023
d3c3e85
fix: cascade lookup delete on plugin removal
DropSnorz Nov 6, 2023
af3eb2c
feat: resolve plugins lookup using components
DropSnorz Nov 6, 2023
d11ea52
feat: filter projects by name using search text field
DropSnorz Nov 7, 2023
2f632d9
feat: add project format version
DropSnorz Nov 9, 2023
e166722
chore: update common-compress to 1.24.0
DropSnorz Nov 9, 2023
50ee894
feat: navigate to plugins view from project plugin
DropSnorz Nov 10, 2023
3753310
doc: update readme with project feature
DropSnorz Nov 12, 2023
aca5959
refactor: improve code quality
DropSnorz Nov 14, 2023
f41ee12
refactor: rename project-related classes
DropSnorz Nov 14, 2023
d41cbbe
refactor: rename lookup result to Found and Missing
DropSnorz Nov 14, 2023
a1b2498
fix: project property name and import issues
DropSnorz Nov 16, 2023
7c54df1
feat: lookup daw plugins after plugin sync task
DropSnorz Nov 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/e6b8ee875daa4f74b5bf1cc8fee6df63)](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=DropSnorz/OwlPlug&utm_campaign=Badge_Grade_Dashboard)
![Last Pre release](https://img.shields.io/github/release-date/dropsnorz/owlplug.svg)
[![Discord](https://img.shields.io/badge/chat-on%20discord-%237289DA.svg)](https://discord.gg/nEdHAMB)
[![Patreon](https://img.shields.io/badge/donate-%E2%99%A5-%23253b80)](https://www.paypal.com/donate?hosted_button_id=7MJGDTQXAPJ22)
[![Donate](https://img.shields.io/badge/donate-%E2%99%A5-%23253b80)](https://www.paypal.com/donate?hosted_button_id=7MJGDTQXAPJ22)


# Overview
Expand Down Expand Up @@ -42,7 +42,7 @@ All kinds of feedbacks are greatly welcomed.
1. Browse binaries from [the release section](http://github.com/dropsnorz/owlplug/releases)
2. Download and run the OwlPlug installer for your platform
* `.msi` installer on Windows
* `.dmg` file on MacOS
* `.dmg` file on macOS
* `.deb` file on Linux
3. Run OwlPlug application

Expand All @@ -63,16 +63,22 @@ winget install owlplug

# Features

## Plugins
## Plugins discovery

OwlPlug can discover VST2, VST3 and AU Plugins. OwlPlug is compatible with all previously installed plugins as long as they are all in a specific root directory, for example `C:/AudioPlugins`. Additional directories can be configured if your plugin setup is fragmented on the filesystem.

After downloading Owlplug, you can still organize (add, move, delete, ...) your plugins with a file explorer or with your favorite DAW without breaking anything.

## Links
## Links creation

A Link allows you to create and manage symlinks across your filesystem and plugin directories. With Links, you can access directories anywhere on your filesystem (Hard drive, USB keys, custom directories...) through a single root plugin directory. For example, you can configure a link named *usb-drive* in `C:/AudioPlugins` to target your usb hard drive `D:/myPlugins`. All plugins in `D:/myPlugins` will be accessible using `C:/AudioPlugins/usb-drive`. This feature may be useful for DAW that scans plugins from predefined or limited number directories. On some Windows version, symlinks creation may require admin privileges.

## DAW Projects analysis

OwlPlug can scan DAW projects to extract referenced plugins. Plugins references in project files are compared to plugins installed in configured directories. This way, users can quickly identify missing plugins required to open projects.

The list of compatible DAWs is available in the [documentation](https://github.com/DropSnorz/OwlPlug/wiki/Projects-and-DAW-Support)

## Explore and download Plugins

OwlPlug can be connected to several remote sources to download plugins. A Remote Source is a collection of downloadable plugins that can be installed locally. OwlPlug can be configured to use any compatible third-party store in *Explore* Tab > *Sources* > *Add a new source...*.
Expand All @@ -95,7 +101,7 @@ Studiorack registry plugins for OwlPlug.

### How to distribute my plugins on OwlPlug ?

* **(Recomended)** Distribute your plugins using on the official OwlPlug Registry. You can find more information on how to proceed in the [registry github repository](https://github.com/OwlPlug/owlplug-registry).
* **(Recommended)** Distribute your plugins using on the official OwlPlug Registry. You can find more information on how to proceed in the [registry github repository](https://github.com/OwlPlug/owlplug-registry).

* Setup and host a custom remote source to distribute multiple plugins, following the registry specification.

Expand All @@ -107,7 +113,7 @@ Studiorack registry plugins for OwlPlug.
## Stack

* Spring boot
* JavaFx & JFoenix
* JavaFx
* Hibernate & H2
* Maven
* Juce
Expand Down
6 changes: 3 additions & 3 deletions owlplug-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.22</version>
<version>1.24.0</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@

package com.owlplug.core.components;

import com.owlplug.core.model.Plugin;
import com.owlplug.core.model.PluginFormat;
import com.owlplug.core.model.platform.OperatingSystem;
import com.owlplug.core.model.platform.RuntimePlatform;
import com.owlplug.core.model.platform.RuntimePlatformResolver;
import com.owlplug.core.utils.FileUtils;
import com.owlplug.explore.model.RemotePackage;
import com.owlplug.project.model.DawApplication;
import java.io.File;
import javafx.scene.image.Image;
import org.slf4j.Logger;
Expand Down Expand Up @@ -75,7 +75,13 @@ public class ApplicationDefaults {
public final Image studiorackLogoSmall = new Image(
ApplicationDefaults.class.getResourceAsStream("/media/studiorack-logo-16.png"));

public final Image abletonLogoImage = new Image(getClass().getResourceAsStream("/icons/ableton-white-16.png"));

public final Image errorIconImage = new Image(
getClass().getResourceAsStream("/icons/error-red-16.png"));

public final Image linkIconImage = new Image(
getClass().getResourceAsStream("/icons/link-grey-16.png"));
public final Image pluginPlaceholderImage = new Image(
getClass().getResourceAsStream("/media/plugin-placeholder.png"));
// CHECKSTYLE:ON
Expand Down Expand Up @@ -103,6 +109,7 @@ public class ApplicationDefaults {
public static final String FIRST_LAUNCH_KEY = "FIRST_LAUNCH_KEY";
public static final String APPLICATION_STATE_KEY = "APPLICATION_STATE_KEY";
public static final String SHOW_DIALOG_DISABLE_PLUGIN_KEY = "SHOW_DIALOG_DISABLE_PLUGIN_KEY";
public static final String PROJECT_DIRECTORY_KEY = "PROJECT_DIRECTORY_KEY";

/**
* Creates a new ApplicationDefaults.
Expand All @@ -122,12 +129,12 @@ public RuntimePlatform getRuntimePlatform() {
/**
* Returns plugin icon based on plugin format.
*
* @param plugin - plugin
* @param format - plugin format
* @return Associated icon
*/
public Image getPluginFormatIcon(Plugin plugin) {
public Image getPluginFormatIcon(PluginFormat format) {

switch (plugin.getFormat()) {
switch (format) {
case VST2:
return vst2Image;
case VST3:
Expand Down Expand Up @@ -159,6 +166,13 @@ public Image getPackageTypeIcon(RemotePackage remotePackage) {
}
}

public Image getDAWApplicationIcon(DawApplication application) {
return switch (application) {
case ABLETON -> abletonLogoImage;
default -> pluginComponentImage;
};
}

public String getDefaultPluginPath(PluginFormat format) {

if (runtimePlatform.getOperatingSystem().equals(OperatingSystem.WIN)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.owlplug.core.tasks.TaskExecutionContext;
import com.owlplug.core.tasks.plugins.discovery.PluginSyncTaskParameters;
import com.owlplug.core.utils.FileUtils;
import com.owlplug.project.components.ProjectTaskFactory;
import java.util.ArrayList;
import java.util.Set;
import java.util.TreeSet;
Expand All @@ -52,6 +53,8 @@ public class CoreTaskFactory extends BaseTaskFactory {
private SymlinkDAO symlinkDAO;
@Autowired
private NativeHostService nativeHostService;
@Autowired
private ProjectTaskFactory projectTaskFactory;

@Autowired
private FileStatDAO fileStatDAO;
Expand Down Expand Up @@ -96,21 +99,26 @@ public TaskExecutionContext createPluginSyncTask(String directoryScope) {
parameters.setDirectoryScope(FileUtils.convertPath(directoryScope));
}

PluginSyncTask task = new PluginSyncTask(parameters,
PluginSyncTask syncTask = new PluginSyncTask(parameters,
pluginDAO,
pluginFootprintDAO,
symlinkDAO,
nativeHostService);

task.setOnSucceeded(e -> {
syncTask.setOnSucceeded(syncEvent -> {
notifyListeners(syncPluginsListeners);
if (directoryScope != null) {
createFileStatSyncTask(directoryScope).scheduleNow();
} else {
createFileStatSyncTask().scheduleNow();
}
TaskExecutionContext lookupTask = projectTaskFactory.createLookupTask();
lookupTask.getTask().setOnScheduled(lookupEvent -> {
if (directoryScope != null) {
createFileStatSyncTask(directoryScope).scheduleNow();
} else {
createFileStatSyncTask().scheduleNow();
}
});
lookupTask.scheduleNow();
});
return create(task);

return create(syncTask);
}

public TaskExecutionContext createFileStatSyncTask() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ private synchronized void scheduleNext() {
public void onSuccess(TaskResult result) {
Platform.runLater(() -> {
if (currentTask.getState().equals(State.FAILED)) {
if (currentTask.getException() != null) {
log.error("Error while running task", currentTask.getException());
}
triggerOnError();
}
removeCurrentTask();
Expand All @@ -115,6 +118,7 @@ public void onSuccess(TaskResult result) {

@Override
public void onFailure(Throwable ex) {
log.error("Error while running task", ex);
Platform.runLater(() -> {
removeCurrentTask();
scheduleNext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public void initialize() {

public void setComponent(PluginComponent component) {
this.currentComponent = component;
pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(component.getPlugin()));
pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(component.getPlugin().getFormat()));
pluginFormatLabel.setText(component.getPlugin().getFormat().getText() + " Plugin Component");
pluginTitleLabel.setText(component.getName());
pluginNameLabel.setText(Optional.ofNullable(component.getDescriptiveName()).orElse(component.getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class DirectoryInfoController extends BaseController {
public void initialize() {

openDirectoryButton.setOnAction(e -> {
PlatformUtils.openDirectoryExplorer(pluginDirectory.getPath());
PlatformUtils.openFromDesktop(pluginDirectory.getPath());
});

pluginDirectoryListView.setCellFactory(new PluginListCellFactory(this.getApplicationDefaults()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public class MainController extends BaseController {
@FXML
private Button downloadUpdateButton;

public static int PLUGINS_TAB_INDEX = 1;

/**
* FXML initialize method.
*/
Expand Down Expand Up @@ -202,6 +204,10 @@ public void dispatchPostInitialize() {

}

public void selectMainTab(int index) {
this.tabPaneHeader.getSelectionModel().select(index);
}

/**
* Refresh Account comboBox.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public void initialize() {
openDirectoryButton.setText("");
openDirectoryButton.setOnAction(e -> {
File pluginFile = new File(pluginPathLabel.getText());
PlatformUtils.openDirectoryExplorer(pluginFile.getParentFile());
PlatformUtils.openFromDesktop(pluginFile.getParentFile());
});

uninstallButton.setOnAction(e -> {
Expand Down Expand Up @@ -155,7 +155,7 @@ public void initialize() {

public void setPlugin(Plugin plugin) {
this.currentPlugin = plugin;
pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(plugin));
pluginFormatIcon.setImage(this.getApplicationDefaults().getPluginFormatIcon(plugin.getFormat()));
pluginFormatLabel.setText(plugin.getFormat().getText() + " Plugin");
pluginTitleLabel.setText(plugin.getName());
pluginNameLabel.setText(Optional.ofNullable(plugin.getDescriptiveName()).orElse(plugin.getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,30 @@ private void buildDirectoryTree(FileTree pluginTree, FilterableTreeItem<Object>
}
}

public void selectPluginInTreeById(long id) {
List<TreeItem> items = getAllChildrens(pluginTreeView.getRoot());

for (TreeItem item : items) {
if (item.getValue() instanceof Plugin plugin
&& plugin.getId().equals(id)) {
int row = pluginTreeView.getRow(item);
pluginTreeView.getSelectionModel().select(row);
}
}

}

private List<TreeItem> getAllChildrens(TreeItem item) {
List<TreeItem> items = new ArrayList<>();
items.add(item);

List<TreeItem> childs = new ArrayList<>(item.getChildren());
for (TreeItem child : childs) {
items.addAll(getAllChildrens(child));
}
return items;
}

class FileTree extends HashMap<String, FileTree> {

private Object nodeValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ public void initialize() {

openLinkButton.setGraphic(new ImageView(this.getApplicationDefaults().symlinkImage));
openLinkButton.setOnAction(e -> {
PlatformUtils.openDirectoryExplorer(symlink.getPath());
PlatformUtils.openFromDesktop(symlink.getPath());
});

openTargetButton.setGraphic(new ImageView(this.getApplicationDefaults().directoryImage));
openTargetButton.setOnAction(e -> {
PlatformUtils.openDirectoryExplorer(symlink.getTargetPath());
PlatformUtils.openFromDesktop(symlink.getTargetPath());
});

pluginDirectoryListView.setCellFactory(new PluginListCellFactory(this.getApplicationDefaults()));
Expand Down
25 changes: 24 additions & 1 deletion owlplug-client/src/main/java/com/owlplug/core/dao/PluginDAO.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,34 @@
package com.owlplug.core.dao;

import com.owlplug.core.model.Plugin;
import com.owlplug.core.model.PluginFormat;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import java.util.List;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;

public interface PluginDAO extends CrudRepository<Plugin, Long> {
public interface PluginDAO extends CrudRepository<Plugin, Long>, JpaSpecificationExecutor<Plugin> {

static Specification<Plugin> nameContains(String name) {
return (plugin, cq, cb) -> cb.like(cb.lower(plugin.get("name")), "%" + name.toLowerCase() + "%");
}

static Specification<Plugin> hasFormat(PluginFormat format) {
return (plugin, cq, cb) -> cb.equal(plugin.get("format"), format);
}

static Specification<Plugin> hasComponentName(String name) {
return (plugin, cq, cb) -> {
Join<Object, Object> component = (Join<Object, Object>) plugin.fetch("components", JoinType.INNER);
return cb.equal(cb.lower(component.get("name")), name.toLowerCase());

};
}



Plugin findByPath(String path);

Expand Down
11 changes: 11 additions & 0 deletions owlplug-client/src/main/java/com/owlplug/core/model/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.owlplug.core.model;

import com.owlplug.project.model.DawPluginLookup;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand All @@ -32,6 +33,8 @@
import jakarta.persistence.Table;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

@Entity
@Inheritance
Expand Down Expand Up @@ -67,6 +70,10 @@ public class Plugin {
fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
private Set<PluginComponent> components = new HashSet<>();

@OneToMany(mappedBy = "plugin")
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<DawPluginLookup> lookups;

public Plugin() {

}
Expand All @@ -77,6 +84,10 @@ public Plugin(String name, String path, PluginFormat format) {
this.format = format;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}
Expand Down
Loading