More WebDJ work.
This commit is contained in:
parent
b498288965
commit
901faaee50
|
@ -1,2 +1 @@
|
|||
vue/vendor/chartjs-colorschemes/*
|
||||
vue/vendor/webcast/*
|
||||
|
|
|
@ -60,6 +60,12 @@ const jsFiles = {
|
|||
files: [
|
||||
'node_modules/luxon/build/global/luxon.min.js'
|
||||
]
|
||||
},
|
||||
'webcaster': {
|
||||
base: null,
|
||||
files: [
|
||||
'js/webcaster/*.js'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ import SettingsPanel from './WebDJ/SettingsPanel.vue';
|
|||
import {useWebDjNode} from "~/components/Public/WebDJ/useWebDjNode";
|
||||
import {provide} from "vue";
|
||||
import {useWebcaster, webcasterProps} from "~/components/Public/WebDJ/useWebcaster";
|
||||
import '~/vendor/webcast/taglib';
|
||||
|
||||
const props = defineProps({
|
||||
...webcasterProps,
|
||||
|
|
|
@ -84,9 +84,9 @@ import {useDevicesList} from "@vueuse/core";
|
|||
import {ref, watch} from "vue";
|
||||
import {useWebDjTrack} from "~/components/Public/WebDJ/useWebDjTrack";
|
||||
|
||||
const {node, isPlaying, trackGain, trackPassThrough, volume, prepare, stop} = useWebDjTrack();
|
||||
const {node, source, isPlaying, trackGain, trackPassThrough, volume, prepare, stop} = useWebDjTrack();
|
||||
|
||||
const {context, createMicrophoneSource} = node;
|
||||
const {createMicrophoneSource} = node;
|
||||
|
||||
const {audioInputs} = useDevicesList({
|
||||
requestPermissions: true,
|
||||
|
@ -94,28 +94,30 @@ const {audioInputs} = useDevicesList({
|
|||
|
||||
const device = ref(audioInputs.value[0]?.deviceId);
|
||||
|
||||
let source = null;
|
||||
let destination = null;
|
||||
|
||||
const createSource = () => {
|
||||
if (source != null) {
|
||||
source.disconnect(context.destination);
|
||||
if (source.value != null && destination !== null) {
|
||||
source.value.disconnect(destination);
|
||||
}
|
||||
|
||||
createMicrophoneSource(device.value, (newSource) => {
|
||||
source = newSource;
|
||||
source.connect(context.destination);
|
||||
source.value = newSource;
|
||||
if (destination !== null) {
|
||||
newSource.connect(destination);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(device, () => {
|
||||
if (this.source == null) {
|
||||
if (source.value == null) {
|
||||
return;
|
||||
}
|
||||
createSource();
|
||||
});
|
||||
|
||||
const play = () => {
|
||||
prepare();
|
||||
destination = prepare();
|
||||
createSource();
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,7 @@ const isLeftPlaylist = computed(() => {
|
|||
|
||||
const {
|
||||
node,
|
||||
source,
|
||||
isPlaying,
|
||||
isPaused,
|
||||
trackGain,
|
||||
|
@ -198,7 +199,7 @@ const {
|
|||
stop
|
||||
} = useWebDjTrack();
|
||||
|
||||
const {context, createFileSource, updateMetadata} = node;
|
||||
const {createFileSource, sendMetadata} = node;
|
||||
|
||||
const fileIndex = ref(-1);
|
||||
const files = ref([]);
|
||||
|
@ -210,8 +211,8 @@ const {$gettext} = useTranslate();
|
|||
|
||||
const langHeader = computed(() => {
|
||||
return isLeftPlaylist.value
|
||||
? this.$gettext('Playlist 1')
|
||||
: this.$gettext('Playlist 2');
|
||||
? $gettext('Playlist 1')
|
||||
: $gettext('Playlist 2');
|
||||
});
|
||||
|
||||
const addNewFiles = (newFiles) => {
|
||||
|
@ -226,8 +227,6 @@ const addNewFiles = (newFiles) => {
|
|||
});
|
||||
};
|
||||
|
||||
let source = null;
|
||||
|
||||
const selectFile = (options = {}) => {
|
||||
if (files.value.length === 0) {
|
||||
return;
|
||||
|
@ -271,23 +270,23 @@ const play = (options = {}) => {
|
|||
|
||||
stop();
|
||||
|
||||
prepare();
|
||||
let destination = prepare();
|
||||
|
||||
createFileSource(file, (newSource) => {
|
||||
source = newSource;
|
||||
source.connect(context.destination);
|
||||
source.value = newSource;
|
||||
newSource.connect(destination);
|
||||
|
||||
if (source.duration !== null) {
|
||||
duration.value = source.duration();
|
||||
if (newSource.duration !== null) {
|
||||
duration.value = newSource.duration();
|
||||
} else if (file.audio !== null) {
|
||||
duration.value = parseFloat(this.file.audio.length);
|
||||
duration.value = parseFloat(file.audio.length);
|
||||
}
|
||||
|
||||
source.play(file);
|
||||
newSource.play(file);
|
||||
|
||||
updateMetadata({
|
||||
title: this.file.metadata.title,
|
||||
artist: this.file.metadata.artist
|
||||
sendMetadata({
|
||||
title: file.metadata.title,
|
||||
artist: file.metadata.artist
|
||||
});
|
||||
}, () => {
|
||||
stop();
|
||||
|
|
|
@ -146,6 +146,7 @@
|
|||
<script setup>
|
||||
import {computed, inject, ref} from "vue";
|
||||
import {syncRef} from "@vueuse/core";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
|
||||
const props = defineProps({
|
||||
stationName: {
|
||||
|
@ -166,10 +167,12 @@ const {
|
|||
sendMetadata
|
||||
} = inject('node');
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const langStreamButton = computed(() => {
|
||||
return (isStreaming.value)
|
||||
? this.$gettext('Stop Streaming')
|
||||
: this.$gettext('Start Streaming');
|
||||
? $gettext('Stop Streaming')
|
||||
: $gettext('Start Streaming');
|
||||
});
|
||||
|
||||
const shownMetadata = ref({});
|
||||
|
|
|
@ -11,16 +11,16 @@ export function useWebDjNode(webcaster) {
|
|||
|
||||
const sink = context.createScriptProcessor(256, 2, 2);
|
||||
|
||||
sink.onaudioprocess((buf) => {
|
||||
sink.onaudioprocess = (buf) => {
|
||||
for (let channel = 0; channel < buf.inputBuffer.numberOfChannels - 1; channel++) {
|
||||
let channelData = buf.inputBuffer.getChannelData(channel);
|
||||
buf.outputBuffer.getChannelData(channel).set(channelData);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const passThrough = context.createScriptProcessor(256, 2, 2);
|
||||
|
||||
passThrough.onaudioprocess((buf) => {
|
||||
passThrough.onaudioprocess = (buf) => {
|
||||
for (let channel = 0; channel < buf.inputBuffer.numberOfChannels - 1; channel++) {
|
||||
let channelData = buf.inputBuffer.getChannelData(channel);
|
||||
|
||||
|
@ -30,7 +30,7 @@ export function useWebDjNode(webcaster) {
|
|||
buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
sink.connect(passThrough);
|
||||
passThrough.connect(context.destination);
|
||||
|
@ -105,8 +105,8 @@ export function useWebDjNode(webcaster) {
|
|||
});
|
||||
};
|
||||
|
||||
const createFileSource = (file, cb) => {
|
||||
return createAudioSource(file, cb);
|
||||
const createFileSource = (file, cb, onEnd) => {
|
||||
return createAudioSource(file, cb, onEnd);
|
||||
};
|
||||
|
||||
const createMicrophoneSource = (audioDeviceId, cb) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {computed, inject, ref, watch} from "vue";
|
||||
import {computed, inject, ref, shallowRef, watch} from "vue";
|
||||
|
||||
export function useWebDjTrack() {
|
||||
const node = inject('node');
|
||||
|
@ -8,17 +8,7 @@ export function useWebDjTrack() {
|
|||
const position = ref(null);
|
||||
const volume = ref(0);
|
||||
|
||||
let source = null;
|
||||
|
||||
const isPlaying = computed(() => {
|
||||
return source !== null;
|
||||
});
|
||||
|
||||
const isPaused = computed(() => {
|
||||
return (source !== null)
|
||||
? source.paused
|
||||
: false;
|
||||
});
|
||||
let source = shallowRef(null);
|
||||
|
||||
const createControlsNode = () => {
|
||||
const bufferSize = 4096;
|
||||
|
@ -27,8 +17,8 @@ export function useWebDjTrack() {
|
|||
|
||||
let newSource = node.context.createScriptProcessor(bufferSize, 2, 2);
|
||||
|
||||
newSource.onaudioprocess((buf) => {
|
||||
position.value = source?.position();
|
||||
newSource.onaudioprocess = (buf) => {
|
||||
position.value = source.value?.position();
|
||||
|
||||
for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
|
||||
let channelData = buf.inputBuffer.getChannelData(channel);
|
||||
|
@ -42,7 +32,7 @@ export function useWebDjTrack() {
|
|||
|
||||
buf.outputBuffer.getChannelData(channel).set(channelData);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return newSource;
|
||||
};
|
||||
|
@ -50,7 +40,7 @@ export function useWebDjTrack() {
|
|||
const createPassThrough = () => {
|
||||
let newSource = node.context.createScriptProcessor(256, 2, 2);
|
||||
|
||||
newSource.onaudioprocess((buf) => {
|
||||
newSource.onaudioprocess = (buf) => {
|
||||
for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
|
||||
let channelData = buf.inputBuffer.getChannelData(channel);
|
||||
|
||||
|
@ -60,7 +50,7 @@ export function useWebDjTrack() {
|
|||
buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return newSource;
|
||||
};
|
||||
|
@ -90,35 +80,48 @@ export function useWebDjTrack() {
|
|||
trackGainNode.connect(passThroughNode);
|
||||
|
||||
node.context.resume();
|
||||
|
||||
return trackGainNode;
|
||||
}
|
||||
|
||||
const isPlaying = computed(() => {
|
||||
return source.value !== null;
|
||||
});
|
||||
|
||||
const isPaused = computed(() => {
|
||||
return (source.value !== null)
|
||||
? source.value.paused
|
||||
: false;
|
||||
});
|
||||
|
||||
const togglePause = () => {
|
||||
if (source === null) {
|
||||
if (source.value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.paused) {
|
||||
source.play();
|
||||
if (source.value.paused) {
|
||||
source.value.play();
|
||||
} else {
|
||||
source.pause();
|
||||
source.value.pause();
|
||||
}
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
source?.stop();
|
||||
source?.disconnect();
|
||||
source.value?.stop();
|
||||
source.value?.disconnect();
|
||||
|
||||
trackGainNode?.disconnect();
|
||||
controlsNode?.disconnect();
|
||||
passThroughNode?.disconnect();
|
||||
|
||||
source = trackGainNode = controlsNode = passThroughNode = null;
|
||||
source.value = trackGainNode = controlsNode = passThroughNode = null;
|
||||
|
||||
position.value = 0.0;
|
||||
};
|
||||
|
||||
return {
|
||||
node,
|
||||
source,
|
||||
trackGain,
|
||||
trackPassThrough,
|
||||
position,
|
||||
|
|
|
@ -24,7 +24,7 @@ export function useWebcaster(props) {
|
|||
};
|
||||
|
||||
if (null !== username) {
|
||||
hello.username = username;
|
||||
hello.user = username;
|
||||
}
|
||||
if (null !== password) {
|
||||
hello.password = password;
|
||||
|
@ -52,10 +52,12 @@ export function useWebcaster(props) {
|
|||
};
|
||||
|
||||
const sendMetadata = (data) => {
|
||||
socket.send(JSON.stringify({
|
||||
type: "metadata",
|
||||
data,
|
||||
}));
|
||||
if (isConnected()) {
|
||||
socket.send(JSON.stringify({
|
||||
type: "metadata",
|
||||
data,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -39,7 +39,6 @@ final class WebDjAction
|
|||
}
|
||||
|
||||
$wss_url = (string)$backend->getWebStreamingUrl($station, $request->getRouter()->getBaseUrl());
|
||||
$wss_url = str_replace('wss://', '', $wss_url);
|
||||
|
||||
return $request->getView()->renderToResponse(
|
||||
response: $response->withHeader('X-Frame-Options', '*'),
|
||||
|
|
|
@ -15,6 +15,12 @@ $this->layout(
|
|||
]
|
||||
);
|
||||
|
||||
$sections->appendStart('bodyjs');
|
||||
?>
|
||||
<script src="<?= $this->assetUrl('dist/lib/webcaster/taglib.js') ?>"></script>
|
||||
<?php
|
||||
$sections->end();
|
||||
|
||||
echo $this->fetch(
|
||||
'partials/vue_body',
|
||||
[
|
||||
|
|
Loading…
Reference in New Issue