Implement "Create track in playlist" endpoint
This commit is contained in:
@ -6,7 +6,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- mp3_composer
|
- mp3_composer
|
||||||
ports:
|
ports:
|
||||||
- 9000:9000
|
- 9001:9001
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:alpine
|
image: postgres:alpine
|
||||||
container_name: composer_postgres
|
container_name: composer_postgres
|
||||||
@ -41,4 +41,4 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
mp3_composer:
|
mp3_composer:
|
||||||
driver: bridge
|
external: true
|
||||||
|
|||||||
18
pom.xml
18
pom.xml
@ -33,7 +33,15 @@
|
|||||||
<modelmapper.version>3.2.4</modelmapper.version>
|
<modelmapper.version>3.2.4</modelmapper.version>
|
||||||
<spring-dotenv.version>4.0.0</spring-dotenv.version>
|
<spring-dotenv.version>4.0.0</spring-dotenv.version>
|
||||||
<apache-tika.version>3.2.3</apache-tika.version>
|
<apache-tika.version>3.2.3</apache-tika.version>
|
||||||
|
<springdoc-openapi.version>2.8.5</springdoc-openapi.version>
|
||||||
|
<jaffree.version>2024.08.29</jaffree.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -98,6 +106,16 @@
|
|||||||
<artifactId>tika-parsers-standard-package</artifactId>
|
<artifactId>tika-parsers-standard-package</artifactId>
|
||||||
<version>${apache-tika.version}</version>
|
<version>${apache-tika.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
<version>${springdoc-openapi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.kokorin.jaffree</groupId>
|
||||||
|
<artifactId>jaffree</artifactId>
|
||||||
|
<version>${jaffree.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
|
|||||||
@ -25,19 +25,24 @@ public class SecurityConfig {
|
|||||||
http.authorizeHttpRequests(auth -> auth
|
http.authorizeHttpRequests(auth -> auth
|
||||||
.anyRequest().authenticated())
|
.anyRequest().authenticated())
|
||||||
.httpBasic(Customizer.withDefaults())
|
.httpBasic(Customizer.withDefaults())
|
||||||
// TODO: Temporary to test API, remove after testing
|
|
||||||
.csrf(c -> c.disable());
|
.csrf(c -> c.disable());
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public CustomUserDetailsService customUserDetailsService(UserRepository repository) {
|
public CustomUserDetailsService customUserDetailsService(UserRepository repository) {
|
||||||
CustomUserDetails defaultUser = new CustomUserDetails(1, "user", passwordEncoder().encode("password"));
|
CustomUserDetails defaultUser1 = create(repository, 1, "user", passwordEncoder().encode("password"));
|
||||||
Optional<User> user = repository.findById(defaultUser.getId());
|
CustomUserDetails defaultUser2 = create(repository, 2, "user1", passwordEncoder().encode("password"));
|
||||||
if (user.isEmpty()) {
|
return new CustomUserDetailsService(defaultUser1, defaultUser2);
|
||||||
repository.save(new User(defaultUser.getUsername()));
|
|
||||||
}
|
}
|
||||||
return new CustomUserDetailsService(defaultUser);
|
|
||||||
|
private CustomUserDetails create(UserRepository repository, long id, String username, String password) {
|
||||||
|
CustomUserDetails userDetails = new CustomUserDetails(id, username, password);
|
||||||
|
Optional<User> user = repository.findById(userDetails.getId());
|
||||||
|
if (user.isEmpty()) {
|
||||||
|
repository.save(new User(userDetails.getUsername()));
|
||||||
|
}
|
||||||
|
return userDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.bivashy.backend.composer.controller;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.dto.track.AddLocalTrackRequest;
|
||||||
|
import com.bivashy.backend.composer.dto.track.TrackResponse;
|
||||||
|
import com.bivashy.backend.composer.model.User;
|
||||||
|
import com.bivashy.backend.composer.service.TrackService;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
public class TrackController {
|
||||||
|
private final TrackService trackService;
|
||||||
|
|
||||||
|
public TrackController(TrackService trackService) {
|
||||||
|
this.trackService = trackService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(path = "/playlist/{playlistId}/track", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
public ResponseEntity<TrackResponse> addLocalTrack(
|
||||||
|
@AuthenticationPrincipal User user,
|
||||||
|
@PathVariable Long playlistId,
|
||||||
|
@ModelAttribute AddLocalTrackRequest request) throws IOException {
|
||||||
|
TrackResponse response = trackService.addLocalTrack(user, playlistId, request);
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.bivashy.backend.composer.dto.track;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record AddLocalTrackRequest(
|
||||||
|
@NotNull MultipartFile source) {
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.bivashy.backend.composer.dto.track;
|
||||||
|
|
||||||
|
public record TrackResponse(
|
||||||
|
Long trackId,
|
||||||
|
String title,
|
||||||
|
String artist,
|
||||||
|
String audioPath,
|
||||||
|
Integer durationSeconds,
|
||||||
|
String fileName) {
|
||||||
|
}
|
||||||
@ -28,6 +28,9 @@ public class TrackMetadata {
|
|||||||
@Column(nullable = false, length = 500)
|
@Column(nullable = false, length = 500)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@Column(length = 500)
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
@Column(name = "audio_path", nullable = false, length = 500)
|
@Column(name = "audio_path", nullable = false, length = 500)
|
||||||
private String audioPath;
|
private String audioPath;
|
||||||
|
|
||||||
@ -46,10 +49,12 @@ public class TrackMetadata {
|
|||||||
TrackMetadata() {
|
TrackMetadata() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackMetadata(Track track, String title, String audioPath, String artist, String thumbnailPath,
|
public TrackMetadata(Track track, String title, String fileName, String audioPath, String artist,
|
||||||
|
String thumbnailPath,
|
||||||
Integer durationSeconds) {
|
Integer durationSeconds) {
|
||||||
this.track = track;
|
this.track = track;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.fileName = fileName;
|
||||||
this.audioPath = audioPath;
|
this.audioPath = audioPath;
|
||||||
this.artist = artist;
|
this.artist = artist;
|
||||||
this.thumbnailPath = thumbnailPath;
|
this.thumbnailPath = thumbnailPath;
|
||||||
@ -68,6 +73,10 @@ public class TrackMetadata {
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAudioPath() {
|
public String getAudioPath() {
|
||||||
return audioPath;
|
return audioPath;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,67 @@
|
|||||||
|
package com.bivashy.backend.composer.model;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.key.PlaylistTrackId;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.IdClass;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "playlist_track")
|
||||||
|
@IdClass(PlaylistTrackId.class)
|
||||||
|
public class TrackPlaylist {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "playlist_id", nullable = false)
|
||||||
|
private Long playlistId;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "track_id", nullable = false)
|
||||||
|
private Long trackId;
|
||||||
|
|
||||||
|
@Column(name = "order_index", nullable = false)
|
||||||
|
private Long order;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "playlist_id", insertable = false, updatable = false)
|
||||||
|
private Playlist playlist;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "track_id", insertable = false, updatable = false)
|
||||||
|
private Track track;
|
||||||
|
|
||||||
|
TrackPlaylist() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackPlaylist(Long playlistId, Long trackId, Long order) {
|
||||||
|
this.playlistId = playlistId;
|
||||||
|
this.trackId = trackId;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPlaylistId() {
|
||||||
|
return playlistId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTrackId() {
|
||||||
|
return trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Playlist getPlaylist() {
|
||||||
|
return playlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Track getTrack() {
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package com.bivashy.backend.composer.model.key;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class PlaylistTrackId implements Serializable {
|
||||||
|
private Long playlistId;
|
||||||
|
private Long trackId;
|
||||||
|
|
||||||
|
PlaylistTrackId() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlaylistTrackId(Long playlistId, Long trackId) {
|
||||||
|
this.playlistId = playlistId;
|
||||||
|
this.trackId = trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getPlaylistId() {
|
||||||
|
return playlistId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlaylistId(Long playlistId) {
|
||||||
|
this.playlistId = playlistId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getTrackId() {
|
||||||
|
return trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrackId(Long trackId) {
|
||||||
|
this.trackId = trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((playlistId == null) ? 0 : playlistId.hashCode());
|
||||||
|
result = prime * result + ((trackId == null) ? 0 : trackId.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
PlaylistTrackId other = (PlaylistTrackId) obj;
|
||||||
|
if (playlistId == null) {
|
||||||
|
if (other.playlistId != null)
|
||||||
|
return false;
|
||||||
|
} else if (!playlistId.equals(other.playlistId))
|
||||||
|
return false;
|
||||||
|
if (trackId == null) {
|
||||||
|
if (other.trackId != null)
|
||||||
|
return false;
|
||||||
|
} else if (!trackId.equals(other.trackId))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.bivashy.backend.composer.repository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.SourceType;
|
||||||
|
|
||||||
|
public interface SourceTypeRepository extends JpaRepository<SourceType, Long> {
|
||||||
|
Optional<SourceType> findByName(String name);
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.bivashy.backend.composer.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.TrackMetadata;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TrackMetadataRepository extends JpaRepository<TrackMetadata, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package com.bivashy.backend.composer.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.TrackPlaylist;
|
||||||
|
import com.bivashy.backend.composer.model.key.PlaylistTrackId;
|
||||||
|
|
||||||
|
public interface TrackPlaylistRepository extends JpaRepository<TrackPlaylist, PlaylistTrackId> {
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.bivashy.backend.composer.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.Track;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TrackRepository extends JpaRepository<Track, Long> {
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.bivashy.backend.composer.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.model.TrackSource;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface TrackSourceRepository extends JpaRepository<TrackSource, Long> {
|
||||||
|
}
|
||||||
@ -1,5 +1,8 @@
|
|||||||
package com.bivashy.backend.composer.service;
|
package com.bivashy.backend.composer.service;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
@ -9,15 +12,18 @@ import com.bivashy.backend.composer.auth.CustomUserDetails;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomUserDetailsService implements UserDetailsService {
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
private final CustomUserDetails defaultUser;
|
private final List<CustomUserDetails> users;
|
||||||
|
|
||||||
public CustomUserDetailsService(CustomUserDetails defaultUser) {
|
public CustomUserDetailsService(CustomUserDetails... users) {
|
||||||
this.defaultUser = defaultUser;
|
this.users = Arrays.asList(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
return defaultUser;
|
return users.stream()
|
||||||
|
.filter(user -> user.getUsername().equals(username))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.bivashy.backend.composer.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.github.kokorin.jaffree.ffprobe.FFprobe;
|
||||||
|
import com.github.kokorin.jaffree.ffprobe.FFprobeResult;
|
||||||
|
import com.github.kokorin.jaffree.ffprobe.Format;
|
||||||
|
import com.github.kokorin.jaffree.ffprobe.PipeInput;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MetadataParseService {
|
||||||
|
public Optional<Metadata> extractMetadata(InputStream input) throws IOException {
|
||||||
|
FFprobeResult result = FFprobe.atPath()
|
||||||
|
.setShowFormat(true)
|
||||||
|
.setInput(PipeInput.pumpFrom(input))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
Format format = result.getFormat();
|
||||||
|
|
||||||
|
if (format == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(new Metadata(format.getTag("title"), format.getTag("artist"), format.getDuration()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static record Metadata(String title, String artist, Float durationSeconds) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package com.bivashy.backend.composer.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.bivashy.backend.composer.dto.track.AddLocalTrackRequest;
|
||||||
|
import com.bivashy.backend.composer.dto.track.TrackResponse;
|
||||||
|
import com.bivashy.backend.composer.model.SourceType;
|
||||||
|
import com.bivashy.backend.composer.model.SourceTypes;
|
||||||
|
import com.bivashy.backend.composer.model.Track;
|
||||||
|
import com.bivashy.backend.composer.model.TrackMetadata;
|
||||||
|
import com.bivashy.backend.composer.model.TrackPlaylist;
|
||||||
|
import com.bivashy.backend.composer.model.TrackSource;
|
||||||
|
import com.bivashy.backend.composer.model.User;
|
||||||
|
import com.bivashy.backend.composer.repository.SourceTypeRepository;
|
||||||
|
import com.bivashy.backend.composer.repository.TrackMetadataRepository;
|
||||||
|
import com.bivashy.backend.composer.repository.TrackPlaylistRepository;
|
||||||
|
import com.bivashy.backend.composer.repository.TrackRepository;
|
||||||
|
import com.bivashy.backend.composer.repository.TrackSourceRepository;
|
||||||
|
import com.bivashy.backend.composer.service.MetadataParseService.Metadata;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TrackService {
|
||||||
|
private final AudioBlobStorageService s3Service;
|
||||||
|
private final TrackSourceRepository trackSourceRepository;
|
||||||
|
private final TrackRepository trackRepository;
|
||||||
|
private final TrackMetadataRepository trackMetadataRepository;
|
||||||
|
private final TrackPlaylistRepository trackPlaylistRepository;
|
||||||
|
private final SourceTypeRepository sourceTypeRepository;
|
||||||
|
private final MetadataParseService metadataParseService;
|
||||||
|
|
||||||
|
public TrackService(AudioBlobStorageService s3Service, TrackSourceRepository trackSourceRepository,
|
||||||
|
TrackRepository trackRepository, TrackMetadataRepository trackMetadataRepository,
|
||||||
|
TrackPlaylistRepository trackPlaylistRepository,
|
||||||
|
SourceTypeRepository sourceTypeRepository, MetadataParseService metadataParseService) {
|
||||||
|
this.s3Service = s3Service;
|
||||||
|
this.trackSourceRepository = trackSourceRepository;
|
||||||
|
this.trackRepository = trackRepository;
|
||||||
|
this.trackMetadataRepository = trackMetadataRepository;
|
||||||
|
this.trackPlaylistRepository = trackPlaylistRepository;
|
||||||
|
this.sourceTypeRepository = sourceTypeRepository;
|
||||||
|
this.metadataParseService = metadataParseService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackResponse addLocalTrack(User user, Long playlistId, AddLocalTrackRequest request) throws IOException {
|
||||||
|
String audioPath = s3Service.store(request.source().getBytes());
|
||||||
|
|
||||||
|
Optional<Metadata> metadata = metadataParseService.extractMetadata(request.source().getInputStream());
|
||||||
|
|
||||||
|
Optional<SourceType> possibleSourceType = sourceTypeRepository.findByName(SourceTypes.FILE);
|
||||||
|
if (possibleSourceType.isEmpty()) {
|
||||||
|
throw new IllegalStateException("cannot find source type " + SourceTypes.FILE);
|
||||||
|
}
|
||||||
|
SourceType sourceType = possibleSourceType.get();
|
||||||
|
TrackSource trackSource = new TrackSource(audioPath, sourceType, LocalDateTime.now());
|
||||||
|
trackSource = trackSourceRepository.save(trackSource);
|
||||||
|
|
||||||
|
Track track = new Track(trackSource);
|
||||||
|
track = trackRepository.save(track);
|
||||||
|
|
||||||
|
String fileName = request.source().getOriginalFilename();
|
||||||
|
String title = metadata.map(m -> m.title()).orElse(fileName);
|
||||||
|
String artist = metadata.map(m -> m.artist()).orElse(null);
|
||||||
|
String thumbnailPath = null; // TODO:?
|
||||||
|
int durationSeconds = metadata.map(m -> m.durationSeconds()).map(Float::intValue).orElse(0);
|
||||||
|
|
||||||
|
TrackMetadata trackMetadata = new TrackMetadata(track, title, fileName, audioPath,
|
||||||
|
artist, thumbnailPath,
|
||||||
|
durationSeconds);
|
||||||
|
trackMetadata = trackMetadataRepository.save(trackMetadata);
|
||||||
|
|
||||||
|
// TODO: use linked list instead of int order?
|
||||||
|
TrackPlaylist playlistTrack = new TrackPlaylist(playlistId, track.getId(), /* order */ 0L);
|
||||||
|
playlistTrack = trackPlaylistRepository.save(playlistTrack);
|
||||||
|
|
||||||
|
return new TrackResponse(
|
||||||
|
track.getId(),
|
||||||
|
trackMetadata.getTitle(),
|
||||||
|
trackMetadata.getArtist(),
|
||||||
|
trackMetadata.getAudioPath(),
|
||||||
|
trackMetadata.getDurationSeconds(),
|
||||||
|
trackMetadata.getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -40,6 +40,7 @@ CREATE TABLE IF NOT EXISTS "track_metadata" (
|
|||||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
"track_id" bigint NOT NULL,
|
"track_id" bigint NOT NULL,
|
||||||
"title" varchar(500) NOT NULL,
|
"title" varchar(500) NOT NULL,
|
||||||
|
"file_name" varchar(500) NOT NULL,
|
||||||
"audio_path" varchar(500) NOT NULL,
|
"audio_path" varchar(500) NOT NULL,
|
||||||
"artist" varchar(500),
|
"artist" varchar(500),
|
||||||
"thumbnail_path" varchar(500),
|
"thumbnail_path" varchar(500),
|
||||||
@ -51,17 +52,19 @@ CREATE TABLE IF NOT EXISTS "track_metadata" (
|
|||||||
CREATE TABLE IF NOT EXISTS "playlist" (
|
CREATE TABLE IF NOT EXISTS "playlist" (
|
||||||
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
"id" bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||||
"owner_id" bigint NOT NULL,
|
"owner_id" bigint NOT NULL,
|
||||||
"title" varchar(500) NOT NULL UNIQUE,
|
"title" varchar(500) NOT NULL,
|
||||||
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
"created_at" timestamp NOT NULL DEFAULT NOW(),
|
||||||
"updated_at" timestamp NOT NULL DEFAULT NOW(),
|
"updated_at" timestamp NOT NULL DEFAULT NOW(),
|
||||||
CONSTRAINT "fk_playlist_owner_id"
|
CONSTRAINT "fk_playlist_owner_id"
|
||||||
FOREIGN KEY ("owner_id") REFERENCES "users" ("id")
|
FOREIGN KEY ("owner_id") REFERENCES "users" ("id"),
|
||||||
|
CONSTRAINT "uq_playlist_owner_title"
|
||||||
|
UNIQUE ("owner_id", "title")
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "playlist_track" (
|
CREATE TABLE IF NOT EXISTS "playlist_track" (
|
||||||
"playlist_id" bigint NOT NULL,
|
"playlist_id" bigint NOT NULL,
|
||||||
"track_id" bigint NOT NULL,
|
"track_id" bigint NOT NULL,
|
||||||
"order" bigint NOT NULL,
|
"order_index" bigint NOT NULL,
|
||||||
CONSTRAINT "pk_playlist_track" PRIMARY KEY ("playlist_id", "track_id"),
|
CONSTRAINT "pk_playlist_track" PRIMARY KEY ("playlist_id", "track_id"),
|
||||||
CONSTRAINT "fk_playlist_track_playlist_id"
|
CONSTRAINT "fk_playlist_track_playlist_id"
|
||||||
FOREIGN KEY ("playlist_id") REFERENCES "playlist" ("id"),
|
FOREIGN KEY ("playlist_id") REFERENCES "playlist" ("id"),
|
||||||
|
|||||||
Reference in New Issue
Block a user