Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to get javafx media metadata without listener

so I've been looking for this for a week now and reading though every problem similar but none seemed to ask the same problem as mine exactly(try reverse engineering other solution similar to what I want with no success.

explained caveman style: I'm trying to create list using Metadata.

  1. I open with a multi dialog and select more than one mp3
  2. I put the file in an ArrayList<File>
  3. I loop though the files with an enhanced for loop and extract metadata using a media variable
  4. The info for the metadata ( like "artist") is what i want to save in an ArrayList for example

the problem is that the listener only works way after the enhanced loop has finished which results in ArrayList<String> having one object with nothing in it

here is a sample:

ArrayList<String> al;
String path;
public void open(){
    files=chooser.showOpenMultipleDialog(new Stage());
    for( File f:files){             
        path=f.getPath();
        Media media = new Media("file:/"+path.replace("\\", "/").replace(" ", "%20"));
        al= new ArrayList<String>();
        media.getMetadata().addListener(new MapChangeListener<String, Object>() {                 
            public void onChanged(Change<? extends String, ? extends Object> change) {
                if (change.wasAdded()) {
                    if (change.getKey().equals("artist")) {
                        al.add((String) change.getValueAdded());
                     }
                }
            }
        });
    }//close for loop
    //then i want to see the size of al like this
    system.out.println(al.size());
    //then it returns 1 no matter how much file i selected
    //when i system out "al" i get an empty string
like image 638
user1972819 Avatar asked Oct 22 '22 19:10

user1972819


1 Answers

the other way to read a media source metadata with adding a listener is extract that information in the mediaplayer .setOnReady(); here is an example part of the java controller class

public class uiController implements Initializable {

@FXML private Label label;
@FXML private ListView<String> lv;
@FXML private AnchorPane root;
@FXML private Button button;

private ObservableList<String> ol= FXCollections.observableArrayList();
private List<File> selectedFiles;
private final Object obj= new Object();

@Override
public void initialize(URL url, ResourceBundle rb) {
    assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'ui.fxml'.";
    assert label != null : "fx:id=\"label\" was not injected: check your FXML file 'ui.fxml'.";
    assert lv != null : "fx:id=\"lv\" was not injected: check your FXML file 'ui.fxml'.";
    assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'ui.fxml'.";

    // initialize your logic here: all @FXML variables will have been injected
    lv.setItems(ol);
}  

@FXML private void open(ActionEvent event) {
    FileChooser.ExtensionFilter extention= new FileChooser.ExtensionFilter("Music Files", "*.mp3","*.m4a","*.aif","*.wav","*.m3u","*.m3u8");
    FileChooser fc= new FileChooser();
    fc.setInitialDirectory(new File(System.getenv("userprofile")));
    fc.setTitle("Select File(s)");
    fc.getExtensionFilters().add(extention);
    selectedFiles =fc.showOpenMultipleDialog(root.getScene().getWindow());
    if(selectedFiles != null &&!selectedFiles.isEmpty()){
        listFiles();
    }
}
/**
 * Convert each fie selected to its URI
 */
private void listFiles(){ 
    try {
        for (File file : selectedFiles) {
            readMetaData(file.toURI().toString());
            synchronized(obj){
               obj.wait(100);
            }
        }
    } catch (InterruptedException ex) {
    }
    System.gc();
}
/**
 * Read a Media source metadata 
 * Note: Sometimes the was unable to extract the metadata especially when 
 * i have selected large number of files reasons i don't known why
 * @param mediaURI Media file URI
 */
private void readMetaData(String mediaURI){
    final MediaPlayer mp= new MediaPlayer(new Media(mediaURI));
    mp.setOnReady(new Runnable() {

        @Override
        public void run() {
            String artistName=(String) mp.getMedia().getMetadata().get("artist");
            ol.add(artistName);
            synchronized(obj){//this is required since mp.setOnReady creates a new thread and our loopp  in the main thread
                obj.notify();// the loop has to wait unitl we are able to get the media metadata thats why use .wait() and .notify() to synce the two threads(main thread and MediaPlayer thread)
            }
        }
    });
}

}

the few changes that have made is used an ObservableList to store the artist name from the metadata

in the code you will find this

 synchronized(obj){
                    obj.wait(100);
                } 
I do this because the mediaplayer .setOnReady() creates a new thread and the loop is in the main application thread, The loop has to wait for some time before the other thread is created and we are able to extract the metadata, and in the .setOnReady() there is a
 synchronized(obj){
                    obj.notify;
                } 
to wake up the main thread hence the loop is able to move to the next item

I admit that this may not be the best solution to do this but am welcomed to anyone who has any better way on how to read JavaFx media metadata from a list of files The full Netbeans project can be found here https://docs.google.com/file/d/0BxDEmOcXqnCLSTFHbTVFcGIzT1E/edit?usp=sharing

plus have created a small MediaPlayer Application using JavaFX which expolits use of the metadata https://docs.google.com/file/d/0BxDEmOcXqnCLR1Z0VGN4ZlJkbUU/edit?usp=sharing

like image 54
Francis Avatar answered Nov 01 '22 09:11

Francis