use of net.robinfriedli.aiode.audio.spotify.SpotifyTrackKind in project aiode by robinfriedli.
the class Playlist method asTrackList.
/**
* returns all Songs as Spotify tracks including all videos that are redirected Spotify tracks i.e. the attribute
* redirectedSpotifyId is set. Mind that this method has to be invoked with client credentials
*/
public List<SpotifyTrack> asTrackList(SpotifyApi spotifyApi) {
SpotifyTrackBulkLoadingService service = new SpotifyTrackBulkLoadingService(spotifyApi);
List<SpotifyTrack> tracks = Lists.newArrayList();
for (PlaylistItem item : getItemsSorted()) {
if (item instanceof Song) {
String id = ((Song) item).getId();
service.add(createItem(id, TRACK), tracks::add);
} else if (item instanceof Episode) {
String id = ((Episode) item).getId();
service.add(createItem(id, EPISODE), tracks::add);
} else if (item instanceof Video && ((Video) item).getRedirectedSpotifyId() != null) {
Video video = (Video) item;
String redirectedSpotifyId = video.getRedirectedSpotifyId();
SpotifyItemKind kindEntity = video.getRedirectedSpotifyKind();
SpotifyTrackKind kind = kindEntity != null ? kindEntity.asEnum() : TRACK;
service.add(createItem(redirectedSpotifyId, kind), tracks::add);
}
}
service.perform();
return tracks;
}
use of net.robinfriedli.aiode.audio.spotify.SpotifyTrackKind in project aiode by robinfriedli.
the class Playlist method getTracks.
/**
* Returns the items in this playlist as objects supported by the {@link PlayableFactory} class. Note that getting the
* Spotify track for a Song requires this method to be invoked with client credentials
*/
public List<Object> getTracks(SpotifyApi spotifyApi) {
List<PlaylistItem> playlistItems = getItemsSorted();
SpotifyTrackBulkLoadingService service = new SpotifyTrackBulkLoadingService(spotifyApi);
Map<Object, Integer> itemsWithIndex = new HashMap<>();
for (int i = 0; i < playlistItems.size(); i++) {
PlaylistItem item = playlistItems.get(i);
if (item instanceof Song) {
String id = ((Song) item).getId();
int finalI = i;
service.add(createItem(id, TRACK), track -> itemsWithIndex.put(track, finalI));
} else if (item instanceof Episode) {
String id = ((Episode) item).getId();
int finalI = i;
service.add(createItem(id, EPISODE), track -> itemsWithIndex.put(track, finalI));
} else if (item instanceof Video) {
Video video = (Video) item;
YouTubeVideo youtubeVideo = video.asYouTubeVideo();
itemsWithIndex.put(youtubeVideo, i);
String spotifyId = video.getRedirectedSpotifyId();
if (!Strings.isNullOrEmpty(spotifyId)) {
SpotifyItemKind kindEntity = video.getRedirectedSpotifyKind();
SpotifyTrackKind kind = kindEntity != null ? kindEntity.asEnum() : TRACK;
service.add(createItem(spotifyId, kind), youtubeVideo::setRedirectedSpotifyTrack);
}
} else if (item instanceof UrlTrack) {
itemsWithIndex.put(item, i);
}
}
service.perform();
return itemsWithIndex.keySet().stream().sorted(Comparator.comparing(itemsWithIndex::get)).collect(Collectors.toList());
}
use of net.robinfriedli.aiode.audio.spotify.SpotifyTrackKind in project aiode by robinfriedli.
the class ChartsCommand method getTrackForRecord.
private Playable getTrackForRecord(Object[] record, Session session) throws Exception {
long sourceEntityPk = (Long) record[0];
PlaybackHistorySource sourceEntity = session.load(PlaybackHistorySource.class, sourceEntityPk);
Playable.Source source = sourceEntity.asEnum();
String id = (String) record[1];
Long spotifyItemKindPk = (Long) record[3];
SpotifyItemKind spotifyItemKind = spotifyItemKindPk != null ? session.load(SpotifyItemKind.class, spotifyItemKindPk) : null;
switch(source) {
case SPOTIFY:
return runWithCredentials(() -> {
if (spotifyItemKind == null) {
throw new IllegalStateException("spotifyItemKind cannot be null for PlaybackHistory entries of source SPOTIFY");
}
SpotifyTrackKind kind = spotifyItemKind.asEnum();
SpotifyService spotifyService = getSpotifyService();
SpotifyTrack track = kind.loadSingleItem(spotifyService, id);
if (track == null) {
return null;
}
return new PlayableTrackWrapper(track);
});
case YOUTUBE:
YouTubeService youTubeService = Aiode.get().getAudioManager().getYouTubeService();
try {
return youTubeService.getVideoForId(id);
} catch (FriendlyException e) {
return null;
}
case URL:
return playableFactory.createPlayable(id, getContext().getSpotifyApi(), false);
}
throw new UnsupportedOperationException("Unsupported source " + sourceEntity);
}
use of net.robinfriedli.aiode.audio.spotify.SpotifyTrackKind in project aiode by robinfriedli.
the class RefreshSpotifyRedirectIndicesTask method run.
@Override
protected void run(JobExecutionContext jobExecutionContext) {
logger.info("Starting SpotifyRedirectIndex refresh");
SessionFactory sessionFactory = StaticSessionProvider.getSessionFactory();
SpotifyRedirectIndexModificationLock spotifyRedirectIndexModificationLock = new SpotifyRedirectIndexModificationLock();
spotifyRedirectIndexModificationLock.setCreationTimeStamp(LocalDateTime.now());
try (Session session = sessionFactory.openSession()) {
Transaction transaction = session.beginTransaction();
session.persist(spotifyRedirectIndexModificationLock);
transaction.commit();
}
try {
Aiode aiode = Aiode.get();
Stopwatch stopwatch = Stopwatch.createStarted();
YouTubeService youTubeService = aiode.getAudioManager().getYouTubeService();
SpotifyTrackBulkLoadingService spotifyTrackBulkLoadingService = new SpotifyTrackBulkLoadingService(spotifyApi, true);
LocalDate currentDate = LocalDate.now();
LocalDate date4WeeksAgo = currentDate.minusDays(28);
StaticSessionProvider.consumeSession(session -> {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<SpotifyRedirectIndex> query = cb.createQuery(SpotifyRedirectIndex.class);
Root<SpotifyRedirectIndex> root = query.from(SpotifyRedirectIndex.class);
query.where(cb.lessThan(root.get("lastUpdated"), date4WeeksAgo));
query.orderBy(cb.asc(root.get("lastUpdated")));
List<SpotifyRedirectIndex> indices = session.createQuery(query).setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE)).getResultList();
if (indices.isEmpty()) {
return;
}
BigDecimal averageDailyIndices = (BigDecimal) session.createSQLQuery("select avg(count) from (select count(*) as count from spotify_redirect_index group by last_updated) as sub").uniqueResult();
int average = averageDailyIndices.setScale(0, RoundingMode.CEILING).intValue();
int updateCount = 0;
for (SpotifyRedirectIndex index : indices) {
SpotifyTrackKind kind = index.getSpotifyItemKind().asEnum();
RefreshTrackIndexTask task = new RefreshTrackIndexTask(session, index, youTubeService);
String spotifyId = index.getSpotifyId();
if (!Strings.isNullOrEmpty(spotifyId)) {
spotifyTrackBulkLoadingService.add(createItem(spotifyId, kind), task);
} else {
session.delete(index);
}
++updateCount;
if (updateCount == average) {
break;
}
}
spotifyTrackBulkLoadingService.perform();
stopwatch.stop();
logger.info(String.format("Regenerated %d spotify redirect indices in %d seconds", updateCount, stopwatch.elapsed(TimeUnit.SECONDS)));
});
} finally {
Transaction transaction = null;
try (Session session = sessionFactory.openSession()) {
transaction = session.beginTransaction();
// since hibernate is now bootstrapped by JPA rather than native after implementing spring boot
// the entity has the be merged because JPA does not allow the deletion of detached entities
Object merge = session.merge(spotifyRedirectIndexModificationLock);
session.delete(merge);
transaction.commit();
} catch (Throwable e) {
// catch exceptions thrown in the finally block so as to not override exceptions thrown in the try block
logger.error("Exception thrown while deleting SpotifyRedirectIndexModificationLock", e);
if (transaction != null) {
transaction.rollback();
}
}
}
}
use of net.robinfriedli.aiode.audio.spotify.SpotifyTrackKind in project aiode by robinfriedli.
the class PlayableFactory method createPlayable.
/**
* Create a single playable for any url. If the url is either an open.spotify or YouTube URL this extracts the ID
* and uses the familiar methods to load the Playables, otherwise this method uses the {@link AudioTrackLoader}
* to load the {@link AudioTrack}s using lavaplayer and wraps them in {@link UrlPlayable}s
*/
@Nullable
public Playable createPlayable(String url, SpotifyApi spotifyApi, boolean redirectSpotify) throws IOException {
URI uri;
try {
uri = URI.create(url);
} catch (IllegalArgumentException e) {
throw new InvalidCommandException("'" + url + "' is not a valid URL");
}
if (uri.getHost().contains("youtube.com")) {
Map<String, String> parameterMap = getParameterMap(uri);
String videoId = parameterMap.get("v");
if (videoId != null) {
return youTubeService.getVideoForId(videoId);
} else {
throw new IllegalArgumentException("Detected YouTube URL but no video id provided");
}
} else if (uri.getHost().equals("youtu.be")) {
String[] parts = uri.getPath().split("/");
return youTubeService.requireVideoForId(parts[parts.length - 1]);
} else if (uri.getHost().equals("open.spotify.com")) {
StringList pathFragments = StringList.createWithRegex(uri.getPath(), "/");
SpotifyTrackKind kind;
String trackId;
if (pathFragments.contains("track")) {
trackId = pathFragments.tryGet(pathFragments.indexOf("track") + 1);
kind = SpotifyTrackKind.TRACK;
} else if (pathFragments.contains("episode")) {
trackId = pathFragments.tryGet(pathFragments.indexOf("episode") + 1);
kind = SpotifyTrackKind.EPISODE;
} else {
throw new IllegalArgumentException("Detected Spotify URL but no track id provided");
}
if (trackId == null) {
throw new InvalidCommandException("No track id provided");
}
try {
String accessToken = spotifyApi.clientCredentials().build().execute().getAccessToken();
spotifyApi.setAccessToken(accessToken);
if (kind == SpotifyTrackKind.TRACK) {
Track track = spotifyService.getTrack(trackId);
return createPlayable(redirectSpotify, track);
} else // noinspection ConstantConditions
if (kind == SpotifyTrackKind.EPISODE) {
Episode episode = spotifyService.getEpisode(trackId);
return createPlayable(redirectSpotify, episode);
} else {
throw new UnsupportedOperationException("unsupported open.spotify URL kind: " + kind);
}
} catch (IOException | SpotifyWebApiException | ParseException e) {
throw new RuntimeException("Exception during Spotify request", e);
} finally {
spotifyApi.setAccessToken(null);
}
} else {
AudioItem audioItem = audioTrackLoader.loadByIdentifier(uri.toString());
if (audioItem instanceof AudioTrack) {
return new UrlPlayable((AudioTrack) audioItem);
} else if (audioItem != null) {
throw new IllegalArgumentException("Loading provided url did not result in an AudioTrack but " + audioItem.getClass().toString());
} else {
return null;
}
}
}
Aggregations