Add API endpoint and frontend modal to view playlist's AutoDJ queue.
This commit is contained in:
parent
4417b7708e
commit
91c73de176
|
@ -17,6 +17,9 @@ release channel, you can take advantage of these new features and fixes.
|
|||
|
||||
- You can now generate listener reports for specific time periods instead of just day ranges.
|
||||
|
||||
- For sequential or shuffled playlists, you can now view the internal queue that the AzuraCast AutoDJ uses to track its
|
||||
song playback order from the "More" dropdown next to the playlist.
|
||||
|
||||
## Code Quality/Technical Changes
|
||||
|
||||
- We have removed the "?12345678" cache-busting timestamp query strings appended to the end of stream URLs. These have
|
||||
|
|
|
@ -361,6 +361,16 @@ return function (App $app) {
|
|||
Controller\Api\Stations\Playlists\PutOrderAction::class
|
||||
);
|
||||
|
||||
$group->get(
|
||||
'/queue',
|
||||
Controller\Api\Stations\Playlists\GetQueueAction::class
|
||||
)->setName('api:stations:playlist:queue');
|
||||
|
||||
$group->delete(
|
||||
'/queue',
|
||||
Controller\Api\Stations\Playlists\DeleteQueueAction::class
|
||||
);
|
||||
|
||||
$group->post(
|
||||
'/import',
|
||||
Controller\Api\Stations\Playlists\ImportAction::class
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
v-if="row.item.source === 'songs' && row.item.order === 'sequential'">
|
||||
{{ langReorderButton }}
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item @click.prevent="doQueue(row.item.links.queue)"
|
||||
v-if="row.item.source === 'songs' && row.item.order !== 'random'">
|
||||
{{ langQueueButton }}
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item @click.prevent="doModify(row.item.links.reshuffle)"
|
||||
v-if="row.item.order === 'shuffle'">
|
||||
{{ langReshuffleButton }}
|
||||
|
@ -110,6 +114,8 @@
|
|||
<edit-modal ref="editModal" :create-url="listUrl" :station-time-zone="stationTimeZone"
|
||||
:enable-advanced-features="enableAdvancedFeatures" @relist="relist"></edit-modal>
|
||||
<reorder-modal ref="reorderModal"></reorder-modal>
|
||||
<queue-modal ref="queueModal"></queue-modal>
|
||||
<reorder-modal ref="reorderModal"></reorder-modal>
|
||||
<import-modal ref="importModal" @relist="relist"></import-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -120,11 +126,12 @@ import Schedule from './components/ScheduleView';
|
|||
import EditModal from './station_playlists/PlaylistEditModal';
|
||||
import ReorderModal from './station_playlists/PlaylistReorderModal';
|
||||
import ImportModal from './station_playlists/PlaylistImportModal';
|
||||
import QueueModal from './station_playlists/PlaylistQueueModal';
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'StationPlaylists',
|
||||
components: { ImportModal, ReorderModal, EditModal, Schedule, DataTable },
|
||||
components: { QueueModal, ImportModal, ReorderModal, EditModal, Schedule, DataTable },
|
||||
props: {
|
||||
listUrl: String,
|
||||
scheduleUrl: String,
|
||||
|
@ -156,6 +163,9 @@ export default {
|
|||
langReorderButton () {
|
||||
return this.$gettext('Reorder');
|
||||
},
|
||||
langQueueButton () {
|
||||
return this.$gettext('Playback Queue');
|
||||
},
|
||||
langReshuffleButton () {
|
||||
return this.$gettext('Reshuffle');
|
||||
},
|
||||
|
@ -226,6 +236,9 @@ export default {
|
|||
doReorder (url) {
|
||||
this.$refs.reorderModal.open(url);
|
||||
},
|
||||
doQueue (url) {
|
||||
this.$refs.queueModal.open(url);
|
||||
},
|
||||
doImport (url) {
|
||||
this.$refs.importModal.open(url);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<b-modal size="lg" id="queue_modal" ref="modal" :title="langTitle" :busy="loading">
|
||||
<p>
|
||||
<translate key="queue_info">This queue contains the remaining tracks in the order they will be queued by the AzuraCast AutoDJ (if the tracks are eligible to be played).</translate>
|
||||
</p>
|
||||
<b-overlay variant="card" :show="loading">
|
||||
<b-table-simple striped class="sortable mb-0">
|
||||
<b-thead>
|
||||
<tr>
|
||||
<th style="width: 50%;" key="lang_col_title" v-translate>Title</th>
|
||||
<th style="width: 50%;" key="lang_col_artist" v-translate>Artist</th>
|
||||
</tr>
|
||||
</b-thead>
|
||||
<b-tbody>
|
||||
<tr class="align-middle" v-for="(row,index) in media" :key="row.id">
|
||||
<td><big>{{ row.title }}</big></td>
|
||||
<td>{{ row.artist }}</td>
|
||||
</tr>
|
||||
</b-tbody>
|
||||
</b-table-simple>
|
||||
</b-overlay>
|
||||
<template v-slot:modal-footer>
|
||||
<b-button variant="default" type="button" @click="close">
|
||||
<translate key="lang_btn_close">Close</translate>
|
||||
</b-button>
|
||||
<b-button variant="danger" type="submit" @click="doClear">
|
||||
<translate key="lang_btn_clear_queue">Clear Queue</translate>
|
||||
</b-button>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'QueueModal',
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
queueUrl: null,
|
||||
media: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
langTitle () {
|
||||
return this.$gettext('Playback Queue');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open (queueUrl) {
|
||||
this.$refs.modal.show();
|
||||
this.queueUrl = queueUrl;
|
||||
this.loading = true;
|
||||
|
||||
axios.get(this.queueUrl).then((resp) => {
|
||||
this.media = resp.data;
|
||||
this.loading = false;
|
||||
}).catch((err) => {
|
||||
this.handleError(err);
|
||||
});
|
||||
},
|
||||
doClear () {
|
||||
axios.delete(this.queueUrl).then((resp) => {
|
||||
notify('<b>' + this.$gettext('Playlist queue cleared.') + '</b>', 'success', false);
|
||||
this.close();
|
||||
}).catch((err) => {
|
||||
this.handleError(err);
|
||||
});
|
||||
},
|
||||
handleError (err) {
|
||||
console.error(err);
|
||||
if (err.response.message) {
|
||||
notify('<b>' + err.response.message + '</b>', 'danger');
|
||||
}
|
||||
},
|
||||
close () {
|
||||
this.loading = false;
|
||||
this.error = null;
|
||||
this.queueUrl = null;
|
||||
|
||||
this.$refs.modal.hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\Api\Stations\Playlists;
|
||||
|
||||
use App\Entity;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class DeleteQueueAction extends AbstractPlaylistsAction
|
||||
{
|
||||
public function __invoke(
|
||||
ServerRequest $request,
|
||||
Response $response,
|
||||
$id
|
||||
): ResponseInterface {
|
||||
$record = $this->requireRecord($request->getStation(), $id);
|
||||
|
||||
$record->setQueue(null);
|
||||
$this->em->persist($record);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return $response->withJson(
|
||||
new Entity\Api\Status(
|
||||
true,
|
||||
__('Playlist queue cleared.')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller\Api\Stations\Playlists;
|
||||
|
||||
use App\Entity;
|
||||
use App\Http\Response;
|
||||
use App\Http\ServerRequest;
|
||||
use App\Paginator;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class GetQueueAction extends AbstractPlaylistsAction
|
||||
{
|
||||
public function __invoke(
|
||||
ServerRequest $request,
|
||||
Response $response,
|
||||
$id
|
||||
): ResponseInterface {
|
||||
$record = $this->requireRecord($request->getStation(), $id);
|
||||
|
||||
if (Entity\StationPlaylist::SOURCE_SONGS !== $record->getSource()) {
|
||||
throw new \InvalidArgumentException('This playlist does not have songs as its primary source.');
|
||||
}
|
||||
|
||||
if (Entity\StationPlaylist::ORDER_RANDOM === $record->getOrder()) {
|
||||
throw new \InvalidArgumentException('This playlist is always shuffled and has no visible queue.');
|
||||
}
|
||||
|
||||
$queue = (array)$record->getQueue();
|
||||
$paginator = Paginator::fromArray($queue, $request);
|
||||
|
||||
return $paginator->write($response);
|
||||
}
|
||||
}
|
|
@ -209,6 +209,12 @@ class PlaylistsController extends AbstractScheduledEntityController
|
|||
[],
|
||||
!$isInternal
|
||||
),
|
||||
'queue' => $router->fromHere(
|
||||
'api:stations:playlist:queue',
|
||||
['id' => $record->getId()],
|
||||
[],
|
||||
!$isInternal
|
||||
),
|
||||
'import' => $router->fromHere('api:stations:playlist:import', ['id' => $record->getId()], [], !$isInternal),
|
||||
'self' => $router->fromHere($this->resourceRouteName, ['id' => $record->getId()], [], !$isInternal),
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue