use of net.robinfriedli.aiode.audio.AudioTrackLoader in project aiode by robinfriedli.
the class AbstractPlayableLoadingCommand method loadSoundCloudTrack.
private void loadSoundCloudTrack(AudioManager audioManager) {
AudioTrackLoader audioTrackLoader = new AudioTrackLoader(audioManager.getPlayerManager());
String commandInput = getCommandInput();
AudioItem audioItem = audioTrackLoader.loadByIdentifier("scsearch:" + commandInput);
if (audioItem instanceof AudioTrack) {
AudioTrack audioTrack = (AudioTrack) audioItem;
handleResults(Lists.newArrayList(new UrlPlayable(audioTrack)));
this.loadedAudioTrack = audioTrack;
} else if (audioItem == null) {
throw new NoResultsFoundException(String.format("No soundcloud track found for '%s'", commandInput));
} else if (audioItem instanceof AudioPlaylist) {
int limit = getArgumentValueWithTypeOrElse("select", Integer.class, 20);
List<AudioTrack> tracks = ((AudioPlaylist) audioItem).getTracks();
if (tracks.isEmpty()) {
throw new NoResultsFoundException(String.format("No soundcloud track found for '%s'", commandInput));
}
if (tracks.size() > limit) {
tracks = tracks.subList(0, limit);
}
askQuestion(tracks, audioTrack -> audioTrack.getInfo().title, audioTrack -> audioTrack.getInfo().author);
}
}
use of net.robinfriedli.aiode.audio.AudioTrackLoader in project aiode by robinfriedli.
the class AudioTrafficSimulationCommand method runAdmin.
@Override
public void runAdmin() throws Exception {
Aiode aiode = Aiode.get();
AudioManager audioManager = aiode.getAudioManager();
AudioPlayerManager playerManager = audioManager.getPlayerManager();
PlayableFactory playableFactory = audioManager.createPlayableFactory(getSpotifyService(), new BlockingTrackLoadingExecutor());
CommandContext context = getContext();
Guild guild = context.getGuild();
ThreadExecutionQueue threadExecutionQueue = aiode.getExecutionQueueManager().getForGuild(guild);
SpotifyApi spotifyApi = context.getSpotifyApi();
AudioTrackLoader audioTrackLoader = new AudioTrackLoader(playerManager);
int streams = getArgumentValueOrElse("streams", DEFAULT_STREAM_COUNT);
int durationSecs = getArgumentValueOrElse("duration", DEFAULT_DURATION);
String playbackUrl = getArgumentValueOrElse("url", DEFAULT_PLAYBACK_URL);
Integer nativeBuffer = getArgumentValueWithTypeOrElse("nativeBuffer", Integer.class, null);
List<Playable> playables = playableFactory.createPlayables(playbackUrl, spotifyApi, true);
if (playables.isEmpty()) {
throw new InvalidCommandException("No playables found for url " + playbackUrl);
}
Playable playable = playables.get(0);
AudioItem audioItem = audioTrackLoader.loadByIdentifier(playable.getPlaybackUrl());
AudioTrack track;
if (audioItem instanceof AudioTrack) {
track = (AudioTrack) audioItem;
} else {
throw new IllegalStateException("Could not get AudioTrack for Playable " + playable);
}
LoopTrackListener loopTrackListener = new LoopTrackListener(track);
List<Thread> playbackThreads = nativeBuffer != null ? null : Lists.newArrayListWithCapacity(streams);
List<Tuple2<IAudioSendSystem, AudioPlayer>> audioSendSystems = nativeBuffer != null ? Lists.newArrayListWithCapacity(streams) : null;
NativeAudioSendFactory nativeAudioSendFactory = nativeBuffer != null ? new NativeAudioSendFactory(nativeBuffer) : null;
LoggingUncaughtExceptionHandler eh = new LoggingUncaughtExceptionHandler();
for (int i = 0; i < streams; i++) {
AudioPlayer player = playerManager.createPlayer();
player.addListener(loopTrackListener);
player.playTrack(track.makeClone());
if (nativeAudioSendFactory != null) {
IAudioSendSystem sendSystem = nativeAudioSendFactory.createSendSystem(new FakePacketProvider(player));
audioSendSystems.add(new Tuple2<>(sendSystem, player));
} else {
Thread playbackThread = new Thread(new PlayerPollingRunnable(player));
playbackThread.setDaemon(true);
playbackThread.setUncaughtExceptionHandler(eh);
playbackThread.setName("simulated-playback-thread-" + i);
playbackThreads.add(playbackThread);
}
}
QueuedTask playbackThreadsManagementTask = new QueuedTask(threadExecutionQueue, new FakePlayerManagementTask(playbackThreads, audioSendSystems, durationSecs)) {
@Override
protected boolean isPrivileged() {
return true;
}
};
playbackThreadsManagementTask.setName("simulated-playback-management-task");
threadExecutionQueue.add(playbackThreadsManagementTask, false);
}
use of net.robinfriedli.aiode.audio.AudioTrackLoader in project aiode by robinfriedli.
the class YouTubeService method searchVideosViaLavaplayer.
private List<YouTubeVideo> searchVideosViaLavaplayer(String searchTerm, int limit) {
AudioTrackLoader audioTrackLoader = new AudioTrackLoader(Aiode.get().getAudioManager().getPlayerManager());
Object result = audioTrackLoader.loadByIdentifier("ytsearch:" + searchTerm);
if (!(result instanceof AudioPlaylist) || ((AudioPlaylist) result).getTracks().isEmpty()) {
throw new NoResultsFoundException(String.format("No YouTube video found for '%s'", searchTerm));
}
List<AudioTrack> tracks = ((AudioPlaylist) result).getTracks();
List<YouTubeVideo> youTubeVideos = Lists.newArrayList();
for (int i = 0; i < tracks.size() && i < limit; i++) {
AudioTrack audioTrack = tracks.get(i);
AudioTrackInfo info = audioTrack.getInfo();
YouTubeVideo youTubeVideo = new YouTubeVideoImpl(info.title, audioTrack.getIdentifier(), audioTrack.getDuration());
youTubeVideo.setCached(audioTrack);
youTubeVideos.add(youTubeVideo);
}
return youTubeVideos;
}
use of net.robinfriedli.aiode.audio.AudioTrackLoader in project aiode by robinfriedli.
the class YouTubeService method redirectSpotify.
/**
* Workaround as Spotify does not allow full playback of tracks via third party APIs using the web api for licencing
* reasons. Gets the metadata and searches the corresponding YouTube video. The only way to stream from Spotify
* directly is by using the $preview argument with the {@link PlayCommand} or {@link QueueCommand} which plays the
* provided mp3 preview.
* <p>
* However Spotify MIGHT release an SDK supporting full playback of songs across all devices, not just browsers in
* which case this method and the corresponding block in {@link PlayableFactory#createPlayable(boolean, Object)} should
* be removed.
* For reference, see <a href="https://github.com/spotify/web-api/issues/57">Web playback of Full Tracks - Github</a>
* <p>
* This method searches 5 youtube videos using the Spotify track name + artist and then uses a combination of
* levenshtein distance, whether or not the channel title matches the artist, the view count and the index in the
* youtube response to determine the best match.
* <p>
* If the current YouTube API quota usage is beneath the threshold then this action
* will use the YouTube API, costing {@link #QUOTA_COST_SEARCH} + {@link #QUOTA_COST_LIST} (this also applies
* when searching with lavaplayer) quota. Else this uses lavaplayer to load the video metadata by scraping the HTML
* page returned by YouTube.
*
* @param youTubeVideo the hollow youtube that has already been added to the queue and awaits to receive values
*/
public void redirectSpotify(HollowYouTubeVideo youTubeVideo) throws IOException {
SpotifyTrack spotifyTrack = youTubeVideo.getRedirectedSpotifyTrack();
if (spotifyTrack == null) {
throw new IllegalArgumentException(youTubeVideo.toString() + " is not a placeholder for a redirected Spotify Track");
}
StringList artists = spotifyTrack.exhaustiveMatch(track -> StringList.create(track.getArtists(), ArtistSimplified::getName), episode -> {
ShowSimplified show = episode.getShow();
if (show != null) {
return StringList.of(show.getName());
}
return StringList.create();
});
String searchTerm = spotifyTrack.getName() + " " + artists.toSeparatedString(" ");
List<String> videoIds;
if (currentQuota.get() < quotaThreshold) {
YouTube.Search.List search = youTube.search().list(List.of("id", "snippet"));
search.setKey(apiKey);
search.setQ(searchTerm);
// set topic to filter results to music video
search.setTopicId("/m/04rlf");
search.setType(List.of("video"));
search.setFields("items(snippet/title,id/videoId)");
search.setMaxResults((long) REDIRECT_SEARCH_AMOUNT);
List<SearchResult> items = doWithQuota(QUOTA_COST_SEARCH, () -> search.execute().getItems());
if (items.isEmpty()) {
youTubeVideo.cancel();
return;
}
videoIds = items.stream().map(item -> item.getId().getVideoId()).collect(Collectors.toList());
} else {
AudioTrackLoader audioTrackLoader = new AudioTrackLoader(Aiode.get().getAudioManager().getPlayerManager());
AudioItem audioItem;
try {
audioItem = audioTrackLoader.loadByIdentifier("ytsearch:" + searchTerm);
} catch (FriendlyException e) {
youTubeVideo.cancel();
return;
}
if (!(audioItem instanceof AudioPlaylist)) {
youTubeVideo.cancel();
return;
}
AudioPlaylist resultList = (AudioPlaylist) audioItem;
List<AudioTrack> tracks = resultList.getTracks();
if (tracks.isEmpty()) {
youTubeVideo.cancel();
return;
}
List<AudioTrack> audioTracks = tracks.subList(0, Math.min(tracks.size(), REDIRECT_SEARCH_AMOUNT));
videoIds = audioTracks.stream().map(AudioTrack::getIdentifier).collect(Collectors.toList());
}
List<Video> videos = getAllVideos(videoIds);
if (videos.isEmpty()) {
youTubeVideo.cancel();
return;
}
Video video = getBestMatch(videos, spotifyTrack, artists);
String videoId = video.getId();
long durationMillis = getDurationMillis(videoId);
String artistString = artists.toSeparatedString(", ");
String title = spotifyTrack.getName() + " by " + artistString;
youTubeVideo.setTitle(title);
youTubeVideo.setId(videoId);
youTubeVideo.setDuration(durationMillis);
}
use of net.robinfriedli.aiode.audio.AudioTrackLoader in project aiode by robinfriedli.
the class YouTubeService method populateList.
/**
* Fetch the data for each {@link HollowYouTubeVideo} in the playlist. This method is typically applied to playlists
* returned by {@link #searchPlaylist(String)} or {@link #searchSeveralPlaylists(long, String)}. This action is
* typically performed asynchronously.
* If the current YouTube API quota usage is beneath the threshold then this action will use the YouTube API, costing
* ({@link #QUOTA_COST_LIST} (item search) + {@link #QUOTA_COST_LIST} (durations)) * (playlistSize / 50) quota.
* Else this uses lavaplayer to load the video metadata by scraping the HTML page returned by YouTube.
*
* @param playlist the playlist for which to load the data of the individual videos
*/
public void populateList(YouTubePlaylist playlist) throws IOException {
if (currentQuota.get() < quotaThreshold) {
YouTube.PlaylistItems.List itemSearch = youTube.playlistItems().list(List.of("snippet"));
itemSearch.setKey(apiKey);
itemSearch.setMaxResults(50L);
itemSearch.setFields("items(snippet/title,snippet/resourceId),nextPageToken");
itemSearch.setPlaylistId(playlist.getId());
String nextPageToken;
List<HollowYouTubeVideo> hollowVideos = playlist.getVideos();
int index = 0;
do {
PlaylistItemListResponse response = doWithQuota(QUOTA_COST_LIST, itemSearch::execute);
nextPageToken = response.getNextPageToken();
List<PlaylistItem> items = response.getItems();
List<HollowYouTubeVideo> currentVideos = Lists.newArrayList();
for (PlaylistItem item : items) {
String videoTitle = item.getSnippet().getTitle();
String videoId = item.getSnippet().getResourceId().getVideoId();
if (index < hollowVideos.size()) {
HollowYouTubeVideo hollowVideo = hollowVideos.get(index);
hollowVideo.setTitle(videoTitle);
hollowVideo.setId(videoId);
currentVideos.add(hollowVideo);
}
++index;
}
loadDurationsAsync(currentVideos);
itemSearch.setPageToken(nextPageToken);
if (Thread.currentThread().isInterrupted()) {
playlist.cancelLoading();
return;
}
} while (!Strings.isNullOrEmpty(nextPageToken));
} else {
AudioTrackLoader audioTrackLoader = new AudioTrackLoader(Aiode.get().getAudioManager().getPlayerManager());
AudioItem audioItem = audioTrackLoader.loadByIdentifier(playlist.getUrl());
if (audioItem instanceof AudioPlaylist) {
List<HollowYouTubeVideo> videos = playlist.getVideos();
List<AudioTrack> tracks = ((AudioPlaylist) audioItem).getTracks();
for (int i = 0; i < videos.size() && i < tracks.size(); i++) {
HollowYouTubeVideo video = videos.get(i);
AudioTrack track = tracks.get(i);
video.setTitle(track.getInfo().title);
video.setId(track.getIdentifier());
video.setDuration(track.getDuration());
video.setCached(track);
}
}
}
// finally cancel each video that couldn't be loaded e.g. if it's private
playlist.cancelLoading();
}
Aggregations