{"id":19829511,"url":"https://github.com/lavalink-devs/lavaplayer","last_synced_at":"2025-04-09T12:10:00.172Z","repository":{"id":184545612,"uuid":"671921104","full_name":"lavalink-devs/lavaplayer","owner":"lavalink-devs","description":"Lavaplayer fork maintained by Lavalink","archived":false,"fork":false,"pushed_at":"2024-05-22T15:50:43.000Z","size":17698,"stargazers_count":142,"open_issues_count":5,"forks_count":52,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-05-22T16:58:26.492Z","etag":null,"topics":["hacktoberfest","lavaplayer"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lavalink-devs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-28T12:58:17.000Z","updated_at":"2024-05-27T13:42:02.846Z","dependencies_parsed_at":"2024-01-11T11:44:44.589Z","dependency_job_id":"d04c4121-0fbb-4f8a-b0f3-2ccf1499b861","html_url":"https://github.com/lavalink-devs/lavaplayer","commit_stats":null,"previous_names":["lavalink-devs/lavaplayer"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lavalink-devs%2Flavaplayer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lavalink-devs%2Flavaplayer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lavalink-devs%2Flavaplayer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lavalink-devs%2Flavaplayer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lavalink-devs","download_url":"https://codeload.github.com/lavalink-devs/lavaplayer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248036067,"owners_count":21037092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest","lavaplayer"],"created_at":"2024-11-12T11:19:04.511Z","updated_at":"2025-04-09T12:10:00.145Z","avatar_url":"https://github.com/lavalink-devs.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LavaPlayer - Audio player library for Discord\n\nLavaPlayer is an audio player library written in Java which can load audio tracks from various sources and convert them\ninto a stream of Opus frames. It is designed for use with Discord bots, but it can be used anywhere where Opus format\noutput is required.\n\n\u003e [!NOTE]\n\u003e This is a fork of the original [Lavaplayer](https://github.com/sedmelluq/lavaplayer) with some additional\n\u003e features and fixes from [Walkyst's fork](https://github.com/Walkyst/lavaplayer-fork).\n\n\u003e [!TIP]\n\u003e **Please read the [FAQ](FAQ.md) in case of issues.**\n\n\u003e [!NOTE]\n\u003e This fork requires Java 11 or newer. If you need Java 8 support you should update as java 8 was released 10 years ago.\n\n#### Maven package\n\nReplace `x.y.z` with the latest version\nnumber: [![Maven Central](https://img.shields.io/maven-central/v/dev.arbjerg/lavaplayer?versionPrefix=2)](https://central.sonatype.com/artifact/dev.arbjerg/lavaplayer)\n\n* Repository: mavenCentral\n* Artifact: **dev.arbjerg:lavaplayer:x.y.z**\n\nSnapshots are published\nto https://maven.lavalink.dev/snapshots \u0026 https://s01.oss.sonatype.org/content/repositories/snapshots\n\nUsing in Gradle:\n\n```gradle\nrepositories {\n  mavenCentral()\n  maven { url \"https://jitpack.io\" } // For com.github.walkyst.JAADec-fork:jaadec-ext-aac \u0026 ibxm-fork:com.github.walkyst:ibxm-fork\n}\n\ndependencies {\n  implementation 'dev.arbjerg:lavaplayer:x.y.z'\n}\n```\n\nUsing in Maven:\n\n```xml\n\u003crepositories\u003e\n    \u003crepository\u003e\n        \u003cid\u003ejitpack\u003c/id\u003e\n        \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n    \u003c/repository\u003e\n\u003c/repositories\u003e\n\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003edev.arbjerg\u003c/groupId\u003e\n    \u003cartifactId\u003elavaplayer\u003c/artifactId\u003e\n    \u003cversion\u003ex.y.z\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## Supported formats\n\nThe set of sources where LavaPlayer can load tracks from is easily extensible, but the ones currently included by\ndefault are:\n\n* ~~YouTube~~ YouTube support is now provided by [youtube-source](https://github.com/lavalink-devs/youtube-source)\n* SoundCloud\n* Bandcamp\n* Vimeo\n* Twitch streams\n* Local files\n* HTTP URLs\n\nThe file formats that LavaPlayer can currently handle are (relevant for file/url sources):\n\n* MP3\n* FLAC\n* WAV\n* Matroska/WebM (AAC, Opus or Vorbis codecs)\n* MP4/M4A (AAC codec)\n* OGG streams (Opus, Vorbis and FLAC codecs)\n* AAC streams\n* Stream playlists (M3U and PLS)\n\n## Resource usage\n\nWhat makes LavaPlayer unique is that it handles everything in the same process. Different sources and container formats\nare handled in Java, while decoding and encoding of audio are handled by embedded native libraries. This gives it a very\nfine-grained control over the resources that it uses, which means a low memory footprint as well as the chance to skip\ndecoding and encoding steps altogether when the input format matches the output format. Some key things to remember:\n\n* Memory usage is both predictable and low. The amount of memory used per track when testing with YouTube was at most\n  350 kilobytes per track plus the off-heap memory for the thread stack, since there is one thread per playing track.\n* The most common format used in YouTube is Opus, which matches the exact output format required for Discord. When no\n  volume adjustment is applied, the packets from YouTube are directly passed to output, which saves CPU cycles.\n* Resource leaks are unlikely because there are no additional processes launched and only one thread per playing track.\n  When an audio player is not queried for an user-configured amount of time, then the playing track is aborted and the\n  thread cleaned up. This avoids thread leaks even when the audio player is not shut down as it is supposed to.\n\n## Features\n\n#### Precise seeking support\n\nSeeking is supported on all non-stream formats and sources. When a seek is performed on a playing track, the previously\nbuffered audio samples will be provided until the seek is finished (this is configurable). When a seek is performed on a\ntrack which has not yet started, it will start immediately from the chosen position.\n\nDue to media containers supporting seeking at different resolutions, the position that a media player can start reading\ndata from might be several seconds from the location that the user actually wanted to seek to. LavaPlayer handles it by\nremembering the position where it was requested to seek to, jumping to the highest position which is not after that and\nthen ignoring the audio until the actual position that was requested. This provides a millisecond accuracy on seeking.\n\n#### Easy track loading\n\nWhen creating an instance of an `AudioPlayerManager`, sources where the tracks should be loaded from with it must be\nmanually registered. When loading tracks, you pass the manager an identifier and a handler which will get asynchronously\ncalled when the result has arrived. The handler has separate methods for receiving resolved tracks, resolved playlists,\nexceptions or being notified when nothing was found for the specified identifier.\n\nSince the tracks hold only minimal meta-information (title, author, duration and identifier), loading playlists does not\nusually require the library to check the page of each individual track for sources such as YouTube or SoundCloud. This\nmakes loading playlists pretty fast.\n\n#### Extensibility\n\nAny source that implements the `AudioSourceManager` interface can be registered to the player manager. These can be\ncustom sources using either some of the supported containers and codecs or defining a totally new way the tracks are\nactually executed, such as delegating it to another process, should the set of formats supported by LavaPlayer by\ndefault not be enough.\n\n## Usage\n\n#### Creating an audio player manager\n\nFirst thing you have to do when using the library is to create a `DefaultAudioPlayerManager` and then configure it to\nuse the settings and sources you want. Here is a sample:\n\n```java\nAudioPlayerManager playerManager = new DefaultAudioPlayerManager();\nAudioSourceManagers.registerRemoteSources(playerManager);\n```\n\nThere are various configuration settings that can be modified:\n\n* Opus encoding and resampling quality settings.\n* Frame buffer duration: how much of audio is buffered in advance.\n* Stuck track threshold: when no data from a playing track comes in the specified time, an event is sent.\n* Abandoned player cleanup threshold: when the player is not queried in the specified amount of time, it is stopped.\n* Garbage collection monitoring: logs statistics of garbage collection pauses every 2 minutes. If the pauses are long\n  enough to cause a stutter in the audio, it will be logged with a warning level, so you could take action to optimize\n  your GC settings.\n\nIf possible, you should use a single instance of a player manager for your whole application. A player manager manages\nseveral thread pools which make no sense to duplicate.\n\n#### Creating an audio player\n\nOnce you have a player manager, you can create players from it. Generally you would want to create a player per every\ndifferent target you might want to separately stream audio to. It is totally fine to create them even if they are\nunlikely to be used, as they do not use any resources on their own without an active track.\n\nCreating a player is rather simple:\n\n```java\nAudioPlayer player = playerManager.createPlayer();\n```\n\nOnce you have an instance of an audio player, you need some way to receive events from it. For that you should register\na listener to it which either extends the `AudioEventAdapter` class or implements `AudioEventListener`. Since that\nlistener receives the events for starting and ending tracks, it makes sense to also make it responsible for scheduling\ntracks. Assuming `TrackScheduler` is a class that implements `AudioEventListener`:\n\n```java\nTrackScheduler trackScheduler = new TrackScheduler(player);\nplayer.addListener(trackScheduler);\n```\n\nNow you have an audio player capable of playing instances of `AudioTrack`. However, what you don't have is audio tracks,\nwhich are the next things you have to obtain.\n\n#### Loading audio tracks\n\nTo load a track, you have to call either the `loadItem` or `loadItemOrdered` method of\nan `AudioPlayerManager`. `loadItem` takes an identifier parameter and a load handler parameter. The identifier is a\npiece of text that should identify the track for some source. For example if it is a YouTube video ID, then YouTube\nsource manager will load it, if it is a file path then the local file source will load it. The handler parameter is an\ninstance of `AudioLoadResultHandler`, which has separate methods for different results of the loading process. You can\neither have a dedicated class for this or you can simply pass it an anonymous class as in the next example:\n\n```java\nplayerManager.loadItem(identifier, new AudioLoadResultHandler() {\n  @Override\n  public void trackLoaded(AudioTrack track) {\n    trackScheduler.queue(track);\n  }\n\n  @Override\n  public void playlistLoaded(AudioPlaylist playlist) {\n    for (AudioTrack track : playlist.getTracks()) {\n      trackScheduler.queue(track);\n    }\n  }\n\n  @Override\n  public void noMatches() {\n    // Notify the user that we've got nothing\n  }\n\n  @Override\n  public void loadFailed(FriendlyException throwable) {\n    // Notify the user that everything exploded\n  }\n}\n```\n\nMost of these methods are rather obvious. In addition to everything exploding, `loadFailed` will also be called for\nexample when a YouTube track is blocked or not available in your area. The `FriendlyException` class has a field\ncalled `severity`. If the value of this is `COMMON`, then it means that the reason is definitely not a bug or a network\nissue, but because the track is not available, such as the YouTube blocked video example. These message in this case can\nsimply be forwarded as is to the user.\n\nThe other method for loading tracks, `loadItemOrdered` is for cases where you want the tracks to be loaded in order for\nexample within one player. `loadItemOrdered` takes an ordering channel key as the first parameter, which is simply any\nobject which remains the same for all the requests that should be loaded in the same ordered queue. The most common use\nwould probably be to just pass it the `AudioPlayer` instance that the loaded tracks will be queued for.\n\n#### Playing audio tracks\n\nIn the previous example I did not actually start playing the loaded track yet, but sneakily passed it on to our\nfictional `TrackScheduler` class instead. Starting the track is however a trivial thing to do:\n\n```java\nplayer.playTrack(track);\n```\n\nNow the track should be playing, which means buffered for whoever needs it to poll its frames. However, you would need\nto somehow react to events, most notably the track finishing, so you could start the next track.\n\n#### Handling events\n\nEvents are handled by event handlers added to an `AudioPlayer` instance. The simplest way for creating the handler is to\nextend the `AudioEventAdapter` class. Here is a quick description of each of the methods it has, in the context of using\nit for a track scheduler:\n\n```java\npublic class TrackScheduler extends AudioEventAdapter {\n  @Override\n  public void onPlayerPause(AudioPlayer player) {\n    // Player was paused\n  }\n\n  @Override\n  public void onPlayerResume(AudioPlayer player) {\n    // Player was resumed\n  }\n\n  @Override\n  public void onTrackStart(AudioPlayer player, AudioTrack track) {\n    // A track started playing\n  }\n\n  @Override\n  public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {\n    if (endReason.mayStartNext) {\n      // Start next track\n    }\n\n    // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true).\n    // endReason == LOAD_FAILED: Loading of a track failed (mayStartNext = true).\n    // endReason == STOPPED: The player was stopped.\n    // endReason == REPLACED: Another track started playing while this had not finished\n    // endReason == CLEANUP: Player hasn't been queried for a while, if you want you can put a\n    //                       clone of this back to your queue\n  }\n\n  @Override\n  public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {\n    // An already playing track threw an exception (track end event will still be received separately)\n  }\n\n  @Override\n  public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {\n    // Audio track has been unable to provide us any audio, might want to just start a new track\n  }\n}\n```\n\n#### JDA integration\n\nTo use it with JDA 4, you would need an instance of `AudioSendHandler`. There is only the slight difference of no\nseparate `canProvide` and `provide` methods in `AudioPlayer`, so the wrapper for this is simple:\n\n```java\npublic class AudioPlayerSendHandler implements AudioSendHandler {\n    private final AudioPlayer audioPlayer;\n    private AudioFrame lastFrame;\n\n    public AudioPlayerSendHandler(AudioPlayer audioPlayer) {\n        this.audioPlayer = audioPlayer;\n    }\n\n    @Override\n    public boolean canProvide() {\n        lastFrame = audioPlayer.provide();\n        return lastFrame != null;\n    }\n\n    @Override\n    public ByteBuffer provide20MsAudio() {\n        return ByteBuffer.wrap(lastFrame.getData());\n    }\n\n    @Override\n    public boolean isOpus() {\n        return true;\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flavalink-devs%2Flavaplayer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flavalink-devs%2Flavaplayer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flavalink-devs%2Flavaplayer/lists"}