Vuetify SoundExchange report.

This commit is contained in:
Buster "Silver Eagle" Neece 2021-11-04 12:35:13 -05:00
parent 5b114a5f31
commit 408d4c6a4b
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
8 changed files with 152 additions and 109 deletions

View File

@ -1,75 +0,0 @@
<?php
return [
'method' => 'post',
'elements' => [
'report_details' => [
'markup',
[
'label' => _('Important Notes'),
'markup' => '
<p>This report is intended for licensing in the United States only, for webcasters paying royalties via
SoundExchange. Learn more about the requirements for reporting and classification on the
<a href="https://www.soundexchange.com/service-provider/reporting-requirements/" target="_blank">
SoundExchange web site
</a>.</p>
<ul>
<li>AzuraCast assumes that your station fits SoundExchange Transmission Category A, "Eligible nonsubscription
transmissions other than broadcast simulcasts and transmissions of non-music programming." If your
station does not fall within this category, update the transmission category field accordingly.
<li>The data collected by AzuraCast meets the SoundExchange standard for Actual Total Performances (ATP)
by tracking unique listeners across all song plays. All other information is derived from the metadata of the
uploaded songs themselves, and may not be completely accurate.</li>
<li>Reporting requirements for SoundExchange may change at any time. AzuraCast is non-commercial community-built
software and cannot guarantee that its reporting format will always be up-to-date.</li>
<li>You should always verify the report generated by AzuraCast before sending it. In particular, either the ISRC
(International Standard Recording Code) or *both* the album and label are required for every row, and may not be
provided in the song metadata. To locate an ISRC for a track, you should use
<a href="https://isrc.soundexchange.com" target="_blank">SoundExchange\'s ISRC search tool</a>.</li>
</ul>',
]
],
'start_date' => [
'text',
[
'type' => 'date',
'label' => _('Report Start Date'),
'required' => true,
]
],
'end_date' => [
'text',
[
'type' => 'date',
'label' => _('Report End Date'),
'required' => true,
],
],
'fetch_isrc' => [
'toggle',
[
'label' => __('Attempt to Automatically Retrieve ISRC When Missing'),
'description' => __(
'If enabled, AzuraCast will connect to the MusicBrainz database to attempt to find an ISRC for any files where one is missing. Disabling this may improve performance.'
),
'selected_text' => __('Yes'),
'deselected_text' => __('No'),
'default' => true,
],
],
'submit' => [
'submit',
[
'type' => 'submit',
'label' => _('Generate Report'),
'class' => 'btn btn-lg btn-primary',
],
],
],
];

View File

@ -104,7 +104,7 @@ return static function (RouteCollectorProxy $app) {
$group->map(
['GET', 'POST'],
'/soundexchange',
Controller\Stations\Reports\SoundExchangeController::class
Controller\Stations\Reports\SoundExchangeAction::class
)
->setName('stations:reports:soundexchange');

View File

@ -28,7 +28,7 @@
<b-alert variant="danger" :show="error != null">{{ error }}</b-alert>
<form id="login-form" action="" method="post">
<form id="login-form" class="form vue-form" action="" method="post">
<input type="hidden" name="csrf" :value="csrf"/>
<b-wrapped-form-group id="username" name="username" label-class="mb-2" :field="$v.form.username"

View File

@ -0,0 +1,116 @@
<template>
<section class="card" role="region">
<div class="card-header bg-primary-dark">
<h3 class="card-title">
<translate key="hdr">SoundExchange Report</translate>
</h3>
</div>
<form id="report-form" class="form vue-form" method="POST" action="">
<input type="hidden" name="csrf" :value="csrf"/>
<div class="card-body">
<b-form-fieldset>
<p>This report is intended for licensing in the United States only, for webcasters paying royalties
via SoundExchange. Learn more about the requirements for reporting and classification on the
<a href="https://www.soundexchange.com/service-provider/reporting-requirements/"
target="_blank">
SoundExchange web site
</a>.
</p>
<ul>
<li>AzuraCast assumes that your station fits SoundExchange Transmission Category A, "Eligible
nonsubscription transmissions other than broadcast simulcasts and transmissions of non-music
programming." If your station does not fall within this category, update the transmission
category field accordingly.
<li>The data collected by AzuraCast meets the SoundExchange standard for Actual Total
Performances (ATP) by tracking unique listeners across all song plays. All other information
is derived from the metadata of the uploaded songs themselves, and may not be completely
accurate.
</li>
<li>Reporting requirements for SoundExchange may change at any time. AzuraCast is non-commercial
community-built software and cannot guarantee that its reporting format will always be
up-to-date.
</li>
<li>You should always verify the report generated by AzuraCast before sending it. In particular,
either the ISRC (International Standard Recording Code) or *both* the album and label are
required for every row, and may not be provided in the song metadata. To locate an ISRC for
a track, you should use <a href="https://isrc.soundexchange.com" target="_blank">SoundExchange's
ISRC search tool</a>.
</li>
</ul>
</b-form-fieldset>
<b-form-fieldset>
<b-wrapped-form-group name="start_date" id="form_start_date" :field="$v.form.start_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group name="end_date" id="form_end_date" :field="$v.form.end_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox name="fetch_isrc" id="form_edit_fetch_isrc" :field="$v.form.fetch_isrc">
<template #label="{lang}">
<translate :key="lang">Attempt to Automatically Retrieve ISRC When Missing</translate>
</template>
<template #description="{lang}">
<translate :key="lang">If enabled, AzuraCast will connect to the MusicBrainz database to attempt to find an ISRC for any files where one is missing. Disabling this may improve performance.</translate>
</template>
</b-wrapped-form-checkbox>
</b-form-fieldset>
<b-button type="submit" size="lg" variant="primary" class="mt-2" :disabled="$v.form.$invalid">
<translate key="btn_submit">Generate Report</translate>
</b-button>
</div>
</form>
</section>
</template>
<script>
import Icon from "~/components/Common/Icon";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {validationMixin} from "vuelidate";
import {required} from 'vuelidate/dist/validators.min.js';
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox";
export default {
name: 'StationsReportsSoundExchange',
props: {
csrf: String,
startDate: String,
endDate: String
},
components: {Icon, BWrappedFormGroup, BFormFieldset, BWrappedFormCheckbox},
mixins: [
validationMixin
],
validations() {
return {
form: {
start_date: {required},
end_date: {required},
fetch_isrc: {}
}
}
},
data() {
return {
form: {
start_date: this.startDate,
end_date: this.endDate,
fetch_isrc: false
}
}
}
}
</script>

View File

@ -0,0 +1,8 @@
import initBase from '~/base.js';
import '~/vendor/bootstrapVue.js';
import '~/vendor/luxon.js';
import SoundExchange from '~/components/Stations/Reports/SoundExchange.vue';
export default initBase(SoundExchange);

View File

@ -43,6 +43,7 @@ module.exports = {
StationsReportsRequests: '~/pages/Stations/Reports/Requests.js',
StationsReportsOverview: '~/pages/Stations/Reports/Overview.js',
StationsReportsPerformance: '~/pages/Stations/Reports/Performance.js',
StationsReportsSoundExchange: '~/pages/Stations/Reports/SoundExchange.js',
StationsReportsTimeline: '~/pages/Stations/Reports/Timeline.js',
StationsSftpUsers: '~/pages/Stations/SftpUsers.js',
StationsWebhooks: '~/pages/Stations/Webhooks.js'

View File

@ -4,9 +4,7 @@ declare(strict_types=1);
namespace App\Controller\Stations\Reports;
use App\Config;
use App\Entity;
use App\Form\Form;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Service\MusicBrainz;
@ -18,42 +16,36 @@ use Throwable;
/**
* Produce a report in SoundExchange (the US webcaster licensing agency) format.
*/
class SoundExchangeController
class SoundExchangeAction
{
protected array $form_config;
public function __construct(
protected EntityManagerInterface $em,
protected MusicBrainz $musicBrainz,
Config $config
protected MusicBrainz $musicBrainz
) {
$this->form_config = $config->get('forms/report/soundexchange');
}
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
{
$station = $request->getStation();
$tzObject = $station->getTimezoneObject();
$startDate = CarbonImmutable::parse('first day of last month', $tzObject);
$endDate = CarbonImmutable::parse('last day of last month', $tzObject);
$csrf = $request->getCsrf();
$form = new Form($this->form_config);
$form->populate(
[
'start_date' => $startDate->format('Y-m-d'),
'end_date' => $endDate->format('Y-m-d'),
]
);
$defaultStartDate = CarbonImmutable::parse('first day of last month', $tzObject)->format('Y-m-d');
$defaultEndDate = CarbonImmutable::parse('last day of last month', $tzObject)->format('Y-m-d');
if ($form->isValid($request)) {
$data = $form->getValues();
if ($request->isPost()) {
$data = (array)$request->getParsedBody();
$csrf->verify($data['csrf'] ?? '', 'soundexchange');
$data['start_date'] ??= $defaultStartDate;
$data['end_date'] ??= $defaultEndDate;
$startDate = CarbonImmutable::parse($data['start_date'] . ' 00:00:00', $tzObject);
$endDate = CarbonImmutable::parse($data['end_date'] . ' 23:59:59', $tzObject);
$fetchIsrc = $data['fetch_isrc'];
$fetchIsrc = 'true' === ($data['fetch_isrc'] ?? 'false');
$export = [
[
@ -99,6 +91,7 @@ class SoundExchangeController
->setParameter('time_end', $endDate->getTimestamp())
->getArrayResult();
// TODO: Fix this (not all song rows have a media_id)
$history_rows_by_id = array_column($history_rows, null, 'media_id');
// Remove any reference to the "Stream Offline" song.
@ -163,11 +156,17 @@ class SoundExchangeController
return $response->renderStringAsFile($export_txt, 'text/plain', $export_filename);
}
return $request->getView()->renderToResponse($response, 'system/form_page', [
'form' => $form,
'render_mode' => 'edit',
'title' => __('SoundExchange Report'),
]);
return $request->getView()->renderVuePage(
response: $response,
component: 'Vue_StationsReportsSoundExchange',
id: 'station-report-soundexchange',
title: __('SoundExchange Report'),
props: [
'csrf' => $csrf->generate('soundexchange'),
'startDate' => $defaultStartDate,
'endDate' => $defaultEndDate,
]
);
}
protected function findISRC(array $song_row): ?string

View File

@ -41,11 +41,5 @@ class Station_ReportsCest extends CestAbstract
$I->amOnPage('/station/' . $station_id . '/reports/soundexchange');
$I->seeResponseCodeIs(200);
$I->see('SoundExchange Report');
$I->submitForm(
'form#azuraforms_form',
['azuraforms_form_start_date' => '11/01/2020', 'azuraforms_form_end_date' => '11/30/2020']
);
$I->seeResponseCodeIs(200);
}
}