EpisodeActionExtra done
This commit is contained in:
parent
3aae3c012f
commit
3f9bc372ce
@ -13,7 +13,7 @@ declare(strict_types=1);
|
||||
return [
|
||||
'routes' => [
|
||||
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'fetch#index', 'url' => '/fetch', 'verb' => 'GET'],
|
||||
['name' => 'podcast#index', 'url' => '/podcast', 'verb' => 'GET'],
|
||||
['name' => 'search#index', 'url' => '/search', 'verb' => 'GET'],
|
||||
['name' => 'toplist#index', 'url' => '/toplist', 'verb' => 'GET'],
|
||||
],
|
||||
|
17
lib/Controller/EpisodesController.php
Normal file
17
lib/Controller/EpisodesController.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\RePod\Controller;
|
||||
|
||||
use OCA\RePod\AppInfo\Application;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\IRequest;
|
||||
|
||||
class EpisodesController extends Controller
|
||||
{
|
||||
public function __construct(IRequest $request)
|
||||
{
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IRequest;
|
||||
|
||||
class FetchController extends Controller
|
||||
class PodcastController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
IRequest $request,
|
112
lib/Core/EpisodeAction/EpisodeActionExtraData.php
Normal file
112
lib/Core/EpisodeAction/EpisodeActionExtraData.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\RePod\Core\EpisodeAction;
|
||||
|
||||
use OCA\GPodderSync\Core\EpisodeAction\EpisodeAction;
|
||||
|
||||
/**
|
||||
* https://github.com/pbek/nextcloud-nextpod/blob/main/lib/Core/EpisodeAction/EpisodeActionExtraData.php.
|
||||
*
|
||||
* @psalm-import-type EpisodeActionType from EpisodeAction
|
||||
*
|
||||
* @psalm-type EpisodeActionExtraDataType = array{
|
||||
* episodeUrl: ?string,
|
||||
* podcastName: ?string,
|
||||
* episodeName: ?string,
|
||||
* episodeLink: ?string,
|
||||
* episodeImage: ?string,
|
||||
* episodeDescription: ?string,
|
||||
* fetchedAtUnix: int,
|
||||
* episodeAction: ?EpisodeActionType
|
||||
* }
|
||||
*/
|
||||
class EpisodeActionExtraData implements \JsonSerializable
|
||||
{
|
||||
public function __construct(
|
||||
private ?string $episodeUrl,
|
||||
private ?string $podcastName,
|
||||
private ?string $episodeName,
|
||||
private ?string $episodeLink,
|
||||
private ?string $episodeImage,
|
||||
private ?string $episodeDescription,
|
||||
private int $fetchedAtUnix,
|
||||
private ?EpisodeAction $episodeAction
|
||||
) {
|
||||
$this->episodeUrl = $episodeUrl;
|
||||
$this->podcastName = $podcastName;
|
||||
$this->episodeName = $episodeName;
|
||||
$this->episodeLink = $episodeLink;
|
||||
$this->episodeImage = $episodeImage;
|
||||
$this->episodeDescription = $episodeDescription;
|
||||
$this->fetchedAtUnix = $fetchedAtUnix;
|
||||
$this->episodeAction = $episodeAction;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->episodeUrl ?? '/no episodeUrl/';
|
||||
}
|
||||
|
||||
public function getEpisodeAction(): ?EpisodeAction
|
||||
{
|
||||
return $this->episodeAction;
|
||||
}
|
||||
|
||||
public function getEpisodeUrl(): ?string
|
||||
{
|
||||
return $this->episodeUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EpisodeActionExtraDataType
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return
|
||||
[
|
||||
'podcastName' => $this->podcastName,
|
||||
'episodeUrl' => $this->episodeUrl,
|
||||
'episodeName' => $this->episodeName,
|
||||
'episodeLink' => $this->episodeLink,
|
||||
'episodeImage' => $this->episodeImage,
|
||||
'episodeDescription' => $this->episodeDescription,
|
||||
'fetchedAtUnix' => $this->fetchedAtUnix,
|
||||
'episodeAction' => $this->episodeAction ? $this->episodeAction->toArray() : null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EpisodeActionExtraDataType
|
||||
*/
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function getPodcastName(): ?string
|
||||
{
|
||||
return $this->podcastName;
|
||||
}
|
||||
|
||||
public function getEpisodeName(): ?string
|
||||
{
|
||||
return $this->episodeName;
|
||||
}
|
||||
|
||||
public function getEpisodeLink(): ?string
|
||||
{
|
||||
return $this->episodeLink;
|
||||
}
|
||||
|
||||
public function getFetchedAtUnix(): int
|
||||
{
|
||||
return $this->fetchedAtUnix;
|
||||
}
|
||||
|
||||
public function getEpisodeImage(): ?string
|
||||
{
|
||||
return $this->episodeImage;
|
||||
}
|
||||
}
|
130
lib/Core/EpisodeAction/EpisodeActionReader.php
Normal file
130
lib/Core/EpisodeAction/EpisodeActionReader.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\RePod\Core\EpisodeAction;
|
||||
|
||||
use OCA\GPodderSync\Db\EpisodeAction\EpisodeActionRepository;
|
||||
use OCA\RePod\Service\UserService;
|
||||
|
||||
class EpisodeActionReader
|
||||
{
|
||||
public function __construct(
|
||||
private EpisodeActionRepository $episodeActionRepository,
|
||||
private UserService $userService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/pbek/nextcloud-nextpod/blob/main/lib/Core/EpisodeAction/EpisodeActionExtraData.php#L119.
|
||||
*
|
||||
* @throws \Exception if the XML data could not be parsed
|
||||
*/
|
||||
public function parseRssXml(string $xmlString, string $episodeUrl, ?int $fetchedAtUnix = null): array
|
||||
{
|
||||
$episodes = [];
|
||||
$xml = new \SimpleXMLElement($xmlString);
|
||||
$channel = $xml->channel;
|
||||
$episodeName = null;
|
||||
$episodeLink = null;
|
||||
$episodeImage = null;
|
||||
$episodeDescription = null;
|
||||
$episodeUrlPath = parse_url($episodeUrl, PHP_URL_PATH);
|
||||
|
||||
// Find episode by url and add data for it
|
||||
/** @var \SimpleXMLElement $item */
|
||||
foreach ($channel->item as $item) {
|
||||
$url = (string) $item->enclosure['url'];
|
||||
|
||||
// First try to match the url directly
|
||||
if (false === strpos($episodeUrl, $url)) {
|
||||
// Then try to match the path only
|
||||
// The podcast http://feeds.feedburner.com/abcradio/10percenthappier has a "rss_browser" query parameter
|
||||
// for every item that changed all the time, so we can't match the full url
|
||||
$path = parse_url($url, PHP_URL_PATH);
|
||||
|
||||
if ($episodeUrlPath !== $path) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get episode action
|
||||
$episodeAction = $this->episodeActionRepository->findByEpisodeUrl($url, $this->userService->getUserUID());
|
||||
|
||||
// Get episode name
|
||||
$episodeName = $this->stringOrNull($item->title);
|
||||
|
||||
// Get episode link
|
||||
$episodeLink = $this->stringOrNull($item->link);
|
||||
|
||||
// Get episode image
|
||||
$episodeImageChildren = $item->children('http://www.itunes.com/dtds/podcast-1.0.dtd');
|
||||
if ($episodeImageChildren) {
|
||||
$episodeImageAttributes = (array) $episodeImageChildren->image->attributes();
|
||||
$episodeImage = $this->stringOrNull(array_key_exists('href', $episodeImageAttributes) ? (string) $episodeImageAttributes['href'] : '');
|
||||
$iTunesChildren = $item->children('itunes', true);
|
||||
|
||||
if ($iTunesChildren && !$episodeImage) {
|
||||
$episodeImage = $this->stringOrNull((string) $iTunesChildren->image['href']);
|
||||
}
|
||||
|
||||
if (!$episodeImage) {
|
||||
$episodeImage = $this->stringOrNull($channel->image->url);
|
||||
}
|
||||
|
||||
if ($iTunesChildren && !$episodeImage) {
|
||||
$episodeImage = $this->stringOrNull((string) $iTunesChildren->image['href']);
|
||||
}
|
||||
|
||||
if (!$episodeImage) {
|
||||
$channelImageChildren = $channel->children('http://www.itunes.com/dtds/podcast-1.0.dtd');
|
||||
if ($channelImageChildren) {
|
||||
$episodeImageAttributes = (array) $channelImageChildren->image->attributes();
|
||||
$episodeImage = $this->stringOrNull(array_key_exists('href', $episodeImageAttributes) ? (string) $episodeImageAttributes['href'] : '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$episodeImage) {
|
||||
preg_match('/<itunes:image\s+href="([^"]+)"/', $xmlString, $matches);
|
||||
$episodeImage = $this->stringOrNull($matches[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Get episode description
|
||||
$episodeContentChildren = $item->children('content', true);
|
||||
|
||||
if ($episodeContentChildren) {
|
||||
$episodeDescription = $this->stringOrNull($episodeContentChildren->encoded);
|
||||
}
|
||||
|
||||
if (!$episodeDescription) {
|
||||
$episodeDescription = $this->stringOrNull($item->description);
|
||||
}
|
||||
|
||||
// Open links in new browser window/tab
|
||||
$episodeDescription = str_replace('<a ', '<a class="description-link" target="_blank" ', $episodeDescription ?? '');
|
||||
|
||||
$episodes[] = new EpisodeActionExtraData(
|
||||
$episodeUrl,
|
||||
$this->stringOrNull($channel->title),
|
||||
$episodeName,
|
||||
$episodeLink,
|
||||
$episodeImage,
|
||||
$episodeDescription,
|
||||
$fetchedAtUnix ?? (new \DateTime())->getTimestamp(),
|
||||
$episodeAction
|
||||
);
|
||||
}
|
||||
|
||||
return $episodes;
|
||||
}
|
||||
|
||||
private function stringOrNull(mixed $value): ?string
|
||||
{
|
||||
if ($value) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -44,7 +44,7 @@ export default {
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
const podcastData = await axios.get(generateUrl('/apps/repod/fetch?url={url}', { url: this.url }))
|
||||
const podcastData = await axios.get(generateUrl('/apps/repod/podcast?url={url}', { url: this.url }))
|
||||
this.feed = podcastData.data.data
|
||||
} catch (e) {
|
||||
this.failed = true
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
class SubscriptionChangeEntity extends Entity implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @template-extends QBMapper<SubscriptionChangeEntity>
|
||||
*/
|
||||
class SubscriptionChangeMapper extends QBMapper
|
||||
{
|
||||
public function __construct(IDBConnection $db)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity[]
|
||||
*/
|
||||
public function findAll(string $userId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?SubscriptionChangeEntity
|
||||
*/
|
||||
public function findByUrl(string $url, string $userId)
|
||||
{
|
||||
}
|
||||
|
||||
public function remove(SubscriptionChangeEntity $subscriptionChangeEntity): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity[]
|
||||
*/
|
||||
public function findAllSubscriptionState(bool $subscribed, \DateTime $sinceTimestamp, string $userId)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||
|
||||
class SubscriptionChangeRepository
|
||||
{
|
||||
public function __construct(private SubscriptionChangeMapper $subscriptionChangeMapper)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity[]
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?SubscriptionChangeEntity
|
||||
*/
|
||||
public function findByUrl(string $episode, string $userId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity[]
|
||||
*/
|
||||
public function findAllSubscribed(\DateTime $sinceTimestamp, string $userId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity[]
|
||||
*/
|
||||
public function findAllUnSubscribed(\DateTime $sinceTimestamp, string $userId)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\GPodderSync\Db\SubscriptionChange;
|
||||
|
||||
class SubscriptionChangeWriter
|
||||
{
|
||||
public function __construct(private SubscriptionChangeMapper $subscriptionChangeMapper)
|
||||
{
|
||||
}
|
||||
|
||||
public function purge(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity
|
||||
*/
|
||||
public function create(SubscriptionChangeEntity $subscriptionChangeEntity)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SubscriptionChangeEntity
|
||||
*/
|
||||
public function update(SubscriptionChangeEntity $subscriptionChangeEntity)
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user