Compare commits

..

No commits in common. "rewrite" and "master" have entirely different histories.

31 changed files with 674 additions and 4492 deletions

View File

@ -1,23 +0,0 @@
.DS_Store
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
.marginalia
js/
build/
coverage/
utils/docker-ci
vendor/
.php-cs-fixer.cache

3
.gitignore vendored
View File

@ -18,6 +18,3 @@ js/
build/
coverage/
utils/docker-ci
vendor/
.php-cs-fixer.cache

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
require_once './vendor/autoload.php';
use Nextcloud\CodingStandard\Config;
class MyConfig extends Config
{
public function getRules(): array
{
$rules = parent::getRules();
$rules['@PhpCsFixer'] = true;
$rules['curly_braces_position']['classes_opening_brace'] = 'next_line_unless_newline_at_signature_end';
$rules['phpdoc_to_comment'] = false;
return $rules;
}
}
$config = new MyConfig();
$config
->getFinder()
->notPath('build')
->notPath('l10n')
->notPath('node_modules')
->notPath('src')
->notPath('vendor')
->in(__DIR__);
return $config;

View File

@ -1,21 +0,0 @@
FROM nextcloud:30
ARG APP_NAME=radio
ENV NEXTCLOUD_UPDATE=1
ENV NEXTCLOUD_ADMIN_USER=$APP_NAME
ENV NEXTCLOUD_ADMIN_PASSWORD=$APP_NAME
ENV NEXTCLOUD_INIT_HTACCESS=1
ENV SQLITE_DATABASE=$APP_NAME
RUN apt-get update && \
apt-get install -y nodejs npm sqlite3 && \
rm -f /usr/local/etc/php/conf.d/opcache-recommended.ini && \
/entrypoint.sh true
USER www-data
COPY --chown=www-data:www-data . apps/$APP_NAME
RUN php occ app:enable $APP_NAME && \
php occ config:system:set debug --value=true
USER root

View File

@ -4,7 +4,7 @@
all: dev-setup lint build-js-production test
# Dev env management
dev-setup: clean clean-dev composer-init npm-init
dev-setup: clean clean-dev npm-init
npm-init:
npm ci
@ -12,12 +12,6 @@ npm-init:
npm-update:
npm update
composer-init:
composer install
composer-update:
composer update
# Building
build-js:
npm run dev
@ -58,7 +52,6 @@ clean:
clean-dev:
rm -rf node_modules
rm -rf vendor
release:
krankerl package
@ -70,13 +63,6 @@ translations:
php utils/docker-ci/translations/translationtool/translationtool.phar create-pot-files
php utils/docker-ci/translations/translationtool/translationtool.phar convert-po-files
dev: dev-setup
docker stop radio || true
docker rm radio || true
docker build -t radio .
docker run -itd --rm --name radio -v $(PWD):/var/www/html/apps/radio -p 80:80 radio
npm run watch
publish-appstore-nightly:
$(eval ASSET_URL = $(shell curl -s https://git.project-insanity.org/api/v4/projects/onny%2Fnextcloud-app-radio/releases | jq -r '.[0].assets.links[0].url'))
wget $(ASSET_URL) -O build/artifacts/radio.tar.gz

View File

@ -16,17 +16,17 @@ app uses radio-browser.info database as a backend.
- 👂 Smoth audio playback and transitions
</description>
<version>2.0.0</version>
<version>1.1.0</version>
<licence>agpl</licence>
<author mail="onny@project-insanity.org">Jonas Heinrich</author>
<author mail="onny@project-insanity.org" >Jonas Heinrich</author>
<namespace>Radio</namespace>
<category>multimedia</category>
<website>https://git.project-insanity.org/onny/nextcloud-app-radio</website>
<bugs>https://git.project-insanity.org/onny/nextcloud-app-radio/issues</bugs>
<screenshot small-thumbnail="https://git.project-insanity.org/onny/nextcloud-app-radio/raw/master/screenshot-thumbnail.jpg">https://git.project-insanity.org/onny/nextcloud-app-radio/raw/master/screenshot.png</screenshot>
<dependencies>
<php min-version="8.1" />
<nextcloud min-version="29" max-version="30"/>
<php min-version="7.4" max-version="8" />
<nextcloud min-version="20" max-version="21"/>
</dependencies>
<navigations>
<navigation>

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,6 +18,7 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
return [
@ -30,41 +29,42 @@ return [
'station' => ['url' => '/station'],
],
'routes' => [
// Web page templates
[
'name' => 'page#index',
'url' => '/',
'verb' => 'GET',
'verb' => 'GET'
],
[
'name' => 'page#index',
'url' => '/top',
'verb' => 'GET',
'postfix' => 'top',
'postfix' => 'top'
],
[
'name' => 'page#index',
'url' => '/recent',
'verb' => 'GET',
'postfix' => 'recent',
'postfix' => 'recent'
],
[
'name' => 'page#index',
'url' => '/new',
'verb' => 'GET',
'postfix' => 'new',
'postfix' => 'new'
],
[
'name' => 'page#index',
'url' => '/favorites',
'verb' => 'GET',
'postfix' => 'favorites',
'postfix' => 'favorites'
],
[
'name' => 'page#index',
'url' => '/categories',
'verb' => 'GET',
'postfix' => 'categories',
'postfix' => 'categories'
],
[
'name' => 'page#index',
@ -78,13 +78,14 @@ return [
'name' => 'favorite_api#preflighted_cors',
'url' => '/api/0.1/{path}',
'verb' => 'OPTIONS',
'requirements' => ['path' => '.+'],
'requirements' => ['path' => '.+']
],
[
'name' => 'recent_api#preflighted_cors',
'url' => '/api/0.1/{path}',
'verb' => 'OPTIONS',
'requirements' => ['path' => '.+'],
],
],
'requirements' => ['path' => '.+']
]
]
];

View File

@ -2,35 +2,11 @@
"name": "onny/radio",
"description": "Lint config for onny/radio",
"license": "AGPL",
"autoload": {
"psr-4": {
"OCA\\Radio\\": "lib/"
}
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm --threads=1 --no-cache --show-info=true",
"rector": "rector && composer cs:fix"
},
"require": {
"php": "^8.1"
},
"require-dev": {
"nextcloud/ocp": "^30.0.2",
"roave/security-advisories": "dev-latest",
"nextcloud/coding-standard": "^1.3.2",
"nextcloud/rector": "^0.2.1",
"rector/rector": "^1.2.10",
"vimeo/psalm": "^5.26.1",
"doctrine/dbal": "^4.2.1"
},
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true,
"platform": {
"php": "8.1"
"classmap-authoritative": true
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -exec php -l \"{}\" \\;"
}
}
}

3567
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,23 +18,25 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
declare(strict_types=1);
namespace OCA\Radio\AppInfo;
use OCA\Radio\Dashboard\RadioWidget;
use OC\Security\CSP\ContentSecurityPolicy;
use OCA\Radio\Search\SearchProvider;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\IRequest;
use Psr\Container\ContainerInterface;
class Application extends App implements IBootstrap
{
use OCA\Radio\Dashboard\RadioWidget;
class Application extends App implements IBootstrap {
public const APP_ID = 'radio';
public function __construct() {
@ -42,11 +44,32 @@ class Application extends App implements IBootstrap
}
public function register(IRegistrationContext $context): void {
$context->registerSearchProvider(SearchProvider::class);
$context->registerDashboardWidget(RadioWidget::class);
$context->registerService('request', static fn (ContainerInterface $c): mixed => $c->get(IRequest::class));
$context->registerService('request', static function ($c) {
return $c->get(IRequest::class);
});
$this->registerCsp();
}
public function boot(IBootContext $context): void {}
public function boot(IBootContext $context): void {
}
/**
* Allow radio-browser hosts in the csp
*
* @throws \OCP\AppFramework\QueryException
*/
public function registerCsp() {
$manager = $this->getContainer()->getServer()->getContentSecurityPolicyManager();
$policy = new ContentSecurityPolicy();
$policy->addAllowedConnectDomain('https://de1.api.radio-browser.info');
$policy->addAllowedImageDomain('*');
$policy->addAllowedMediaDomain('*');
$manager->addDefaultPolicy($policy);
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,23 +18,24 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Controller;
use OCA\Radio\Service\StationNotFound;
use Closure;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
trait Errors
{
protected function handleNotFound(\Closure $callback): DataResponse {
try {
/** @psalm-suppress MixedArgument */
return new DataResponse($callback());
} catch (StationNotFound $stationNotFound) {
$message = ['message' => $stationNotFound->getMessage()];
use OCA\Radio\Service\StationNotFound;
trait Errors {
protected function handleNotFound(Closure $callback): DataResponse {
try {
return new DataResponse($callback());
} catch (StationNotFound $e) {
$message = ['message' => $e->getMessage()];
return new DataResponse($message, Http::STATUS_NOT_FOUND);
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,70 +18,60 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Controller;
use OC;
use OCA\Radio\ExportResponse;
use OCA\Radio\AppInfo\Application;
use OCA\Radio\Service\FavoriteService;
use OCA\Radio\Service\UserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\Defaults;
use OCP\HintException;
use OCP\IDateTimeFormatter;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use SimpleXMLElement;
use DOMDocument;
class ExportController extends Controller {
/** @var FavoriteService */
private $service;
/** @var string */
private $userId;
class ExportController extends Controller
{
use Errors;
public function __construct(
IRequest $request,
private readonly FavoriteService $service,
private readonly UserService $user,
private readonly Defaults $defaults,
private readonly IDateTimeFormatter $dateTime,
) {
public function __construct(IRequest $request,
FavoriteService $service,
$userId) {
parent::__construct(Application::APP_ID, $request);
$this->service = $service;
$this->userId = $userId;
}
#[NoAdminRequired]
public function index(): DataDownloadResponse {
$user = $this->user->getUser();
if (is_null($user)) {
throw new HintException('User not logged in');
}
/**
* @NoAdminRequired
*/
public function index() {
$xml = new \SimpleXMLElement('<?xml version="1.0"?><playlist></playlist>');
$xml = new SimpleXMLElement('<?xml version="1.0"?><playlist></playlist>');
$xml->addAttribute('encoding', 'UTF-8');
$trackList = $xml->addChild('trackList');
if ($trackList instanceof \SimpleXMLElement) {
foreach ($this->service->findAll($user->getUID()) as $station) {
foreach($this->service->findAll($this->userId) as $station) {
$track = $trackList->addChild('track');
if ($track instanceof \SimpleXMLElement) {
$track->addChild('location', $station->getUrlresolved());
$track->addChild('title', $station->getName());
$track->addChild('image', $station->getFavicon());
}
}
}
$dom = new \DOMDocument('1.0');
$dom = new DOMDocument("1.0");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
/** @psalm-suppress ArgumentTypeCoercion */
$dom->loadXML($xml->asXML());
$returnstring = $dom->saveXML();
$userName = $user->getDisplayName();
$productName = $this->defaults->getName();
$date = $this->dateTime->formatDate(time());
$export_name = sprintf('%s Radio Favorites (%s) (%s).xspf', $productName, $userName, $date);
return new ExportResponse($dom->saveXML());
return new DataDownloadResponse($returnstring, $export_name, 'application/xspf+xml');
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,101 +18,83 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Controller;
use OCA\Radio\AppInfo\Application;
use OCA\Radio\Db\Station;
use OCA\Radio\Service\FavoriteService;
use OCA\Radio\Service\UserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
class FavoriteController extends Controller
{
class FavoriteController extends Controller {
/** @var FavoriteService */
private $service;
/** @var string */
private $userId;
use Errors;
public function __construct(
IRequest $request,
private readonly FavoriteService $service,
private readonly UserService $user,
) {
public function __construct(IRequest $request,
FavoriteService $service,
$userId) {
parent::__construct(Application::APP_ID, $request);
$this->service = $service;
$this->userId = $userId;
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function index(): DataResponse {
return new DataResponse($this->service->findAll($this->user->getUserUID()));
return new DataResponse($this->service->findAll($this->userId));
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function show(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->find($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->find($id, $this->userId);
});
}
#[NoAdminRequired]
public function create(
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return new DataResponse($this->service->create(
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function create(string $stationuuid, string $name, string $favicon, string $urlresolved,
string $bitrate, string $country, string $language, string $homepage,
string $codec, string $tags): DataResponse {
return new DataResponse($this->service->create($stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId));
}
#[NoAdminRequired]
public function update(
int $id,
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->update(
$id,
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function update(int $id, string $stationuuid,
string $name, string $favicon, string $urlresolved,
string $bitrate, string $country, string $language, string $homepage,
string $codec, string $tags): DataResponse {
return $this->handleNotFound(function () use ($id, $stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags) {
return $this->service->update($id, $stationuuid, $name, $favicon,
$urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId);
});
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function destroy(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->delete($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->delete($id, $this->userId);
});
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,42 +18,36 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
declare(strict_types=1);
namespace OCA\Radio\Controller;
use OCA\Radio\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Controller;
use OCP\Util;
class PageController extends Controller
{
public function __construct(IRequest $request) {
parent::__construct(Application::APP_ID, $request);
class PageController extends Controller {
protected $appName;
public function __construct($appName, IRequest $request) {
parent::__construct($appName, $request);
$this->appName = $appName;
}
#[NoAdminRequired]
#[NoCSRFRequired]
public function index(): TemplateResponse {
Util::addScript(Application::APP_ID, 'radio-main');
Util::addStyle(Application::APP_ID, 'icons');
// Allow radio-browser hosts in the csp
$policy = new ContentSecurityPolicy();
$policy->addAllowedConnectDomain('https://de1.api.radio-browser.info');
$policy->addAllowedImageDomain('*');
$policy->addAllowedMediaDomain('*');
$response = new TemplateResponse(Application::APP_ID, 'main');
$response->setContentSecurityPolicy($policy);
/**
* @NoAdminRequired
* @NoCSRFRequired
*/
public function index() {
Util::addScript($this->appName, 'radio-main');
Util::addStyle($this->appName, 'icons');
$response = new TemplateResponse($this->appName, 'main');
return $response;
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,101 +18,82 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Controller;
use OCA\Radio\AppInfo\Application;
use OCA\Radio\Db\Station;
use OCA\Radio\Service\RecentService;
use OCA\Radio\Service\UserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
class RecentController extends Controller
{
class RecentController extends Controller {
/** @var RecentService */
private $service;
/** @var string */
private $userId;
use Errors;
public function __construct(
IRequest $request,
private readonly RecentService $service,
private readonly UserService $user,
) {
public function __construct(IRequest $request,
RecentService $service,
$userId) {
parent::__construct(Application::APP_ID, $request);
$this->service = $service;
$this->userId = $userId;
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function index(): DataResponse {
return new DataResponse($this->service->findAll($this->user->getUserUID()));
return new DataResponse($this->service->findAll($this->userId));
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function show(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->find($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->find($id, $this->userId);
});
}
#[NoAdminRequired]
public function create(
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return new DataResponse($this->service->create(
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function create(string $stationuuid, string $name, string $favicon, string $urlresolved,
string $bitrate, string $country, string $language, string $homepage,
string $codec, string $tags): DataResponse {
return new DataResponse($this->service->create($stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId));
}
#[NoAdminRequired]
public function update(
int $id,
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->update(
$id,
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function update(int $id, string $stationuuid, string $name,
string $favicon, string $urlresolved, string $bitrate, string $country,
string $language, string $homepage, string $codec, string $tags): DataResponse {
return $this->handleNotFound(function () use ($id, $stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags) {
return $this->service->update($id, $stationuuid, $name, $favicon,
$urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId);
});
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function destroy(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->delete($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->delete($id, $this->userId);
});
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,103 +18,87 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Controller;
use OCA\Radio\AppInfo\Application;
use OCA\Radio\Db\Station;
use OCA\Radio\Service\RadioBrowserApiService;
use OCA\Radio\Service\RecentService;
use OCA\Radio\Service\UserService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
class StationController extends Controller
{
class StationController extends Controller {
/** @var StationController */
private $service;
/** @var RadioBrowserApiService */
private $radiobrowserapi;
/** @var string */
private $userId;
use Errors;
public function __construct(
IRequest $request,
private readonly RecentService $service,
private readonly RadioBrowserApiService $radiobrowserapi,
private readonly UserService $user,
) {
public function __construct(IRequest $request,
RecentService $service,
RadioBrowserApiService $radiobrowserapi,
$userId) {
parent::__construct(Application::APP_ID, $request);
$this->service = $service;
$this->radiobrowserapi = $radiobrowserapi;
$this->userId = $userId;
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function index(): DataResponse {
return new DataResponse($this->service->findAll($this->user->getUserUID()));
return new DataResponse($this->service->findAll($this->userId));
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function show(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->find($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->find($id, $this->userId);
});
}
#[NoAdminRequired]
public function create(
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return new DataResponse($this->service->create(
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function create(string $stationuuid, string $name, string $favicon, string $urlresolved,
string $bitrate, string $country, string $language, string $homepage,
string $codec, string $tags): DataResponse {
return new DataResponse($this->service->create($stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId));
}
#[NoAdminRequired]
public function update(
int $id,
string $stationuuid,
string $name,
string $favicon,
string $urlresolved,
string $bitrate,
string $country,
string $language,
string $homepage,
string $codec,
string $tags
): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->update(
$id,
$stationuuid,
$name,
$favicon,
$urlresolved,
$bitrate,
$country,
$language,
$homepage,
$codec,
$tags,
$this->user->getUserUID()
));
/**
* @NoAdminRequired
*/
public function update(int $id, string $stationuuid, string $name,
string $favicon, string $urlresolved, string $bitrate, string $country,
string $language, string $homepage, string $codec, string $tags): DataResponse {
return $this->handleNotFound(function () use ($id, $stationuuid, $name,
$favicon, $urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags) {
return $this->service->update($id, $stationuuid, $name, $favicon,
$urlresolved, $bitrate, $country, $language, $homepage, $codec,
$tags, $this->userId);
});
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function destroy(int $id): DataResponse {
return $this->handleNotFound(fn (): ?Station => $this->service->delete($id, $this->user->getUserUID()));
return $this->handleNotFound(function () use ($id) {
return $this->service->delete($id, $this->userId);
});
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,43 +18,72 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Dashboard;
use OCA\Radio\AppInfo\Application;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Util;
class RadioWidget implements IWidget
{
public function __construct(
private readonly IL10N $l10n,
private readonly IURLGenerator $urlGenerator
) {}
use OCA\Radio\AppInfo\Application;
class RadioWidget implements IWidget {
/** @var IL10N */
private $l10n;
/** @var IURLGenerator */
private $urlGenerator;
public function __construct(
IL10N $l10n,
IURLGenerator $urlGenerator
) {
$this->l10n = $l10n;
$this->urlGenerator = $urlGenerator;
}
/**
* @inheritDoc
*/
public function getId(): string {
return Application::APP_ID;
}
/**
* @inheritDoc
*/
public function getTitle(): string {
return $this->l10n->t('Radio stations');
}
/**
* @inheritDoc
*/
public function getOrder(): int {
return 10;
}
/**
* @inheritDoc
*/
public function getIconClass(): string {
return 'icon-radio';
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute('radio.page.index'));
}
/**
* @inheritDoc
*/
public function load(): void {
Util::addScript(Application::APP_ID, 'radio-dashboard');
Util::addStyle(Application::APP_ID, 'dashboard');

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,50 +18,49 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
* @extends QBMapper<Station>
*/
class FavoriteMapper extends QBMapper
{
class FavoriteMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'favorites', Station::class);
}
/**
* @throws MultipleObjectsReturnedException
* @param int $id
* @param string $userId
* @return Entity|Station
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function find(int $id, string $userId): Station {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('favorites')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
;
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
return $this->findEntity($qb);
}
/**
* @return Station[]
* @param string $userId
* @return array
*/
public function findAll(string $userId): array {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('favorites')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
;
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
return $this->findEntities($qb);
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,30 +18,31 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
* @extends QBMapper<Station>
*/
class RecentMapper extends QBMapper
{
class RecentMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, 'recent', Station::class);
}
/**
* @throws MultipleObjectsReturnedException
* @param int $id
* @param string $userId
* @return Entity|Station
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function find(int $id, string $userId): Station {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->selectDistinct('stationuuid')
->addSelect('name')
@ -58,16 +57,16 @@ class RecentMapper extends QBMapper
->from('recent')
->orderBy('id', 'DESC')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
;
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
return $this->findEntity($qb);
}
/**
* @return Station[]
* @param string $userId
* @return array
*/
public function findAll(string $userId): array {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->selectDistinct('stationuuid')
->addSelect('name')
@ -81,9 +80,7 @@ class RecentMapper extends QBMapper
->addSelect('tags')
->from('recent')
->orderBy('id', 'DESC')
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)))
;
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId)));
return $this->findEntities($qb);
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,61 +18,27 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Db;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;
/**
* @method string getStationuuid()
* @method string getName()
* @method ?string getFavicon()
* @method ?string getUrlresolved()
* @method ?string getBitrate()
* @method ?string getCountry()
* @method ?string getLanguage()
* @method ?string getHomepage()
* @method ?string getCodec()
* @method ?string getTags()
* @method string getUserId()
* @method void setStationuuid(string $stationuuid)
* @method void setName(string $name)
* @method void setFavicon(?string $favicon)
* @method void setUrlresolved(?string $urlresolved)
* @method void setBitrate(?string $bitrate)
* @method void setCountry(?string $country)
* @method void setLanguage(?string $language)
* @method void setHomepage(?string $homepage)
* @method void setCodec(?string $codec)
* @method void setTags(?string $tags)
* @method void setUserId(string $userId)
*
* @psalm-suppress PropertyNotSetInConstructor
*/
class Station extends Entity implements \JsonSerializable
{
protected string $stationuuid;
protected string $name;
protected ?string $favicon = null;
protected ?string $urlresolved = null;
protected ?string $bitrate = null;
protected ?string $country = null;
protected ?string $language = null;
protected ?string $homepage = null;
protected ?string $codec = null;
protected ?string $tags = null;
protected string $userId;
class Station extends Entity implements JsonSerializable {
protected $stationuuid;
protected $name;
protected $favicon;
protected $urlresolved;
protected $bitrate;
protected $country;
protected $language;
protected $homepage;
protected $codec;
protected $tags;
protected $userId;
public function jsonSerialize(): array {
return [
@ -88,8 +52,7 @@ class Station extends Entity implements \JsonSerializable
'language' => $this->language,
'homepage' => $this->homepage,
'codec' => $this->codec,
'tags' => $this->tags,
'userId' => $this->userId,
'tags' => $this->tags
];
}
}

56
lib/ExportResponse.php Normal file
View File

@ -0,0 +1,56 @@
<?php
/**
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio;
use OC;
use OC\HintException;
use OCP\AppFramework\Http\Response;
class ExportResponse extends Response {
private $returnstring;
public function __construct($returnstring) {
parent::__construct();
$user = OC::$server->getUserSession()->getUser();
if (is_null($user)) {
throw new HintException('User not logged in');
}
$userName = $user->getDisplayName();
$productName = OC::$server->getThemingDefaults()->getName();
$dateTime = OC::$server->getDateTimeFormatter();
$export_name = '"' . $productName . ' Radio Favorites (' . $userName . ') (' . $dateTime->formatDate(time()) . ').xspf"';
$this->addHeader("Cache-Control", "private");
$this->addHeader("Content-Type", " application/xspf+xml");
$this->addHeader("Content-Length", strlen($returnstring));
$this->addHeader("Content-Disposition", "attachment; filename=" . $export_name);
$this->returnstring = $returnstring;
}
public function render() {
return $this->returnstring;
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,24 +18,27 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
declare(strict_types=1);
namespace OCA\Radio\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
class Version000000Date20181013124731 extends SimpleMigrationStep {
class Version000000Date20181013124731 extends SimpleMigrationStep
{
/**
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
*
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,6 +18,7 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
declare(strict_types=1);
@ -25,22 +26,32 @@ declare(strict_types=1);
namespace OCA\Radio\Search;
use OCA\Radio\AppInfo\Application;
use OCP\Http\Client\IClientService;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IURLGenerator;
use OCP\Search\IProvider;
use OCP\Search\ISearchQuery;
use OCP\Search\SearchResult;
use OCP\Search\SearchResultEntry;
use Psr\Log\LoggerInterface;
class SearchProvider implements IProvider
{
use OCP\Http\Client\IClientService;
use function urlencode;
class SearchProvider implements IProvider {
/** @var IClientService */
private $clientService;
/** @var IURLGenerator */
private $url;
public function __construct(
private readonly IClientService $clientService,
private readonly IURLGenerator $url,
private readonly LoggerInterface $logger,
) {}
IClientService $clientService,
IURLGenerator $url
) {
$this->clientService = $clientService;
$this->url = $url;
}
public function getId(): string {
return Application::APP_ID;
@ -51,43 +62,38 @@ class SearchProvider implements IProvider
}
public function getOrder(string $route, array $routeParameters): int {
if (str_starts_with($route, 'files.')) {
if (strpos($route, 'files' . '.') === 0) {
return 25;
}
if (str_starts_with($route, Application::APP_ID.'.')) {
} elseif (strpos($route, Application::APP_ID . '.') === 0) {
return -1;
}
return 4;
}
public function search(IUser $user, ISearchQuery $query): SearchResult {
$term = $query->getTerm();
$url = 'https://de1.api.radio-browser.info/json/stations/byname/'.$term.'?limit=20';
$url = "https://de1.api.radio-browser.info/json/stations/byname/" . $term . "?limit=20";
$client = $this->clientService->newClient();
try {
$response = $client->get($url);
} catch (\Exception $exception) {
$this->logger->error('Could not search for radio stations: '.$exception->getMessage());
throw $exception;
} catch (Exception $e) {
$this->logger->error("Could not search for radio stations: " . $e->getMessage());
throw $e;
}
$body = (string) $response->getBody();
/** @var array<array<string, string>> $parsed */
$body = $response->getBody();
$parsed = json_decode($body, true);
$result = array_map(fn (array $result): SearchResultEntry => new SearchResultEntry(
$result = array_map(function (array $result) use ($term) {
return new SearchResultEntry(
$result['favicon'],
$result['name'],
str_replace(',', ', ', $result['tags']),
$this->url->linkToRouteAbsolute('radio.page.index').'#/search/'.$term,
str_replace(",",", ",$result['tags']),
$this->url->linkToRouteAbsolute('radio.page.index') . '#/search/' . $term,
'icon-radio-trans'
), $parsed);
);
}, $parsed);
return SearchResult::complete(
$this->getName(),

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,55 +18,56 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Service;
use OCA\Radio\Db\FavoriteMapper;
use OCA\Radio\Db\Station;
use Exception;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
class FavoriteService
{
public function __construct(
private readonly FavoriteMapper $mapper
) {
use OCA\Radio\Db\Station;
use OCA\Radio\Db\FavoriteMapper;
class FavoriteService {
/** @var FavoriteMapper */
private $mapper;
public function __construct(FavoriteMapper $mapper) {
$this->mapper = $mapper;
}
/**
* @return Station[]
*/
public function findAll(string $userId): array {
return $this->mapper->findAll($userId);
}
public function find(int $id, string $userId): ?Station {
private function handleException(Exception $e): void {
if ($e instanceof DoesNotExistException ||
$e instanceof MultipleObjectsReturnedException) {
throw new StationNotFound($e->getMessage());
} else {
throw $e;
}
}
public function find($id, $userId) {
try {
return $this->mapper->find($id, $userId);
// in order to be able to plug in different storage backends like files
// for instance it is a good idea to turn storage related exceptions
// into service related exceptions so controllers and service users
// have to deal with only one type of exception
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
public function create(
string $stationuuid,
string $name,
?string $favicon,
?string $urlresolved,
?string $bitrate,
?string $country,
?string $language,
?string $homepage,
?string $codec,
?string $tags,
string $userId
): Station {
public function create($stationuuid, $name, $favicon, $urlresolved,
$bitrate, $country, $language, $homepage, $codec, $tags, $userId) {
$station = new Station();
$station->setStationuuid($stationuuid);
$station->setName($name);
@ -81,24 +80,11 @@ class FavoriteService
$station->setCodec($codec);
$station->setTags($tags);
$station->setUserId($userId);
return $this->mapper->insert($station);
}
public function update(
int $id,
string $stationuuid,
string $name,
?string $favicon,
?string $urlresolved,
?string $bitrate,
?string $country,
?string $language,
?string $homepage,
?string $codec,
?string $tags,
string $userId
): ?Station {
public function update($id, $stationuuid, $name, $favicon, $urlresolved,
$bitrate, $country, $language, $homepage, $codec, $tags, $userId) {
try {
$station = $this->mapper->find($id, $userId);
$station->setStationuuid($stationuuid);
@ -111,30 +97,19 @@ class FavoriteService
$station->setHomepage($homepage);
$station->setCodec($codec);
$station->setTags($tags);
return $this->mapper->update($station);
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
public function delete(int $id, string $userId): ?Station {
public function delete($id, $userId) {
try {
$station = $this->mapper->find($id, $userId);
$this->mapper->delete($station);
return $station;
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
private function handleException(\Throwable $e): void {
if ($e instanceof DoesNotExistException
|| $e instanceof MultipleObjectsReturnedException) {
throw new StationNotFound($e->getMessage());
}
throw $e;
}
}

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,116 +18,119 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
declare(strict_types=1);
namespace OCA\Radio\Service;
use OCP\IURLGenerator;
use OCP\Http\Client\IClientService;
use Psr\Log\LoggerInterface;
class RadioBrowserApiService
{
public string $baseUrl = 'https://api.fyyd.de/0.2';
use function urlencode;
class RadioBrowserApiService {
/** @var IClientService */
private $clientService;
/** @var IURLGenerator */
private $url;
public $baseUrl = "https://api.fyyd.de/0.2";
public function __construct(
private readonly IClientService $clientService,
private readonly LoggerInterface $logger,
) {}
IClientService $clientService,
IURLGenerator $url
) {
$this->clientService = $clientService;
$this->url = $url;
}
public function queryEpisodes(int $podcast_id, int $count = 20, int $page = 0): mixed {
$url = $this->baseUrl.'/podcast/episodes';
$options = [];
public function queryEpisodes(int $podcast_id, int $count = 20, int $page = 0) {
$url = $this->baseUrl . "/podcast/episodes";
$options['query'] = [
'podcast_id' => $podcast_id,
'count' => $count,
'page' => $page,
'page' => $page
];
$client = $this->clientService->newClient();
try {
$response = $client->get($url, $options);
} catch (\Exception $exception) {
$this->logger->error('Could not search for podcasts: '.$exception->getMessage());
} catch (Exception $e) {
$this->logger->error("Could not search for podcasts: " . $e->getMessage());
throw $e;
}
$body = $response->getBody();
throw $exception;
$parsed = json_decode($body, true);
return $parsed;
}
$body = (string) $response->getBody();
public function queryEpisode(int $episode_id) {
return json_decode($body, true);
}
public function queryEpisode(int $episode_id): mixed {
$url = $this->baseUrl.'/episode';
$options = [];
$url = $this->baseUrl . "/episode";
$options['query'] = [
'episode_id' => $episode_id,
];
$client = $this->clientService->newClient();
try {
$response = $client->get($url, $options);
} catch (\Exception $exception) {
$this->logger->error('Could not search for podcasts: '.$exception->getMessage());
} catch (Exception $e) {
$this->logger->error("Could not search for podcasts: " . $e->getMessage());
throw $e;
}
$body = $response->getBody();
$parsed = json_decode($body, true);
throw $exception;
return $parsed;
}
$body = (string) $response->getBody();
public function queryPodcast(int $podcast_id) {
return json_decode($body, true);
}
public function queryPodcast(int $podcast_id): mixed {
$url = $this->baseUrl.'/podcast';
$options = [];
$url = $this->baseUrl . "/podcast";
$options['query'] = [
'podcast_id' => $podcast_id,
];
$client = $this->clientService->newClient();
try {
$response = $client->get($url, $options);
} catch (\Exception $exception) {
$this->logger->error('Could not search for podcasts: '.$exception->getMessage());
} catch (Exception $e) {
$this->logger->error("Could not search for podcasts: " . $e->getMessage());
throw $e;
}
$body = $response->getBody();
$parsed = json_decode($body, true);
throw $exception;
return $parsed;
}
$body = (string) $response->getBody();
public function queryCategory(string $category, int $count = 20,
int $page = 0) {
return json_decode($body, true);
}
public function queryCategory(
string $category,
int $count = 20,
int $page = 0
): mixed {
$options = [];
if ('hot' === $category) {
$url = $this->baseUrl.'/feature/podcast/hot';
if ($category === 'hot') {
$url = $this->baseUrl . "/feature/podcast/hot";
$options['query'] = [
'count' => $count,
'page' => $page,
];
} elseif ('latest' === $category) {
$url = $this->baseUrl.'/podcast/latest';
} else if ($category === 'latest') {
$url = $this->baseUrl . "/podcast/latest";
$options['query'] = [
'count' => $count,
'page' => $page,
];
} else {
$url = $this->baseUrl.'/category';
$url = $this->baseUrl . "/category";
$options['query'] = [
'count' => $count,
'page' => $page,
@ -136,17 +139,15 @@ class RadioBrowserApiService
}
$client = $this->clientService->newClient();
try {
$response = $client->get($url, $options);
} catch (\Exception $exception) {
$this->logger->error('Could not search for podcasts: '.$exception->getMessage());
throw $exception;
} catch (Exception $e) {
$this->logger->error("Could not search for podcasts: " . $e->getMessage());
throw $e;
}
$body = $response->getBody();
$parsed = json_decode($body, true);
$body = (string) $response->getBody();
return json_decode($body, true);
return $parsed;
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,55 +18,56 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Service;
use OCA\Radio\Db\RecentMapper;
use OCA\Radio\Db\Station;
use Exception;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
class RecentService
{
public function __construct(
private readonly RecentMapper $mapper
) {
use OCA\Radio\Db\Station;
use OCA\Radio\Db\RecentMapper;
class RecentService {
/** @var RecentMapper */
private $mapper;
public function __construct(RecentMapper $mapper) {
$this->mapper = $mapper;
}
/**
* @return Station[]
*/
public function findAll(string $userId): array {
return $this->mapper->findAll($userId);
}
public function find(int $id, string $userId): ?Station {
private function handleException(Exception $e): void {
if ($e instanceof DoesNotExistException ||
$e instanceof MultipleObjectsReturnedException) {
throw new StationNotFound($e->getMessage());
} else {
throw $e;
}
}
public function find($id, $userId) {
try {
return $this->mapper->find($id, $userId);
// in order to be able to plug in different storage backends like files
// for instance it is a good idea to turn storage related exceptions
// into service related exceptions so controllers and service users
// have to deal with only one type of exception
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
public function create(
string $stationuuid,
string $name,
?string $favicon,
?string $urlresolved,
?string $bitrate,
?string $country,
?string $language,
?string $homepage,
?string $codec,
?string $tags,
string $userId
): Station {
public function create($stationuuid, $name, $favicon, $urlresolved,
$bitrate, $country, $language, $homepage, $codec, $tags, $userId) {
$station = new Station();
$station->setStationuuid($stationuuid);
$station->setName($name);
@ -81,24 +80,11 @@ class RecentService
$station->setCodec($codec);
$station->setTags($tags);
$station->setUserId($userId);
return $this->mapper->insert($station);
}
public function update(
int $id,
string $stationuuid,
string $name,
?string $favicon,
?string $urlresolved,
?string $bitrate,
?string $country,
?string $language,
?string $homepage,
?string $codec,
?string $tags,
string $userId
): ?Station {
public function update($id, $stationuuid, $name, $favicon, $urlresolved,
$bitrate, $country, $language, $homepage, $codec, $tags, $userId) {
try {
$station = $this->mapper->find($id, $userId);
$station->setStationuuid($stationuuid);
@ -111,33 +97,19 @@ class RecentService
$station->setHomepage($homepage);
$station->setCodec($codec);
$station->setTags($tags);
return $this->mapper->update($station);
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
public function delete(int $id, string $userId): ?Station {
public function delete($id, $userId) {
try {
$station = $this->mapper->find($id, $userId);
$this->mapper->delete($station);
return $station;
} catch (\Exception $exception) {
return $this->handleException($exception);
} catch (Exception $e) {
$this->handleException($e);
}
}
/**
* @throws \Exception
*/
private function handleException(\Throwable $e): void {
if ($e instanceof DoesNotExistException
|| $e instanceof MultipleObjectsReturnedException) {
throw new StationNotFound($e->getMessage());
}
throw $e;
}
}

View File

@ -1,9 +1,7 @@
<?php
declare(strict_types=1);
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -20,8 +18,10 @@ declare(strict_types=1);
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Radio\Service;
class StationNotFound extends \Exception {}
class StationNotFound extends \Exception {
}

View File

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace OCA\Radio\Service;
use OCP\IUser;
use OCP\IUserSession;
class UserService
{
public function __construct(
private readonly IUserSession $userSession
) {}
public function getUserUID(): string {
$user = $this->getUser();
return $user instanceof IUser ? $user->getUID() : '';
}
public function getUser(): ?IUser {
return $this->userSession->getUser();
}
}

View File

@ -1,21 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedCode="false"
phpVersion="8.1"
>
<projectFiles>
<directory name="lib" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<extraFiles>
<directory name="vendor"/>
</extraFiles>
</psalm>

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
use Nextcloud\Rector\Set\NextcloudSets;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__.'/appinfo',
__DIR__.'/lib',
])
->withPhpSets(php81: true)
->withSets([
NextcloudSets::NEXTCLOUD_27,
])
->withPreparedSets(
deadCode: true,
codeQuality: true,
codingStyle: true,
typeDeclarations: true,
privatization: true,
instanceOf: true,
earlyReturn: true,
strictBooleans: true,
rectorPreset: true,
phpunitCodeQuality: true,
doctrineCodeQuality: true,
symfonyCodeQuality: true,
symfonyConfigs: true,
twig: true,
phpunit: true,
)
;

View File

@ -1,7 +1,7 @@
<?php
/**
* Radio App.
* Radio App
*
* @author Jonas Heinrich
* @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
@ -18,5 +18,7 @@
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
echo "<div id='vue-content'></div>";