RepositoryAudioFile.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * blueMarine II: Semantic Media Centre
  5.  * http://tidalwave.it/projects/bluemarine2
  6.  *
  7.  * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
  8.  *
  9.  * *********************************************************************************************************************
  10.  *
  11.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  12.  * the License. You may obtain a copy of the License at
  13.  *
  14.  *     http://www.apache.org/licenses/LICENSE-2.0
  15.  *
  16.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  17.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  18.  * specific language governing permissions and limitations under the License.
  19.  *
  20.  * *********************************************************************************************************************
  21.  *
  22.  * git clone https://bitbucket.org/tidalwave/bluemarine2-src
  23.  * git clone https://github.com/tidalwave-it/bluemarine2-src
  24.  *
  25.  * *********************************************************************************************************************
  26.  */
  27. package it.tidalwave.bluemarine2.model.impl.catalog;

  28. import javax.annotation.Nonnegative;
  29. import javax.annotation.Nonnull;
  30. import javax.annotation.concurrent.Immutable;
  31. import javax.inject.Inject;
  32. import java.time.Duration;
  33. import java.util.Optional;
  34. import java.io.IOException;
  35. import java.nio.file.Path;
  36. import java.nio.file.Files;
  37. import org.eclipse.rdf4j.query.BindingSet;
  38. import org.eclipse.rdf4j.repository.Repository;
  39. import org.springframework.beans.factory.annotation.Configurable;
  40. import it.tidalwave.util.Id;
  41. import it.tidalwave.util.Memoize;
  42. import it.tidalwave.bluemarine2.util.Formatters;
  43. import it.tidalwave.bluemarine2.model.MediaFileSystem;
  44. import it.tidalwave.bluemarine2.model.audio.AudioFile;
  45. import it.tidalwave.bluemarine2.model.audio.Record;
  46. import it.tidalwave.bluemarine2.model.finder.audio.MusicArtistFinder;
  47. import it.tidalwave.bluemarine2.model.spi.MetadataSupport;
  48. import it.tidalwave.bluemarine2.model.spi.PathAwareEntity;
  49. import it.tidalwave.bluemarine2.model.impl.AudioMetadataFactory;
  50. import it.tidalwave.bluemarine2.model.impl.catalog.finder.RepositoryMusicArtistFinder;
  51. import it.tidalwave.bluemarine2.model.impl.catalog.finder.RepositoryRecordFinder;
  52. import lombok.EqualsAndHashCode;
  53. import lombok.Getter;
  54. import lombok.extern.slf4j.Slf4j;
  55. import static it.tidalwave.bluemarine2.util.Miscellaneous.*;
  56. import static it.tidalwave.bluemarine2.model.MediaItem.Metadata.*;
  57. import org.springframework.core.io.FileSystemResource;
  58. import org.springframework.core.io.Resource;

  59. /***********************************************************************************************************************
  60.  *
  61.  * An implementation of {@link AudioFile} that is mapped to a {@link Repository}.
  62.  *
  63.  * @stereotype  Datum
  64.  *
  65.  * @author  Fabrizio Giudici
  66.  *
  67.  **********************************************************************************************************************/
  68. @Immutable @Configurable @EqualsAndHashCode(of = { "path", "trackId" }, callSuper = false) @Slf4j
  69. public class RepositoryAudioFile extends RepositoryEntitySupport implements AudioFile
  70.   {
  71.     @Getter @Nonnull
  72.     private final Path path; // FIXME: rename to relativePath?

  73.     @Getter @Nonnull
  74.     private final Metadata metadata;

  75.     @Nonnull
  76.     private final Optional<Id> trackId;

  77.     @Nonnull
  78.     private final Optional<Duration> duration;

  79.     @Nonnull
  80.     private final Optional<Long> fileSize;

  81.     private final Memoize<Metadata> fallbackMetadata = new Memoize<>();

  82.     @Inject
  83.     private MediaFileSystem fileSystem;

  84.     public RepositoryAudioFile (@Nonnull final Repository repository, @Nonnull final BindingSet bindingSet)
  85.       {
  86.         super(repository, bindingSet, "audioFile", rdfsLabelOf(bindingSet.getBinding("path").getValue().stringValue()));
  87.         this.path      = toPath(bindingSet.getBinding("path"));
  88.         this.duration  = toDuration(bindingSet.getBinding("duration"));
  89.         this.fileSize  = toLong(bindingSet.getBinding("fileSize"));
  90.         this.trackId   = toId(bindingSet.getBinding("track"));

  91.         this.metadata = new MetadataSupport(path).with(TITLE, rdfsLabel)
  92.                                                  .with(DURATION, duration)
  93.                                                  .with(FILE_SIZE, fileSize)
  94.                                                  .withFallback(key -> fallbackMetadata.get(this::loadFallbackMetadata));
  95.       }

  96.     @Override @Nonnull
  97.     public AudioFile getAudioFile()
  98.       {
  99.         return this;
  100.       }

  101.     @Override @Nonnull
  102.     public Optional<Resource> getContent()
  103.       throws IOException
  104.       {
  105.         final Path absolutePath = normalizedPath(getAbsolutePath());
  106.         return Files.exists(absolutePath) ? Optional.of(new FileSystemResource(absolutePath.toFile())) : Optional.empty();
  107.       }

  108.     @Override @Nonnegative
  109.     public long getSize()
  110.       throws IOException
  111.       {
  112.         return Files.size(normalizedPath(getAbsolutePath()));
  113.       }

  114.     @Override @Nonnull
  115.     public MusicArtistFinder findMakers()
  116.       {
  117.         return new RepositoryMusicArtistFinder(repository).makerOf(trackId.get());
  118.       }

  119.     @Override @Nonnull
  120.     public MusicArtistFinder findComposers()
  121.       {
  122.         return new RepositoryMusicArtistFinder(repository).makerOf(trackId.get());
  123. //        return new RepositoryAudioFileArtistFinder(this); FIXME
  124.       }

  125.     @Override @Nonnull
  126.     public Optional<Record> getRecord()
  127.       {
  128.         return trackId.flatMap(tid -> new RepositoryRecordFinder(repository).containingTrack(tid).optionalFirstResult());
  129.       }

  130.     @Override @Nonnull
  131.     public Optional<PathAwareEntity> getParent() // FIXME: drop it
  132.       {
  133.         throw new UnsupportedOperationException();
  134.       }

  135.     @Override @Nonnull
  136.     public String toString()
  137.       {
  138.         return String.format("RepositoryAudioFileEntity(%s, %s)", path, id);
  139.       }

  140.     @Override @Nonnull
  141.     public String toDumpString()
  142.       {
  143.         return String.format("%s %8s %s %s    -    %s", duration.map(Formatters::format).orElse("??:??"),
  144.                                                         fileSize.map(Object::toString).orElse(""),
  145.                                                         id, path, rdfsLabel);
  146.       }

  147.     @Nonnull
  148.     private Metadata loadFallbackMetadata()
  149.       {
  150.         final Path absolutePath = getAbsolutePath();
  151.         log.debug(">>>> loading fallback metadata from: {}", absolutePath);
  152.         // Don't check for file existence, it would fail for some files - see BMT-46. AudioMetadataFactory does all.
  153.         return AudioMetadataFactory.loadFrom(absolutePath);
  154.       }

  155.     @Nonnull
  156.     private Path getAbsolutePath()
  157.       {
  158.         return fileSystem.getRootPath().resolve(path);
  159.       }

  160.     @Nonnull
  161.     private static String rdfsLabelOf (@Nonnull final String path)
  162.       {
  163.         return path.replaceAll("^.*/", "");
  164.       }
  165.   }