213 lines
7.4 KiB
PHP
213 lines
7.4 KiB
PHP
<?php
|
|
namespace App\Controller\Stations\Reports;
|
|
|
|
use App\Entity;
|
|
use App\Form\Form;
|
|
use App\Http\Response;
|
|
use App\Http\ServerRequest;
|
|
use App\Config;
|
|
use Doctrine\ORM\EntityManager;
|
|
use GuzzleHttp\Client;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
|
|
/**
|
|
* Produce a report in SoundExchange (the US webcaster licensing agency) format.
|
|
*/
|
|
class SoundExchangeController
|
|
{
|
|
/** @var EntityManager */
|
|
protected EntityManager $em;
|
|
|
|
/** @var Client */
|
|
protected Client $http_client;
|
|
|
|
/** @var array */
|
|
protected array $form_config;
|
|
|
|
/**
|
|
* @param EntityManager $em
|
|
* @param Client $http_client
|
|
* @param Config $config
|
|
*/
|
|
public function __construct(EntityManager $em, Client $http_client, Config $config)
|
|
{
|
|
$this->em = $em;
|
|
$this->form_config = $config->get('forms/report/soundexchange');
|
|
$this->http_client = $http_client;
|
|
}
|
|
|
|
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
|
|
{
|
|
$station = $request->getStation();
|
|
|
|
$form = new Form($this->form_config);
|
|
$form->populate([
|
|
'start_date' => date('Y-m-d', strtotime('first day of last month')),
|
|
'end_date' => date('Y-m-d', strtotime('last day of last month')),
|
|
]);
|
|
|
|
if (!empty($_POST) && $form->isValid($_POST)) {
|
|
$data = $form->getValues();
|
|
|
|
$start_date = strtotime($data['start_date'] . ' 00:00:00');
|
|
$end_date = strtotime($data['end_date'] . ' 23:59:59');
|
|
|
|
$export = [
|
|
[
|
|
'NAME_OF_SERVICE',
|
|
'TRANSMISSION_CATEGORY',
|
|
'FEATURED_ARTIST',
|
|
'SOUND_RECORDING_TITLE',
|
|
'ISRC',
|
|
'ALBUM_TITLE',
|
|
'MARKETING_LABEL',
|
|
'ACTUAL_TOTAL_PERFORMANCES',
|
|
],
|
|
];
|
|
|
|
$all_media = $this->em->createQuery(/** @lang DQL */ 'SELECT sm
|
|
FROM App\Entity\StationMedia sm
|
|
WHERE sm.station_id = :station_id')
|
|
->setParameter('station_id', $station->getId())
|
|
->getArrayResult();
|
|
|
|
$media_by_id = [];
|
|
foreach ($all_media as $media_row) {
|
|
$media_by_id[$media_row['song_id']] = $media_row;
|
|
}
|
|
|
|
$history_rows = $this->em->createQuery(/** @lang DQL */ 'SELECT
|
|
sh.song_id AS song_id, COUNT(sh.id) AS plays, SUM(sh.unique_listeners) AS unique_listeners
|
|
FROM App\Entity\SongHistory sh
|
|
WHERE sh.station_id = :station_id
|
|
AND sh.timestamp_start <= :time_end
|
|
AND sh.timestamp_end >= :time_start
|
|
GROUP BY sh.song_id')
|
|
->setParameter('station_id', $station->getId())
|
|
->setParameter('time_start', $start_date)
|
|
->setParameter('time_end', $end_date)
|
|
->getArrayResult();
|
|
|
|
$history_rows_by_id = [];
|
|
foreach ($history_rows as $history_row) {
|
|
$history_rows_by_id[$history_row['song_id']] = $history_row;
|
|
}
|
|
|
|
// Remove any reference to the "Stream Offline" song.
|
|
$offline_song_hash = Entity\Song::getSongHash(['text' => 'stream_offline']);
|
|
unset($history_rows_by_id[$offline_song_hash]);
|
|
|
|
// Get all songs not found in the StationMedia library
|
|
$not_found_songs = array_diff_key($history_rows_by_id, $media_by_id);
|
|
|
|
if (!empty($not_found_songs)) {
|
|
|
|
$songs_raw = $this->em->createQuery(/** @lang DQL */ 'SELECT s
|
|
FROM App\Entity\Song s
|
|
WHERE s.id IN (:song_ids)')
|
|
->setParameter('song_ids', array_keys($not_found_songs))
|
|
->getArrayResult();
|
|
|
|
foreach ($songs_raw as $song_row) {
|
|
$media_by_id[$song_row['id']] = $song_row;
|
|
}
|
|
}
|
|
|
|
// Assemble report items
|
|
$station_name = $station->getName();
|
|
|
|
$set_isrc_query = $this->em->createQuery(/** @lang DQL */ 'UPDATE
|
|
App\Entity\StationMedia sm
|
|
SET sm.isrc = :isrc
|
|
WHERE sm.song_id = :song_id
|
|
AND sm.station_id = :station_id')
|
|
->setParameter('station_id', $station->getId());
|
|
|
|
foreach ($history_rows_by_id as $song_id => $history_row) {
|
|
|
|
$song_row = $media_by_id[$song_id];
|
|
|
|
// Try to find the ISRC if it's not already listed.
|
|
if (array_key_exists('isrc', $song_row) && $song_row['isrc'] === null) {
|
|
$isrc = $this->_findISRC($song_row);
|
|
$song_row['isrc'] = $isrc;
|
|
|
|
$set_isrc_query->setParameter('isrc', $isrc)
|
|
->setParameter('song_id', $song_id)
|
|
->execute();
|
|
}
|
|
|
|
$export[] = [
|
|
$station_name,
|
|
'A',
|
|
$song_row['artist'] ?? '',
|
|
$song_row['title'] ?? '',
|
|
$song_row['isrc'] ?? '',
|
|
$song_row['album'] ?? '',
|
|
'',
|
|
$history_row['unique_listeners'],
|
|
];
|
|
|
|
}
|
|
|
|
// Assemble export into SoundExchange format
|
|
$export_txt_raw = [];
|
|
foreach ($export as $export_row) {
|
|
foreach ($export_row as $i => $export_col) {
|
|
if (!is_numeric($export_col)) {
|
|
$export_row[$i] = '^' . str_replace(['^', '|'], ['', ''], strtoupper($export_col)) . '^';
|
|
}
|
|
}
|
|
$export_txt_raw[] = implode('|', $export_row);
|
|
}
|
|
$export_txt = implode("\n", $export_txt_raw);
|
|
|
|
// Example: WABC01012009-31012009_A.txt
|
|
$export_filename = strtoupper($station->getShortName())
|
|
. date('dmY', $start_date) . '-'
|
|
. date('dmY', $end_date) . '_A.txt';
|
|
|
|
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'),
|
|
]);
|
|
}
|
|
|
|
protected function _findISRC($song_row): string
|
|
{
|
|
// Temporarily disable this feature, as the Spotify API now requires authentication for all requests.
|
|
return '';
|
|
|
|
$query_parts = [];
|
|
if (!empty($song_row['artist'])) {
|
|
$query_parts[] = 'artist:"' . $song_row['artist'] . '"';
|
|
}
|
|
if (!empty($song_row['album'])) {
|
|
$query_parts[] = 'album:"' . $song_row['album'] . '"';
|
|
}
|
|
$query_parts[] = $song_row['title'];
|
|
|
|
$search_response = $this->http_client->get('https://api.spotify.com/v1/search', [
|
|
'query' => [
|
|
'q' => implode(' ', $query_parts),
|
|
'type' => 'track',
|
|
'limit' => 1,
|
|
],
|
|
]);
|
|
|
|
$search_result_raw = $search_response->getBody()->getContents();
|
|
$search_result = @json_decode($search_result_raw, true);
|
|
|
|
if (!empty($search_result['tracks']['items'])) {
|
|
$track = $search_result['tracks']['items'][0];
|
|
return str_replace('-', '', $track['external_ids']['isrc']);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|