getStation(); $podcast = $this->podcastRepository->fetchPodcastForStation($station, $podcast_id); $queryBuilder = $this->em->createQueryBuilder() ->select('e, p, pm') ->from(Entity\PodcastEpisode::class, 'e') ->join('e.podcast', 'p') ->leftJoin('e.media', 'pm') ->where('e.podcast = :podcast') ->orderBy('e.title', 'ASC') ->setParameter('podcast', $podcast); $searchPhrase = trim($request->getParam('searchPhrase', '')); if (!empty($searchPhrase)) { $queryBuilder->andWhere('e.title LIKE :title') ->setParameter('title', '%' . $searchPhrase . '%'); } return $this->listPaginatedFromQuery($request, $response, $queryBuilder->getQuery()); } public function getAction( ServerRequest $request, Response $response, string $episode_id ): ResponseInterface { $station = $request->getStation(); $record = $this->getRecord($station, $episode_id); if (null === $record) { return $response->withStatus(404) ->withJson(new Entity\Api\Error(404, __('Record not found!'))); } $return = $this->viewRecord($record, $request); return $response->withJson($return); } public function createAction( ServerRequest $request, Response $response, string $podcast_id ): ResponseInterface { $station = $request->getStation(); $podcast = $this->podcastRepository->fetchPodcastForStation($station, $podcast_id); $record = $this->editRecord( $request->getParsedBody(), new Entity\PodcastEpisode($podcast) ); $this->processFiles($request, $record); return $response->withJson($this->viewRecord($record, $request)); } public function editAction( ServerRequest $request, Response $response, string $episode_id ): ResponseInterface { $podcast = $this->getRecord($request->getStation(), $episode_id); if ($podcast === null) { return $response->withStatus(404) ->withJson(new Entity\Api\Error(404, __('Record not found!'))); } $this->editRecord($request->getParsedBody(), $podcast); $this->processFiles($request, $podcast); return $response->withJson(new Entity\Api\Status(true, __('Changes saved successfully.'))); } public function deleteAction( ServerRequest $request, Response $response, string $episode_id ): ResponseInterface { $station = $request->getStation(); $record = $this->getRecord($station, $episode_id); if (null === $record) { return $response->withStatus(404) ->withJson(new Entity\Api\Error(404, __('Record not found!'))); } $fsStation = new StationFilesystems($station); $this->episodeRepository->delete($record, $fsStation->getPodcastsFilesystem()); return $response->withJson(new Entity\Api\Status(true, __('Record deleted successfully.'))); } /** * @param Entity\Station $station * @param string $id */ protected function getRecord(Entity\Station $station, string $id): ?object { return $this->episodeRepository->fetchEpisodeForStation($station, $id); } protected function viewRecord(object $record, ServerRequest $request): mixed { if (!($record instanceof Entity\PodcastEpisode)) { throw new \InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass)); } $isInternal = ('true' === $request->getParam('internal', 'false')); $router = $request->getRouter(); $return = new Entity\Api\PodcastEpisode(); $return->id = $record->getId(); $return->title = $record->getTitle(); $return->description = $record->getDescription(); $return->explicit = $record->getExplicit(); $return->publish_at = $record->getPublishAt(); $mediaRow = $record->getMedia(); $return->has_media = ($mediaRow instanceof Entity\PodcastMedia); if ($mediaRow instanceof Entity\PodcastMedia) { $media = new Entity\Api\PodcastMedia(); $media->id = $mediaRow->getId(); $media->original_name = $mediaRow->getOriginalName(); $media->length = $mediaRow->getLength(); $media->length_text = $mediaRow->getLengthText(); $media->path = $mediaRow->getPath(); $return->has_media = true; $return->media = $media; } else { $return->has_media = false; $return->media = new Entity\Api\PodcastMedia(); } $return->art_updated_at = $record->getArtUpdatedAt(); $return->has_custom_art = (0 !== $return->art_updated_at); $return->art = $router->fromHere( route_name: 'api:stations:podcast:episode:art', route_params: ['episode_id' => $record->getId() . '|' . $record->getArtUpdatedAt()], absolute: true ); $return->links = [ 'self' => $router->fromHere( route_name: $this->resourceRouteName, route_params: ['episode_id' => $record->getId()], absolute: !$isInternal ), 'public' => $router->fromHere( route_name: 'public:podcast:episode', route_params: ['episode_id' => $record->getId()], absolute: !$isInternal ), 'download' => $router->fromHere( route_name: 'api:stations:podcast:episode:download', route_params: ['episode_id' => $record->getId()], absolute: !$isInternal ), ]; $acl = $request->getAcl(); $station = $request->getStation(); if ($acl->isAllowed(Acl::STATION_PODCASTS, $station)) { $return->links['art'] = $router->fromHere( route_name: 'api:stations:podcast:episode:art-internal', route_params: ['episode_id' => $record->getId()], absolute: !$isInternal ); } return $return; } protected function processFiles( ServerRequest $request, Entity\PodcastEpisode $record ): void { $files = $request->getUploadedFiles(); $artwork = $files['artwork_file'] ?? null; if ($artwork instanceof UploadedFileInterface && UPLOAD_ERR_OK === $artwork->getError()) { $this->episodeRepository->writeEpisodeArt( $record, $artwork->getStream()->getContents() ); $this->em->persist($record); $this->em->flush(); } $media = $files['media_file'] ?? null; if ($media instanceof UploadedFileInterface && UPLOAD_ERR_OK === $media->getError()) { $fsStations = new StationFilesystems($request->getStation()); $fsTemp = $fsStations->getTempFilesystem(); $originalName = basename($media->getClientFilename()) ?? $record->getId() . '.mp3'; $originalExt = pathinfo($originalName, PATHINFO_EXTENSION); $tempPath = $fsTemp->getLocalPath($record->getId() . '.' . $originalExt); $media->moveTo($tempPath); $artwork = $this->podcastMediaRepository->upload( $record, $originalName, $tempPath, $fsStations->getPodcastsFilesystem() ); if (!empty($artwork) && 0 === $record->getArtUpdatedAt()) { $this->episodeRepository->writeEpisodeArt( $record, $artwork ); } $this->em->persist($record); $this->em->flush(); } } }