beaTunes News

Wednesday, December 21, 2016

Update: AcousticBrainz Mood Plugin

beaTunes4 logo

For the last 1.5 years, lots of you have taken advantage of the AcousticBrainz plugin for beaTunes. I've finally found the time to move the source code to GitHub, so that it can be found in the same repository as the other sample plugins.

While doing so, I added two features:

  1. You can now choose, whether the plugin should replace existing mood values or not.
  2. You can ask the plugin to embed mood attributes in the id3 v2.4 TMOO tag.

Please note, that to retrieve mood values from AcousticBrainz, MusicBrainz ids are required. beaTunes makes an effort to find those, but may not always succeed. Also, keep in mind, that the values AcousticBrainz offers are based on a statistical model. In other words, the accuracy may vary.

You can install/update the plugin via beaTunes' Plugin preferences. Enjoy.

Labels: , , , ,

Wednesday, June 15, 2011

Managing Plugins

I have this unfortunate habit of starting blog entries with sentences like "Some of you may have wondered ..." or "As you have probably noticed ...". Well. Perhaps some of you really have noticed that the blog support has been removed from beaTunes 3. But I doubt it.

The sad truth is, hardly anyone has ever used this feature and because it was hardly used, it was in fairly bad shape, which of course led to less use. The vicious circle of a non-essential feature. What do you do with a feature that's not really necessary? One, that's not the product's core? Right, you either kill it or you make it a plugin.

This is exactly what happened to the blog support. So if you are one of the very few who liked and used the blog support, you will need to install the blog plugin. And that's the real reason for this post.

beaTunes 3 now has a built-in plugin manager that allows you to discover, download and install plugins via the preference panel.

Just open the beaTunes preferences and go to the Plugin pane. There you can inspect which plugins are currently installed, uninstall them, update them or check out other available plugins. One of the plugins currently available is the blog plugin. Others are old friends, like the keytocomment or the last.fm plugin.

If you want your Java plugin to be listed here, please contact me through the support forum. For now, plugins need to be submitted as Maven projects in source code.

Labels: ,

Tuesday, June 14, 2011

Lowering CoGS: beaTunes' new Plugin API

beaTunes3 logoFor a long time beaTunes has offered a more or less stable plugin API. With beaTunes 3 the API has changed significantly - and it has become a lot easier to use. The new AudioSong object for example, lets you easily manipulate song metadata without having to worry about how and where that data is going to be saved.

But an API is only worth the bits it's coded in, if the cost of getting started (CoGS™) is low enough. Let's recap:

The old API was...

  • poorly documented,
  • plugins needed to be in written in Java (i.e. compiled) and
  • one needed to package them in a special way, which included adding a plugin descriptor in a defined location.

In other words, the old API was a perfect example of how not to do it.

Now, let me say that the new API is still not the greatest API ever (imagine that!). But is has gotten a lot better and CoGS is a lot lower.

Better Documentation

There is now a page that offers a real starting point. The access to the javadocs has been there before, but now they are also deployed to the Maven repository. That makes them a lot easier to access from your IDE (if you are using one). That said, most people don't learn an API by looking at the reference docs. Annotated sample code teaches most people way more in much less time.

It's like API essence: take a sip, mix it with your own experience and it magically inflates to knowledge.

This is why I made an effort to write some key examples - code that can be modified and re-used as you see fit. But most importantly, it just works without you having to find out some boring details the hard way.

No need to compile

For the old API you had to install a Java JDK, setup an IDE, install Maven, create a project... before you could do anything. The only question left to answer here is: Did you give up after the JDK installation or before the Maven setup?

Of course IDEs can be great and make your life a lot easier. The same is true for Maven. But what, if you just want to code something simple, like a global search and replace? This is the kind of thing that should be done via a simple script. No JDK, no Maven, no IDE.

No hassle.

And that's exactly what beaTunes 3 is capable of.

Little scripts called beaTlets can now be dropped into a special directory and beaTunes will pick them up the next time it starts. The API is still written in Java, but thanks to JSR-223 magic, your code can be in either Groovy, JRuby or Jython. Working examples for all three scripting languages can be found here.

Trivial Installation

I already mentioned it - these fancy little scripts called beaTlets can be installed by merely moving a file into the right directory. No need to write a special descriptor anymore. That said, you can still do it to communicate certain needs to beaTunes. But you don't have to.

Conclusion

Writing simple plugins for beaTunes has become a lot easier. CoGS has been lowered a lot. Should you still have questions, please feel free to contact me or post to the Plugins section of the forum. I'm here to help.

Labels: ,

Announcing Jipes 0.9.2

Jipes is an open source library that allows you to efficiently compute audio features. Possible uses for these features are general music information retrieval (MIR) applications or more specifically personal music software like beaTunes. As a matter of fact, since version 3, beaTunes uses Jipes.

To create your own Jipes-based music analyzer in beaTunes, check out the Song Property Analyzer example in the plugin API documentation.

Labels: , ,

Monday, September 27, 2010

New Lyrics plugin using musixmatch.com

I recently came across this cool new service called musixmatch.

They provide lyrics that are actually licensed. Coding a plugin for their alpha API was a breeze.

After installation you will see a new task in the Analysis Options dialog - the one that is shown after you selected some songs and clicked Analyze. This allows you to batch-fetch lyrics.

For the time being, you may run into daily request limits, i.e. after X number of requests you may see 401 or 402 errors. You might want to try again the next day.

Also, please note that this plugin may cease to work, once their service goes into official production or the (not yet completely stable) API is changed. That aside, it probably won't work after 11/2010.

People interested in coding plugins for beaTunes, might want to look at the provided source code. Anyhow - here it is:

Enjoy!

(installation instructions for beaTunes plugins)

Update 9/29/2010

Due to changes in the musixmatch API, the 1.0.0 version of the plugin broke (well, that's what happens when using alpha APIs). I fixed the issue and updated the links above to point to a new version, 1.0.1. When updating the plugin, please make sure you remove any older versions of the plugin.

Labels: ,

Monday, August 16, 2010

Importing Metadata from Filename

beaTunes2 logoBefore there was id3 most people organized their music by using folder and filenames. E.g.:

.../U2/How To Dismantle An Atomic Bomb/01 Vertigo.mp3

Unfortunately, iTunes ignores folder structures when importing files, so all the valuable metadata is lost. This plugin offers a first step in recovering this information by allowing you to read the folder names and write them as album, artist, title and tracknumber to the iTunes database (and the id3 tags).

After installation, the plugin will show up in your Analysis Options dialog. It offers a couple of pre-defined patterns you can pick from. These patterns should cover about 80% of the typical cases. If your file/folder naming schema does not fit any of these patterns, don't use this plugin!

As always when mass-processing any files, try it out with just a few files and if you are uncertain of the outcome, don't do it or first create a backup that you know how to restore.

Labels: ,

Monday, March 15, 2010

Deleting orphaned Files II

beaTunes2 logoA while ago I posted a plugin for deleting orphaned files in your iTunes Music folder. Well, along with a couple of other plugins, it unfortunately broke, because of some internal updates to beaTunes. Here's the updated version:

Labels: ,

Sunday, February 21, 2010

beaTunes 2.1.4 requires update for KeyToComment and KeyToGrouping

beaTunes2 logoDue to changes in 2.1.4, users of the keytoXX plugins may experience IncompatibleClassChangeErrors. Please update those two plugins. Note, that since 2.1 the location for Windows plugins has changed!

Don't forget to remove the old versions!

Labels: , ,

Thursday, February 18, 2010

New version of Last.FM plugin

beaTunes2 logoThis new version of the Last.FM tag importer allows you to manipulate how many tags are imported. For installation instructions, please see the installation FAQ.

  • Binary plugin - just place into beaTunes' plugin folder (make sure to remove the old version!)
  • Source code - Maven2 project

Enjoy.

Labels: , ,

Wednesday, January 6, 2010

Skipping tags and lyrics

beaTunes2 logoI hate to do this, but apparently there is and has been a bug in beaTunes 2.1 that can lead to beaTunes skipping some analysis steps when using Use Online Resources. Under certain circumstances, plugins like the Last.FM plugin may simply be skipped. The workaround is obviously to turn Use Online Resources off, when using said plugin. I promise this issue will be fixed in the next update. For those of you, who need a solution right now - fell free to use this 2.1.2 dev snapshot for OS X.

Labels: , ,

Wednesday, December 16, 2009

Updates for KeyToComment and KeyToGrouping plugins

beaTunes2 logoAs the API for beaTunes has slightly changed with 2.1, I'd wanted to post updates to both plugins. Note, that the location for Windows plugins has changed!

Don't forget to remove the old versions!

Labels: ,

Sunday, November 8, 2009

Update for Last.FM tag importer

beaTunes2 logoThis is a small update for the Last.FM tag plugin... Couple of things got fixed. E.g. in some cases all tags were imported.

  • Binary plugin - just place into beaTunes' plugin folder (make sure to remove the old version!)
  • Source code - Maven2 project

Please see this posting to find out how to install the plugin.

Also, please note that the code is licensed under LGPL - so you are free to use it pretty much any way you want, modify it, make it better etc. The only thing that's missing from the source is the Last.fm API key - you would have to get your own. If you have any questions about the code, please contact me!

Update

Because it's been asked - this is how you change the number of tags that are included:

  • install Maven2
  • download the source code for the plugin from the news blog
  • edit the file LastFMTopThreeTags.java, line 236 (if (tags.size() >= 3) {) - you simply have to increase that number
  • make sure you enter a valid HTTP user agent and a valid Last.FM API key at the top of the file (there are comments telling you where)
  • build it with mvn clean install
  • the build result will be in the target subdirectory

And of course it always helps to read the README.txt file :-)

Please discuss possible problems in the (still quite new and empty) Plugins Forum.

Thanks!

Labels: , ,

Saturday, October 24, 2009

Importing top three tags from Last.fm

beaTunes2 logoInspired by a user request, I approached the good people at Last.fm to figure out how to import tags from their database. The work is not done, but here's a first shot at it. This plugin queries the Last.fm database and inserts the top three tags as comments into your songs and also into the beaTunes tag database. It's implemented as a SongAnalysisTask, meaning, it will show up as an analysis option when you analyze songs with beaTunes (it will be at the very bottom, so you probably have to scroll down to it). Make sure to try this on few songs before tagging your whole library!

Please see this posting to find out how to install the plugin.

Also, please note that the code is licensed under LGPL - so you are free to use it pretty much any way you want, modify it, make it better etc. The only thing that's missing from the source is the Last.fm API key - you would have to get your own. If you have any questions about the code, please contact me!

Labels: , ,

Tuesday, October 20, 2009

Wanna help?

beaTunes2 logoI'm looking to hire preferably a CS student from RTP to help me out with some development (especially plugin stuff) and email support. Interested? Then check out the listing.

Labels: , ,

Wednesday, May 27, 2009

Deleting orphaned Files

beaTunes2 logoAs most of you know, beaTunes has the ability to find and delete duplicates. Up to version 2.0.7, these duplicates were only deleted from the iTunes database, but the actual files were not removed. I changed this in 2.0.8 - whenever possible, files are now moved to the trash/recycling bin or are simply deleted, should trash or recycling bin not be available (e.g. on network drives). But if you have deleted duplicates with an old version of beaTunes, it might be difficult to identify the files that are not referenced by iTunes anymore.

To solve the dilemma, I wrote a little plugin, that compares the iTunes database with all the files in a folder, i.e. the music folder where you keep your mp3's etc. in.

Note that this only makes sense, when you let iTunes manage your music collection or all your music files are in one folder that contains nothing but those files!

To install the plugin, download deleteorphans-1.0.0.jar and place it in your plugin folder (see this posting to find out where that is). The next time you start beaTunes, there will be an additional entry in your Tools menu called Delete orphans. Click on it and enter the location of your music folder, which is usually called iTunes Music. This folder should contain nothing but your audio/video files!

The plugin now compares the iTunes library with the contents of that folder and presents a list of files it found in the folder (or its subfolders), but not in iTunes. Review the list carefully and make sure you only select those files that you actually want to delete, then delete them.

For those of you interested in how the plugin works, take a look at the sources.

This plugins has been updated!

Labels: ,

Friday, March 27, 2009

Key to Comment

beaTunes2 logo

Want to copy your musical keys to the comment field? Check out this plugin (sources).

(installation instructions are the same as for keytogrouping)

Enjoy!

Labels: , ,

Updated Key to Grouping plugin

beaTunes2 logo

A while ago, before the beaTunes 2 release, I posted about how to develop a simple analysis task plugin. Because this plugin broke a while ago, I decided to go ahead and update the old post with a new version of the same plugin. Which isn't really news.

But!

I also updated the pom.xml of the project so that it actually compiles from the command line, i.e. with mvn clean install. Of course you need Maven2 installed, but everything else is automatic.

This is possible, because I decided to upload the beaTunes binaries to our own little repository. It should simplify plugin development significantly.

Labels: , ,

Saturday, February 21, 2009

Creating your own Song Context Components

beaTunes2 logo

As a beaTunes user you know that the matching song table and the Amazon song info panel are integral parts of the application. Both are hosted in the same space, right below the main song table. beaTunes' new plugin API makes it simple to write additional, custom components for the same space. I'd like to show you with a simple example how it's done.

The idea of this plugin is to show the Wikipedia page corresponding to the selected song's artist. Because Java's JEditorPane has a rather poor HTML rendering performance and JWebPane is definitely not available on the Mac, we will use JDIC as browser component (this example will only run on OS X, the concept is the same though for Windows). Note, that the Mac version of JDIC is rather old and really only an alpha version. So expect unnecessary repaints and other visual artifacts. But this is not about the perfect embedded browser for Java, it's about custom SongContextComponents.

Subclassing SongContextComponent

Our Wikipedia component has to subclass com.tagtraum.beatunes.songtable.SongContextComponent. This superclass does most of the work for us. We only have to implement a couple of methods. Let's start with the simple stuff. Just like any other well behaved plugin, our component should have an id.

public String getId() {
return "songcontextcomponent.wikipedia";
}

Furthermore, it needs to specify a status label text, which for our purposes may always remain empty (future versions of the API won't require this anymore).

public String getStatusLabel() {
return null;
}

Setting up the UI

To set up the component itself, we have to declare a couple of fields and instantiate the UI component in the constructor. Also, as part of the interface, we have to implement the getComponent() method, which returns the JComponent that is going to be integrated into beaTunes' UI. Here's how it's done:

private static final Logger LOG = LogManager.getLogger(Wikipedia.class);

// the component we hand to beaTunes
private JPanel component;
// the actual browser component
private IWebBrowser webBrowser;
// we want to remember the last artist we've shown
// to avoid reloading the same page
private String lastArtist;
// page loading should be done in a separate thread, not the event thread
private ExecutorService executorService;
// loading will be done in a Future, which can be cancelled
private Future future;


public Wikipedia(final BeaTunes beaTunes) {
super(beaTunes);
// register the show hide action for this SongContextComponent
setShowHideActionId("wikipedia.show.hide");
this.executorService = Executors.newSingleThreadExecutor(
PriorityThreadFactory.getInstance(Thread.MIN_PRIORITY));
this.webBrowser = BrowserEngineManager.instance().getActiveEngine().getWebBrowser();
// webBrowser already has scrollbars, therefore a JPanel is good enough as component
this.component = new JPanel(new BorderLayout());
this.component.add((Component) webBrowser, BorderLayout.CENTER);
}

public JComponent getComponent() {
return component;
}

Reacting to song selections

Now that we have set up the webBrowser component, the only thing that's left to do, is to put some content into the browser window. To do so, we implement the update(Song) method. This method is called when the user selects a song in the main song table. To be precise, it is not called right away when the user selects a song, but with a little delay. This allows us to ignore the selection, should the user just scroll down a list of songs using the cursor keys.

public void update(final Song song) {
// sanity checks
if (song == null) return;
if (song.getArtist() == null || song.getArtist().length() == 0) return;
final String artist = song.getAlbumArtist() != null
&& song.getAlbumArtist().length() != 0
? song.getAlbumArtist() : song.getArtist();
// is this what we just showed?
if (artist.equals(lastArtist)) return;
lastArtist = artist;
showArtist(artist);
}

private void showArtist(final String artist) {
if (future != null) {
future.cancel(true);
}
future = executorService.submit(new Runnable() {
public void run() {
try {
// respect the user's language
final String language = getApplication().getLocale().getLanguage();
final URL url = new URL("http://" + language
+ ".wikipedia.org/wiki/Special:Search?search="
+ URLEncoder.encode(artist, "UTF-8") + "&go=Go");
webBrowser.setURL(url);
} catch (IOException e) {
LOG.error("Unable to open page for " + artist, e);
}
}
});
}

That's basically it.

Showing some Action

Well, almost... there are some things we still need to take care of. To start with, we of course have to provide a plugin.xml file to register the class. But more importantly, we have to provide some Action to show and hide our Wikipedia component. To do so, we subclass com.tagtraum.beatunes.action.standard.SongContextComponentShowHideAction. Here's the class:

public class ShowHideWikipediaAction extends SongContextComponentShowHideAction {

public ShowHideWikipediaAction(final BeaTunes beaTunes) {
super(beaTunes);
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_W,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
| InputEvent.SHIFT_MASK));
}

public void init() {
// always call super.init() to make sure the action gets enabled/disabled properly
super.init();
putValue(Action.NAME, "Show Artist Info");
putValue(Action.SHORT_DESCRIPTION, "Shows artist info on Wikipedia");
putValue(Action.SMALL_ICON,
new ImageIcon(Wikipedia.class.getResource("wikipedia.png")));
putValue(BaseAction.SELECTED_ICON,
new ImageIcon(Wikipedia.class.getResource("wikipedia_selected.png")));
putValue(BaseAction.DISABLED_ICON,
new ImageIcon(Wikipedia.class.getResource("wikipedia_disabled.png")));
putValue(BaseAction.DISABLED_SELECTED_ICON,
new ImageIcon(Wikipedia.class.getResource("wikipedia_disabled.png")));
}

public String getId() {
// this is the id we reference in the Wikipedia class
return "wikipedia.show.hide";
}

public ActionLocation[] getActionLocations() {
return new ActionLocation[] {
// add to Edit menu
new RelativeActionLocation(BeaTunesUIRegion.EDIT_MENU,
RelativeActionLocation.RelativePosition.BEFORE, "tree.show.hide"),
// add as first (index 0) item to the lower right button panel
new AbsoluteActionLocation(BeaTunesUIRegion.LOWER_RIGHT_BUTTON_PANEL, 0)
};
}
}

And here is the very simple plugin.xml that needs to be placed in the META-INF directory of our jar.

<?xml version="1.0" encoding="UTF-8" ?>
<!-- Contains a list of plugins to be instantiated by beaTunes -->
<plugins>
<!-- SongContextComponent -->
<plugin class="com.beatunes.wikipedia.Wikipedia"/>
<!-- Actions -->
<plugin class="com.beatunes.wikipedia.ShowHideWikipediaAction"/>
</plugins>

Zip it!

Since the plugin uses third party libraries, we can't just put everything in one jar and place it in the plugin folder. Instead, we have to create a folder with a subfolder called lib. Into the lib folder we place the plugin's jar, jdic.jar and the native library libjdic.jnilib (both from JDIC). Then we zip the whole thing up.

In essence, the directory structure looks like this:

/lib/wikipedia-1.0.0.jar
/jdic.jar
/libjdic.jnilib

For beaTunes 2, the plugin classloader is configured to add all jars in the lib directory, and the classes subdirectory itself, should it exist. Note that plugins are not isolated from each other or from beaTunes, so library and/or class version conflicts are something to stay aware of.

You can download the full source for this example from here and the ready to go binary from here.

Labels: , , , ,

Thursday, December 18, 2008

Adding a custom menu item

beaTunes LogoAs I mentioned before, beaTunes 2 will be a lot more friendly towards developers who want to write plugins. As a matter of fact, many of the standard components you can see in the UI are plugins already. If you're curious, just unzip the beaTunes.jar (on OS X open the app bundle and navigate to Contents/Resources/Java - that's where all the jars are, on Windows, just go to the apps lib directory) and checkout the file plugin.xml in the META-INF directory. That file is read by beaTunes and the classes in it are instantiated and registered with the plugin manager. For you that basically means, you have to include a plugin.xml in your own plugin jar file.

But this post wasn't supposed to be about plugin.xml, it was supposed to be about how to get a new menu item into beaTunes. And that is actually pretty easy.

Obviously, a menu item is backed by an action, in the case of beaTunes it should be a com.tagtraum.beatunes.action.BaseAction. So, just subclass BaseAction. Because getId() and actionPerformed() are abstract, you will have to implement them. getId() is supposed to return a String id. Make something up. actionPerformed() does whatever you want your action to do.

public String getId() {
return "com.mycomp.action.somethingfancy";
}

public void actionPerformed(final ActionEvent e) {
// do something grand!
}

Now, obviously you want your action to have a name. To give it one, implement a constructor like this:

public MyAction(final BeaTunes beaTunes) {
super(beaTunes);
putValue(Action.NAME, "My Menu Item");
// add other action properties as you see fit
}

At this point you have an action with a name, an id and some action method, but beaTunes doesn't know yet where to put the thing. To tell it where to show it, you have to override the following method:

public ActionLocation[] getActionLocations() {
// install the action in just one location - in this case the tool menu
return new ActionLocation[] {
new AbsoluteActionLocation(BeaTunesUIRegion.TOOL_MENU)
};
}

The sample code above installs the action in the tool menu. Now, unfortunately that does not give you any control over where exactly in the tool menu the action will be placed. To get more control, you can use a RelativeActionLocation. Like this:

new RelativeActionLocation(BeaTunesUIRegion.EDIT_MENU,
RelativeActionLocation.RelativePosition.BEFORE, "tree.show.hide")

This location installs an action in the edit menu, right before the action with the id tree.show.hide.

Now compile your action, put it in a jar and add a META-INF/plugin.xml-file that looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<plugins>
<plugin class="com.yourcomp.MyAction"/>
</plugins>

Then place the jar into the beaTunes plugin directory and start beaTunes.

Done!

Note: This code will only work with beaTunes 2. It has been updated for EA16.

Labels: ,

Wednesday, December 17, 2008

Copy Keys to Grouping Plugin

beaTunes LogoWe all know the problem: iTunes does not show tonal key information available in beaTunes. To solve it, I wrote a little beaTunes 2.0 plugin, that copies the key info into the (often unused) grouping field. Just download the this file and place it into your plugin directory. Make sure that it is the only version of the plugin in that folder - i.e. remove any older versions.

The location of your plugin directory depends on your operating system:

  • XP: c:\Documents and Settings\[username]\.beaTunes\plugins
  • Vista: c:\Users\[username]\.beaTunes\plugins
  • OS X: [your_home]/Library/Application Support/beaTunes/Plug-Ins

If you're interested in how the plugin works - it's trivial and here's the maven project complete with Java sources. Just make sure you somehow point to the beaTunes binaries for compilation.

Important!

This plugin needs at least beaTunes 2.0.3 to work correctly!

Note: This post has been updated to reflect changes in EA16 and changed again to reflect changes in 2.0.3.

Labels: , , ,