Search in sources :

Example 1 with AudioTrackLoader

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);
    }
}
Also used : UrlPlayable(net.robinfriedli.aiode.audio.UrlPlayable) NoResultsFoundException(net.robinfriedli.aiode.exceptions.NoResultsFoundException) AudioTrackLoader(net.robinfriedli.aiode.audio.AudioTrackLoader) AudioTrack(com.sedmelluq.discord.lavaplayer.track.AudioTrack) AudioItem(com.sedmelluq.discord.lavaplayer.track.AudioItem) AudioPlaylist(com.sedmelluq.discord.lavaplayer.track.AudioPlaylist)

Example 2 with AudioTrackLoader

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);
}
Also used : CommandContext(net.robinfriedli.aiode.command.CommandContext) NativeAudioSendFactory(com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory) ThreadExecutionQueue(net.robinfriedli.aiode.concurrent.ThreadExecutionQueue) AudioTrackLoader(net.robinfriedli.aiode.audio.AudioTrackLoader) AudioPlayer(com.sedmelluq.discord.lavaplayer.player.AudioPlayer) Guild(net.dv8tion.jda.api.entities.Guild) AudioItem(com.sedmelluq.discord.lavaplayer.track.AudioItem) AudioManager(net.robinfriedli.aiode.audio.AudioManager) SpotifyApi(se.michaelthelin.spotify.SpotifyApi) LoggingUncaughtExceptionHandler(net.robinfriedli.aiode.exceptions.handler.handlers.LoggingUncaughtExceptionHandler) BlockingTrackLoadingExecutor(net.robinfriedli.aiode.audio.exec.BlockingTrackLoadingExecutor) PlayableFactory(net.robinfriedli.aiode.audio.PlayableFactory) IAudioSendSystem(net.dv8tion.jda.api.audio.factory.IAudioSendSystem) Aiode(net.robinfriedli.aiode.Aiode) QueuedTask(net.robinfriedli.aiode.concurrent.QueuedTask) AudioPlayerManager(com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager) InvalidCommandException(net.robinfriedli.aiode.exceptions.InvalidCommandException) Playable(net.robinfriedli.aiode.audio.Playable) Tuple2(groovy.lang.Tuple2) AudioTrack(com.sedmelluq.discord.lavaplayer.track.AudioTrack)

Example 3 with AudioTrackLoader

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;
}
Also used : NoResultsFoundException(net.robinfriedli.aiode.exceptions.NoResultsFoundException) AudioTrackLoader(net.robinfriedli.aiode.audio.AudioTrackLoader) AudioTrack(com.sedmelluq.discord.lavaplayer.track.AudioTrack) AudioTrackInfo(com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo) AudioPlaylist(com.sedmelluq.discord.lavaplayer.track.AudioPlaylist)

Example 4 with AudioTrackLoader

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);
}
Also used : ShowSimplified(se.michaelthelin.spotify.model_objects.specification.ShowSimplified) StringList(net.robinfriedli.stringlist.StringList) AudioTrackLoader(net.robinfriedli.aiode.audio.AudioTrackLoader) SearchResult(com.google.api.services.youtube.model.SearchResult) AudioItem(com.sedmelluq.discord.lavaplayer.track.AudioItem) FriendlyException(com.sedmelluq.discord.lavaplayer.tools.FriendlyException) SpotifyTrack(net.robinfriedli.aiode.audio.spotify.SpotifyTrack) Video(com.google.api.services.youtube.model.Video) AudioTrack(com.sedmelluq.discord.lavaplayer.track.AudioTrack) AudioPlaylist(com.sedmelluq.discord.lavaplayer.track.AudioPlaylist)

Example 5 with AudioTrackLoader

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();
}
Also used : AudioTrackLoader(net.robinfriedli.aiode.audio.AudioTrackLoader) AudioItem(com.sedmelluq.discord.lavaplayer.track.AudioItem) PlaylistItemListResponse(com.google.api.services.youtube.model.PlaylistItemListResponse) AudioTrack(com.sedmelluq.discord.lavaplayer.track.AudioTrack) PlaylistItem(com.google.api.services.youtube.model.PlaylistItem) AudioPlaylist(com.sedmelluq.discord.lavaplayer.track.AudioPlaylist)

Aggregations

AudioTrack (com.sedmelluq.discord.lavaplayer.track.AudioTrack)6 AudioTrackLoader (net.robinfriedli.aiode.audio.AudioTrackLoader)6 AudioItem (com.sedmelluq.discord.lavaplayer.track.AudioItem)5 AudioPlaylist (com.sedmelluq.discord.lavaplayer.track.AudioPlaylist)4 Video (com.google.api.services.youtube.model.Video)2 FriendlyException (com.sedmelluq.discord.lavaplayer.tools.FriendlyException)2 NoResultsFoundException (net.robinfriedli.aiode.exceptions.NoResultsFoundException)2 PlaylistItem (com.google.api.services.youtube.model.PlaylistItem)1 PlaylistItemListResponse (com.google.api.services.youtube.model.PlaylistItemListResponse)1 SearchResult (com.google.api.services.youtube.model.SearchResult)1 NativeAudioSendFactory (com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory)1 AudioPlayer (com.sedmelluq.discord.lavaplayer.player.AudioPlayer)1 AudioPlayerManager (com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager)1 AudioTrackInfo (com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo)1 Tuple2 (groovy.lang.Tuple2)1 Nullable (javax.annotation.Nullable)1 IAudioSendSystem (net.dv8tion.jda.api.audio.factory.IAudioSendSystem)1 Guild (net.dv8tion.jda.api.entities.Guild)1 Aiode (net.robinfriedli.aiode.Aiode)1 AudioManager (net.robinfriedli.aiode.audio.AudioManager)1