diff --git a/bun.lockb b/bun.lockb
index 15835a152..bb0d9d95e 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/index.html b/index.html
index f0fb0ca12..990705b9c 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
+
props.onChange(e.currentTarget.checked)}
checked={props.value}
+ disabled={disabled}
/>
-
+
{title}
{description}
diff --git a/src/generated/typings/index.ts b/src/generated/typings/index.ts
index e2207e9c9..b84c40c0c 100644
--- a/src/generated/typings/index.ts
+++ b/src/generated/typings/index.ts
@@ -1,11 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
-export type Config = { theme: string, audio_volume: number, audio_playback_rate: number | null, audio_output_device: string, audio_muted: boolean, audio_shuffle: boolean, audio_repeat: Repeat, default_view: DefaultView, library_sort_by: SortBy, library_sort_order: SortOrder, library_folders: Array, library_autorefresh: boolean, sleepblocker: boolean, auto_update_checker: boolean, minimize_to_tray: boolean, notifications: boolean, track_view_density: string, };
+export type Config = { theme: string, audio_volume: number, audio_playback_rate: number | null, audio_output_device: string, audio_muted: boolean, audio_shuffle: boolean, audio_repeat: Repeat, audio_playback_mode: PlaybackMode, default_view: DefaultView, library_sort_by: SortBy, library_sort_order: SortOrder, library_folders: Array, library_autorefresh: boolean, sleepblocker: boolean, auto_update_checker: boolean, minimize_to_tray: boolean, notifications: boolean, track_view_density: string, };
export type DefaultView = "Library" | "Playlists";
export type IPCEvent = { "Unknown": string } | "PlaybackPlay" | "PlaybackPause" | "PlaybackStop" | "PlaybackPlayPause" | "PlaybackPrevious" | "PlaybackNext" | "PlaybackStart" | "LibraryScanProgress" | "GoToLibrary" | "GoToPlaylists" | "GoToSettings" | "JumpToPlayingTrack";
+export type PlaybackMode = "Default" | "Blob";
+
/** ----------------------------------------------------------------------------
* Playlist
* represent a playlist, that has a name and a list of tracks
diff --git a/src/lib/player.ts b/src/lib/player.ts
index 8cce00a45..805293a61 100644
--- a/src/lib/player.ts
+++ b/src/lib/player.ts
@@ -1,6 +1,7 @@
import { convertFileSrc } from '@tauri-apps/api/core';
+import { info } from '@tauri-apps/plugin-log';
-import type { Track } from '../generated/typings';
+import type { PlaybackMode, Track } from '../generated/typings';
import config from './config';
import { logAndNotifyError } from './utils';
@@ -10,6 +11,7 @@ interface PlayerOptions {
audioOutputDevice?: string;
volume?: number;
muted?: boolean;
+ playbackMode: PlaybackMode;
}
/**
@@ -23,6 +25,7 @@ interface PlayerOptions {
class Player {
private audio: HTMLAudioElement;
private track: Track | null;
+ private playbackMode: PlaybackMode;
constructor(options?: PlayerOptions) {
const mergedOptions = {
@@ -30,6 +33,7 @@ class Player {
volume: 1,
muted: false,
audioOutputDevice: 'default',
+ playbackMode: 'Default' as PlaybackMode,
...options,
};
@@ -43,6 +47,9 @@ class Player {
this.audio.playbackRate = mergedOptions.playbackRate;
this.audio.volume = mergedOptions.volume;
this.audio.muted = mergedOptions.muted;
+ this.playbackMode = mergedOptions.playbackMode;
+
+ info(`Player playback mode: ${this.playbackMode}`);
}
async play() {
@@ -89,6 +96,11 @@ class Player {
this.audio.defaultPlaybackRate = playbackRate;
}
+ setPlaybackMode(playbackMode: PlaybackMode) {
+ info(`Playback mode set to: ${playbackMode}`);
+ this.playbackMode = playbackMode;
+ }
+
async setOutputDevice(deviceID: string) {
try {
// @ts-ignore
@@ -105,16 +117,19 @@ class Player {
async setTrack(track: Track) {
this.track = track;
- // Cursed Linux: https://github.com/tauri-apps/tauri/issues/3725#issuecomment-2325248116
- if (window.__MUSEEKS_PLATFORM === 'linux') {
- const blobUrl = URL.createObjectURL(
- await fetch(convertFileSrc(track.path)).then((res) => res.blob()),
- );
- this.audio.src = blobUrl;
- return;
+ switch (this.playbackMode) {
+ case 'Default': {
+ const blobUrl = URL.createObjectURL(
+ await fetch(convertFileSrc(track.path)).then((res) => res.blob()),
+ );
+ this.audio.src = blobUrl;
+ return;
+ }
+ case 'Blob': {
+ this.audio.src = convertFileSrc(track.path);
+ return;
+ }
}
-
- this.audio.src = convertFileSrc(track.path);
}
setCurrentTime(currentTime: number) {
@@ -128,6 +143,17 @@ class Player {
isPaused() {
return this.audio.paused;
}
+
+ getDebug(): string {
+ return `
+Player Debug Information:
+- mode: ${this.playbackMode}
+- internal src: ${this.audio.src}
+- external src: ${this.track?.path}
+- current time: ${this.audio.currentTime}
+- playback rate: ${this.audio.playbackRate}
+- volume: ${this.audio.volume}`;
+ }
}
/**
@@ -140,4 +166,5 @@ export default new Player({
playbackRate: config.getInitial('audio_playback_rate') ?? 1,
audioOutputDevice: config.getInitial('audio_output_device'),
muted: config.getInitial('audio_muted'),
+ playbackMode: config.getInitial('audio_playback_mode'),
});
diff --git a/src/main.tsx b/src/main.tsx
index 2b2eb01cf..a55718209 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -11,7 +11,6 @@ import * as ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router';
import queryClient from './lib/query-client';
-import router from './routes';
/*
|--------------------------------------------------------------------------
@@ -28,19 +27,22 @@ import './styles/general.css';
|--------------------------------------------------------------------------
*/
-logger.attachConsole();
+(async function createRoot() {
+ await logger.attachConsole();
-const wrap = document.getElementById('wrap');
+ const wrap = document.getElementById('wrap');
+ const router = (await import('./routes')).default;
-if (wrap) {
- const root = ReactDOM.createRoot(wrap);
- root.render(
-
-
-
-
- ,
- );
-} else {
- document.body.innerHTML = 'x_x
';
-}
+ if (wrap) {
+ const root = ReactDOM.createRoot(wrap);
+ root.render(
+
+
+
+
+ ,
+ );
+ } else {
+ document.body.innerHTML = 'x_x
';
+ }
+})();
diff --git a/src/routes/settings-audio.tsx b/src/routes/settings-audio.tsx
index 5d943dd30..363a1da83 100644
--- a/src/routes/settings-audio.tsx
+++ b/src/routes/settings-audio.tsx
@@ -4,6 +4,7 @@ import AudioOutputSelect from '../components/AudioOutputSelect';
import * as Setting from '../components/Setting';
import { usePlayerAPI } from '../stores/usePlayerStore';
+import CheckboxSetting from '../components/SettingCheckbox';
import useInvalidate, { useInvalidateCallback } from '../hooks/useInvalidate';
import type { SettingsLoaderData } from './settings';
@@ -41,6 +42,16 @@ export default function ViewSettingsAudio() {
onChange={useInvalidateCallback(playerAPI.setOutputDevice)}
/>
+
+ playerAPI.setPlaybackMode(e ? 'Blob' : 'Default').then(invalidate)
+ }
+ />
);
}
diff --git a/src/stores/usePlayerStore.ts b/src/stores/usePlayerStore.ts
index 42a7d87ff..30e6bcdf3 100644
--- a/src/stores/usePlayerStore.ts
+++ b/src/stores/usePlayerStore.ts
@@ -2,7 +2,7 @@ import debounce from 'lodash-es/debounce';
import type { StateCreator } from 'zustand';
import { persist } from 'zustand/middleware';
-import type { Repeat, Track } from '../generated/typings';
+import type { PlaybackMode, Repeat, Track } from '../generated/typings';
import config from '../lib/config';
import database from '../lib/database';
import player from '../lib/player';
@@ -33,6 +33,7 @@ type PlayerState = API<{
setVolume: (volume: number) => void;
setMuted: (muted: boolean) => void;
setPlaybackRate: (value: number) => Promise
;
+ setPlaybackMode: (value: PlaybackMode) => Promise;
setOutputDevice: (deviceID: string) => Promise;
jumpTo: (to: number) => void;
startFromQueue: (index: number) => Promise;
@@ -106,7 +107,9 @@ const usePlayerStore = createPlayerStore((set, get) => ({
const track = queue[queuePosition];
await player.setTrack(track);
- await player.play().catch(logAndNotifyError);
+ await player
+ .play()
+ .catch((err) => logAndNotifyError(err, undefined, false, true));
let queueCursor = queuePosition; // Clean that variable mess later
@@ -360,6 +363,15 @@ const usePlayerStore = createPlayerStore((set, get) => ({
}
},
+ /**
+ * Enable alternate playback for audio (more latency, but better x-platform
+ * compatibility).
+ */
+ setPlaybackMode: async (value) => {
+ await config.set('audio_playback_mode', value);
+ player.setPlaybackMode(value);
+ },
+
/**
* Jump to a time in the track
*/