diff --git a/app/Http/Controllers/PlayController.php b/app/Http/Controllers/PlayController.php
index 81366fee..688c99a3 100644
--- a/app/Http/Controllers/PlayController.php
+++ b/app/Http/Controllers/PlayController.php
@@ -4,17 +4,20 @@ namespace App\Http\Controllers;
use App\Http\Requests\SongPlayRequest;
use App\Models\Song;
+use App\Models\User;
use App\Services\Streamer\Streamer;
+use Illuminate\Contracts\Auth\Authenticatable;
class PlayController extends Controller
{
- public function __invoke(SongPlayRequest $request, Song $song, ?bool $transcode = null, ?int $bitRate = null)
+ /** @param User $user */
+ public function __invoke(Authenticatable $user, SongPlayRequest $request, Song $song, ?bool $transcode = null)
{
$this->authorize('access', $song);
return (new Streamer(song: $song, config: [
'transcode' => (bool) $transcode,
- 'bit_rate' => $bitRate,
+ 'bit_rate' => $user->preferences->transcodeQuality,
'start_time' => (float) $request->time,
]))->stream();
}
diff --git a/app/Values/UserPreferences.php b/app/Values/UserPreferences.php
index 5b54f1fa..96d102f3 100644
--- a/app/Values/UserPreferences.php
+++ b/app/Values/UserPreferences.php
@@ -13,6 +13,7 @@ final class UserPreferences implements Arrayable, JsonSerializable
'show_now_playing_notification' => 'boolean',
'confirm_before_closing' => 'boolean',
'transcode_on_mobile' => 'boolean',
+ 'transcode_quality' => 'integer',
'show_album_art_overlay' => 'boolean',
'lyrics_zoom_level' => 'integer',
'make_uploads_public' => 'boolean',
@@ -29,6 +30,7 @@ final class UserPreferences implements Arrayable, JsonSerializable
'show_now_playing_notification',
'confirm_before_closing',
'transcode_on_mobile',
+ 'transcode_quality',
'show_album_art_overlay',
'make_uploads_public',
'support_bar_no_bugging',
@@ -50,6 +52,7 @@ final class UserPreferences implements Arrayable, JsonSerializable
public bool $showNowPlayingNotification,
public bool $confirmBeforeClosing,
public bool $transcodeOnMobile,
+ public int $transcodeQuality,
public bool $showAlbumArtOverlay,
public bool $makeUploadsPublic,
public bool $supportBarNoBugging,
@@ -63,6 +66,10 @@ final class UserPreferences implements Arrayable, JsonSerializable
Assert::oneOf($this->artistsViewMode, ['list', 'thumbnails']);
Assert::oneOf($this->albumsViewMode, ['list', 'thumbnails']);
Assert::oneOf($this->activeExtraPanelTab, [null, 'Lyrics', 'Artist', 'Album', 'YouTube']);
+
+ if (!in_array($this->transcodeQuality, [64, 96, 128, 192, 256, 320], true)) {
+ $this->transcodeQuality = 128;
+ }
}
public static function fromArray(array $data): self
@@ -77,6 +84,7 @@ final class UserPreferences implements Arrayable, JsonSerializable
showNowPlayingNotification: $data['show_now_playing_notification'] ?? true,
confirmBeforeClosing: $data['confirm_before_closing'] ?? false,
transcodeOnMobile: $data['transcode_on_mobile'] ?? true,
+ transcodeQuality: $data['transcode_quality'] ?? 128,
showAlbumArtOverlay: $data['show_album_art_overlay'] ?? true,
makeUploadsPublic: $data['make_uploads_public'] ?? false,
supportBarNoBugging: $data['support_bar_no_bugging'] ?? false,
@@ -126,6 +134,7 @@ final class UserPreferences implements Arrayable, JsonSerializable
'confirm_before_closing' => $this->confirmBeforeClosing,
'show_album_art_overlay' => $this->showAlbumArtOverlay,
'transcode_on_mobile' => $this->transcodeOnMobile,
+ 'transcode_quality' => $this->transcodeQuality,
'make_uploads_public' => $this->makeUploadsPublic,
'lastfm_session_key' => $this->lastFmSessionKey,
'support_bar_no_bugging' => $this->supportBarNoBugging,
diff --git a/docs/usage/profile-preferences.md b/docs/usage/profile-preferences.md
index 4430f477..533ce99d 100644
--- a/docs/usage/profile-preferences.md
+++ b/docs/usage/profile-preferences.md
@@ -39,7 +39,7 @@ Koel allows you to set a couple of preferences:
* Whether to show a notification whenever a new song starts playing
* Whether to confirm before closing Koel’s browser tab
* Whether to show a translucent, blurred overlay of the current album’s art
-* Whether to transcode music on the fly (mobile only, useful if you have a slow network connection)
+* Whether to transcode music to a lower bitrate (mobile only, useful if you have a slow connection)
* Whether to set your uploaded music as public by default
These preferences are saved immediately upon change and synced across all of your devices.
diff --git a/docs/usage/streaming.md b/docs/usage/streaming.md
index 2bfd8f6d..777825fc 100644
--- a/docs/usage/streaming.md
+++ b/docs/usage/streaming.md
@@ -26,10 +26,17 @@ If you're using [Koel mobile app](https://koel.dev/#mobile) and can't play the s
Koel always uses the native PHP method if you're transcoding or streaming from a cloud storage.
:::
-## Transcoding FLAC
+## FLAC Transcoding
-Koel supports transcoding FLAC to mp3 on the fly when streaming music. This behavior can be controlled via a `TRANSCODE_FLAC` setting in `.env` file:
+By default, Koel streams FLAC files as-is, which means the lossless audio quality is preserved.
+However, you can opt to have FLAC transcoded to mp3 to, for example, reduce bandwidth or support older devices. This behavior can be controlled via the `TRANSCODE_FLAC` setting in `.env` file:
-* `false`: Disable FLAC transcoding. Koel will stream FLAC files as-is, producing the lossless audio quality. This is the default behavior.
-* `true`: Enable FLAC transcoding. Koel will transcode FLAC to mp3 on the fly. You'll need to have [FFmpeg](https://ffmpeg.org/) installed on your server and set its executable path via the `FFMPEG_PATH` setting in the `.env` file. The transcoding quality can also be controlled via `OUTPUT_BIT_RATE` (defaults to `128`).
+* `false`: Disable transcoding and stream FLAC files as-is, producing the lossless audio quality. This is the default behavior.
+* `true`: Transcode FLAC to mp3 before streaming. You'll need to have [FFmpeg](https://ffmpeg.org/) installed on your server and set its executable path via the `FFMPEG_PATH` setting in the `.env` file. The transcoding quality can also be controlled via `OUTPUT_BIT_RATE` (defaults to `128`).
+When transcoding is enabled, Koel caches the transcoded files to improve performance. If for any reason you want to clear the cache, run `php artisan cache:clear`.
+
+## Transcoding on Mobile
+
+On a mobile device where data usage is a concern, you might want to transcode all songs to a lower bitrate to save bandwidth.
+This can be done via the [Preferences screen](./profile-preferences#preferences) and requires the same FFmpeg setup as FLAC transcoding.
diff --git a/resources/assets/js/__tests__/UnitTestCase.ts b/resources/assets/js/__tests__/UnitTestCase.ts
index ed03c0fa..3a583a93 100644
--- a/resources/assets/js/__tests__/UnitTestCase.ts
+++ b/resources/assets/js/__tests__/UnitTestCase.ts
@@ -56,6 +56,7 @@ export default abstract class UnitTestCase {
commonStore.state.allows_download = true
commonStore.state.uses_i_tunes = true
commonStore.state.supports_batch_downloading = true
+ commonStore.state.supports_transcoding = true
cb && cb()
})
}
diff --git a/resources/assets/js/components/profile-preferences/PreferencesForm.spec.ts b/resources/assets/js/components/profile-preferences/PreferencesForm.spec.ts
index 3b40420c..9f626614 100644
--- a/resources/assets/js/components/profile-preferences/PreferencesForm.spec.ts
+++ b/resources/assets/js/components/profile-preferences/PreferencesForm.spec.ts
@@ -1,6 +1,7 @@
import { expect, it } from 'vitest'
import { screen } from '@testing-library/vue'
import isMobile from 'ismobilejs'
+import { commonStore } from '@/stores'
import UnitTestCase from '@/__tests__/UnitTestCase'
import PreferencesForm from './PreferencesForm.vue'
@@ -9,13 +10,19 @@ new class extends UnitTestCase {
it('has "Transcode on mobile" option for mobile users', () => {
isMobile.phone = true
this.render(PreferencesForm)
- screen.getByRole('checkbox', { name: 'Convert and play media at 128kbps on mobile' })
+ screen.getByTestId('transcode_on_mobile')
})
it('does not have "Transcode on mobile" option for non-mobile users', async () => {
isMobile.phone = false
this.render(PreferencesForm)
- expect(screen.queryByRole('checkbox', { name: 'Convert and play media at 128kbps on mobile' })).toBeNull()
+ expect(screen.queryByTestId('transcode_on_mobile')).toBeNull()
+ })
+
+ it('does not have "Transcode on mobile" option if transcoding is not supported', async () => {
+ isMobile.phone = true
+ commonStore.state.supports_transcoding = false
+ expect(screen.queryByTestId('transcode_on_mobile')).toBeNull()
})
}
}
diff --git a/resources/assets/js/components/profile-preferences/PreferencesForm.vue b/resources/assets/js/components/profile-preferences/PreferencesForm.vue
index 27fff9af..469304f7 100644
--- a/resources/assets/js/components/profile-preferences/PreferencesForm.vue
+++ b/resources/assets/js/components/profile-preferences/PreferencesForm.vue
@@ -24,10 +24,22 @@
Confirm before closing Koel
-
+
-
- Convert and play media at 128kbps on mobile
+
+ Convert and play media at
+
+ kbps on mobile