Skip to content

Method: getMetadata()

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:
29: import javax.annotation.Nonnegative;
30: import javax.annotation.Nonnull;
31: import javax.annotation.concurrent.Immutable;
32: import javax.inject.Inject;
33: import java.time.Duration;
34: import java.util.Optional;
35: import java.io.IOException;
36: import java.nio.file.Files;
37: import java.nio.file.Path;
38: import org.springframework.beans.factory.annotation.Configurable;
39: import org.springframework.core.io.FileSystemResource;
40: import org.springframework.core.io.Resource;
41: import org.eclipse.rdf4j.query.BindingSet;
42: import org.eclipse.rdf4j.repository.Repository;
43: import it.tidalwave.util.Id;
44: import it.tidalwave.util.Memoize;
45: import it.tidalwave.bluemarine2.util.Formatters;
46: import it.tidalwave.bluemarine2.model.MediaFileSystem;
47: import it.tidalwave.bluemarine2.model.audio.AudioFile;
48: import it.tidalwave.bluemarine2.model.audio.Record;
49: import it.tidalwave.bluemarine2.model.finder.audio.MusicArtistFinder;
50: import it.tidalwave.bluemarine2.model.impl.AudioMetadataFactory;
51: import it.tidalwave.bluemarine2.model.impl.catalog.finder.RepositoryMusicArtistFinder;
52: import it.tidalwave.bluemarine2.model.impl.catalog.finder.RepositoryRecordFinder;
53: import it.tidalwave.bluemarine2.model.spi.MetadataSupport;
54: import it.tidalwave.bluemarine2.model.spi.PathAwareEntity;
55: import lombok.EqualsAndHashCode;
56: import lombok.Getter;
57: import lombok.extern.slf4j.Slf4j;
58: import static it.tidalwave.bluemarine2.util.PathNormalization.*;
59: import static it.tidalwave.bluemarine2.model.MediaItem.Metadata.*;
60:
61: /***********************************************************************************************************************
62: *
63: * An implementation of {@link AudioFile} that is mapped to a {@link Repository}.
64: *
65: * @stereotype Datum
66: *
67: * @author Fabrizio Giudici
68: *
69: **********************************************************************************************************************/
70: @Immutable @Configurable @EqualsAndHashCode(of = { "path", "trackId" }, callSuper = false) @Slf4j
71: public class RepositoryAudioFile extends RepositoryEntitySupport implements AudioFile
72: {
73: @Getter @Nonnull
74: private final Path path; // FIXME: rename to relativePath?
75:
76: @Getter @Nonnull
77: private final Metadata metadata;
78:
79: @Nonnull
80: private final Optional<Id> trackId;
81:
82: @Nonnull
83: private final Optional<Duration> duration;
84:
85: @Nonnull
86: private final Optional<Long> fileSize;
87:
88: private final Memoize<Metadata> fallbackMetadata = new Memoize<>();
89:
90: @Inject
91: private MediaFileSystem fileSystem;
92:
93: public RepositoryAudioFile (@Nonnull final Repository repository, @Nonnull final BindingSet bindingSet)
94: {
95: super(repository, bindingSet, "audioFile", rdfsLabelOf(bindingSet.getBinding("path").getValue().stringValue()));
96: this.path = toPath(bindingSet.getBinding("path"));
97: this.duration = toDuration(bindingSet.getBinding("duration"));
98: this.fileSize = toLong(bindingSet.getBinding("fileSize"));
99: this.trackId = toId(bindingSet.getBinding("track"));
100:
101: this.metadata = new MetadataSupport(path).with(TITLE, rdfsLabel)
102: .with(DURATION, duration)
103: .with(FILE_SIZE, fileSize)
104: .withFallback(key -> fallbackMetadata.get(this::loadFallbackMetadata));
105: }
106:
107: @Override @Nonnull
108: public AudioFile getAudioFile()
109: {
110: return this;
111: }
112:
113: @Override @Nonnull
114: public Optional<Resource> getContent()
115: throws IOException
116: {
117: final Path absolutePath = fixedPath(getAbsolutePath());
118: return Files.exists(absolutePath) ? Optional.of(new FileSystemResource(absolutePath.toFile())) : Optional.empty();
119: }
120:
121: @Override @Nonnegative
122: public long getSize()
123: throws IOException
124: {
125: return Files.size(fixedPath(getAbsolutePath()));
126: }
127:
128: @Override @Nonnull
129: public MusicArtistFinder findMakers()
130: {
131: return new RepositoryMusicArtistFinder(repository).makerOf(trackId.get());
132: }
133:
134: @Override @Nonnull
135: public MusicArtistFinder findComposers()
136: {
137: return new RepositoryMusicArtistFinder(repository).makerOf(trackId.get());
138: // return new RepositoryAudioFileArtistFinder(this); FIXME
139: }
140:
141: @Override @Nonnull
142: public Optional<Record> getRecord()
143: {
144: return trackId.flatMap(tid -> new RepositoryRecordFinder(repository).containingTrack(tid).optionalFirstResult());
145: }
146:
147: @Override @Nonnull
148: public Optional<PathAwareEntity> getParent() // FIXME: drop it
149: {
150: throw new UnsupportedOperationException();
151: }
152:
153: @Override @Nonnull
154: public String toString()
155: {
156: return String.format("RepositoryAudioFileEntity(%s, %s)", path, id);
157: }
158:
159: @Override @Nonnull
160: public String toDumpString()
161: {
162: return String.format("%s %8s %s %s - %s", duration.map(Formatters::format).orElse("??:??"),
163: fileSize.map(Object::toString).orElse(""),
164: id, path, rdfsLabel);
165: }
166:
167: @Nonnull
168: private Metadata loadFallbackMetadata()
169: {
170: final Path absolutePath = getAbsolutePath();
171: log.debug(">>>> loading fallback metadata from: {}", absolutePath);
172: // Don't check for file existence, it would fail for some files - see BMT-46. AudioMetadataFactory does all.
173: return AudioMetadataFactory.loadFrom(absolutePath);
174: }
175:
176: @Nonnull
177: private Path getAbsolutePath()
178: {
179: return fileSystem.getRootPath().resolve(path);
180: }
181:
182: @Nonnull
183: private static String rdfsLabelOf (@Nonnull final String path)
184: {
185: return path.replaceAll("^.*/", "");
186: }
187: }