JavaFxMediaPlayer.java

/*
 * *********************************************************************************************************************
 *
 * blueMarine II: Semantic Media Centre
 * http://tidalwave.it/projects/bluemarine2
 *
 * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
 *
 * *********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * *********************************************************************************************************************
 *
 * git clone https://bitbucket.org/tidalwave/bluemarine2-src
 * git clone https://github.com/tidalwave-it/bluemarine2-src
 *
 * *********************************************************************************************************************
 */
package it.tidalwave.bluemarine2.ui.audio.renderer.impl.javafx;

import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.time.Duration;
import java.nio.file.Path;
import javafx.beans.value.ObservableValue;
import javafx.scene.media.Media;
import it.tidalwave.bluemarine2.model.MediaFileSystem;
import it.tidalwave.bluemarine2.model.MediaItem;
import it.tidalwave.bluemarine2.ui.audio.renderer.spi.MediaPlayerSupport;
import lombok.extern.slf4j.Slf4j;

/***********************************************************************************************************************
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
@Slf4j
public class JavaFxMediaPlayer extends MediaPlayerSupport
  {
    private static final javafx.util.Duration SKIP_DURATION = javafx.util.Duration.seconds(1);

    @Nullable
    private Media media;

    @Nullable
    private javafx.scene.media.MediaPlayer mediaPlayer;

    @Inject
    private MediaFileSystem fileSystem;

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    private final Runnable cleanup = () ->
      {
        log.debug(">>>> media reproduction finished");
        // FIXME: remove listener from currentTimeProperty
        mediaPlayer = null;
        statusProperty.setValue(Status.STOPPED);
      };

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void setMediaItem (@Nonnull final MediaItem mediaItem)
      throws Exception
      {
        log.info("setMediaItem({})", mediaItem);
        checkNotPlaying();
        this.mediaItem = mediaItem;
        final Path path = fileSystem.getRootPath().resolve(mediaItem.getPath()).toAbsolutePath();
        log.debug("path:     {}", path);
        log.debug("metadata: {}", mediaItem.getMetadata());
        media = new Media(path.toUri().toString());
        statusProperty.set(Status.STOPPED);
        playTimeProperty.set(Duration.ZERO);
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public synchronized void play()
      throws Exception
      {
        log.info("play()");
        checkNotPlaying();

        if ((mediaPlayer != null) && mediaPlayer.getStatus().equals(javafx.scene.media.MediaPlayer.Status.PAUSED))
          {
            mediaPlayer.play();
          }
        else
          {
            if (mediaPlayer != null)
              {
                mediaPlayer.dispose();
              }

            mediaPlayer = new javafx.scene.media.MediaPlayer(media);
            // FIXME: bidirectional bind to an expression?
            mediaPlayer.currentTimeProperty().addListener(
                    (ObservableValue<? extends javafx.util.Duration> observable,
                    javafx.util.Duration oldValue,
                    javafx.util.Duration newValue) ->
                            playTimeProperty.setValue(Duration.ofMillis((long)newValue.toMillis())));

            mediaPlayer.play();
            mediaPlayer.setOnEndOfMedia(cleanup);
            mediaPlayer.setOnError(cleanup);
            mediaPlayer.setOnHalted(cleanup);
          }

        statusProperty.setValue(Status.PLAYING);
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void stop()
      {
        log.info("stop()");

        if (mediaPlayer != null)
          {
            mediaPlayer.stop();
            statusProperty.setValue(Status.STOPPED);
          }
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void pause()
      {
        log.info("pause()");

        if (mediaPlayer != null)
          {
            mediaPlayer.pause();
            statusProperty.setValue(Status.PAUSED);
          }
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void rewind()
      {
        log.info("rewind()");

        if (mediaPlayer != null)
          {
            mediaPlayer.seek(mediaPlayer.getCurrentTime().subtract(SKIP_DURATION));
          }
      }

    /*******************************************************************************************************************
     *
     * {@inheritDoc}
     *
     ******************************************************************************************************************/
    @Override
    public void fastForward()
      {
        log.info("fastForward()");

        if (mediaPlayer != null)
          {
            mediaPlayer.seek(mediaPlayer.getCurrentTime().add(SKIP_DURATION));
          }
      }

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    private void checkNotPlaying()
      throws Exception
      {
        if ((mediaPlayer != null) && mediaPlayer.getStatus().equals(javafx.scene.media.MediaPlayer.Status.PLAYING))
          {
            throw new Exception("Already playing");
          }
      }
  }