Adjust EpisodeAction upload to gpodder api, remove String upload

Remove 3rd dimension of POST data by directly accessing request data - a simple EpisodeActions array has to be posted now. This way the api is closer to gpodder.
Additionally, removed EpisodeAction upload as String, so it's now incompatible with older versions
This commit is contained in:
Jonathan Flueren 2021-10-05 14:57:49 +02:00 committed by thrillfall
parent 91c2dccd3c
commit 795a9253d2
4 changed files with 40 additions and 121 deletions

View File

@ -22,6 +22,8 @@ class EpisodeActionController extends Controller {
private $userId; private $userId;
private EpisodeActionSaver $episodeActionSaver; private EpisodeActionSaver $episodeActionSaver;
protected $request;
public function __construct( public function __construct(
string $AppName, string $AppName,
IRequest $request, IRequest $request,
@ -33,6 +35,7 @@ class EpisodeActionController extends Controller {
$this->episodeActionRepository = $episodeActionRepository; $this->episodeActionRepository = $episodeActionRepository;
$this->userId = $UserId; $this->userId = $UserId;
$this->episodeActionSaver = $episodeActionSaver; $this->episodeActionSaver = $episodeActionSaver;
$this->request = $request;
} }
/** /**
@ -40,10 +43,15 @@ class EpisodeActionController extends Controller {
* @NoAdminRequired * @NoAdminRequired
* @NoCSRFRequired * @NoCSRFRequired
* *
* @return Response * @return JSONResponse
*/ */
public function create($data) { public function create(): JSONResponse {
return $this->episodeActionSaver->saveEpisodeActions($data, $this->userId);
$episodeActionsArray = $this->filterEpisodesFromRequestParams($this->request->getParams());
$this->episodeActionSaver->saveEpisodeActions($episodeActionsArray, $this->userId);
return new JSONResponse(["timestamp" => time()]);
} }
/** /**
@ -67,4 +75,26 @@ class EpisodeActionController extends Controller {
"timestamp" => time() "timestamp" => time()
]); ]);
} }
/**
* @param array $requestParams
*
* @return array $episodeActionsArray
*/
public function filterEpisodesFromRequestParams(array $data): array {
return array_filter($data, "is_numeric", ARRAY_FILTER_USE_KEY);
}
/**
* @param int|null $since
*
* @return DateTime
*/
private function createDateTimeFromTimestamp(?int $since): DateTime {
return ($since !== null)
? (new \DateTime)->setTimestamp($since)
: (new \DateTime('-1 week'));
}
} }

View File

@ -5,55 +5,6 @@ namespace OCA\GPodderSync\Core\EpisodeAction;
class EpisodeActionReader class EpisodeActionReader
{ {
const EPISODEACTION_IDENTIFIER = 'EpisodeAction{';
/**
* @param string $episodeActionsString
* @return EpisodeAction[]
*/
public function fromString(string $episodeActionsString): array
{
$patterns = [
'/EpisodeAction{(podcast=\')(?<podcast>.*?)(\', episode=\')(?<episode>.*?)(\', guid=\')(?<guid>.*?)(\', action=)(?<action>.*?)(, timestamp=)(?<timestamp>.*?)(, started=)(?<started>.*?)(, position=)(?<position>.*?)(, total=)(?<total>.*?)}]*/',
'/EpisodeAction{(podcast=\')(?<podcast>.*?)(\', episode=\')(?<episode>.*?)(\', action=)(?<action>.*?)(, timestamp=)(?<timestamp>.*?)(, started=)(?<started>.*?)(, position=)(?<position>.*?)(, total=)(?<total>.*?)}]*/',
];
$episodeActions = [];
$episodeActionStrings = explode(self::EPISODEACTION_IDENTIFIER, $episodeActionsString);
array_shift($episodeActionStrings);
foreach ($episodeActionStrings as $episodeActionString) {
foreach ($patterns as $pattern) {
preg_match(
$pattern,
self::EPISODEACTION_IDENTIFIER . $episodeActionString,
$matches
);
if ($matches["action"] !== null) {
$episodeActions[] = new EpisodeAction(
$matches["podcast"],
$matches["episode"],
$matches["action"],
$matches["timestamp"],
(int)$matches["started"],
(int)$matches["position"],
(int)$matches["total"],
$matches["guid"] ?? null,
null,
);
break;
}
}
}
return $episodeActions;
}
/** /**
* @param $episodeActionsArray[] * @param $episodeActionsArray[]
* @return EpisodeAction[] * @return EpisodeAction[]

View File

@ -29,32 +29,27 @@ class EpisodeActionSaver
} }
/** /**
* @param string|array $data * @param array $episodeActionsArray
* *
* @return EpisodeActionEntity[] * @return void
*/ */
public function saveEpisodeActions($data, string $userId): array public function saveEpisodeActions($episodeActionsArray, string $userId): void
{ {
$episodeActionEntities = []; $episodeActions = $this->episodeActionReader->fromArray($episodeActionsArray);
$episodeActions = is_array($data)
? $this->episodeActionReader->fromArray($data)
: $this->episodeActionReader->fromString($data);
foreach ($episodeActions as $episodeAction) { foreach ($episodeActions as $episodeAction) {
$episodeActionEntity = $this->hydrateEpisodeActionEntity($episodeAction, $userId); $episodeActionEntity = $this->hydrateEpisodeActionEntity($episodeAction, $userId);
try { try {
$episodeActionEntities[] = $this->episodeActionWriter->save($episodeActionEntity); $this->episodeActionWriter->save($episodeActionEntity);
} catch (UniqueConstraintViolationException $uniqueConstraintViolationException) { } catch (UniqueConstraintViolationException $uniqueConstraintViolationException) {
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId); $this->updateEpisodeAction($episodeActionEntity, $userId);
} catch (Exception $exception) { } catch (Exception $exception) {
if ($exception->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { if ($exception->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
$episodeActionEntities[] = $this->updateEpisodeAction($episodeActionEntity, $userId); $this->updateEpisodeAction($episodeActionEntity, $userId);
} }
} }
} }
return $episodeActionEntities;
} }
private function convertTimestampToUnixEpoch(string $timestamp): string private function convertTimestampToUnixEpoch(string $timestamp): string

View File

@ -7,63 +7,6 @@ use OCA\GPodderSync\Core\EpisodeAction\EpisodeActionReader;
use Test\TestCase; use Test\TestCase;
class EpisodeActionReaderTest extends TestCase { class EpisodeActionReaderTest extends TestCase {
public function testCreateFromString(): void {
$reader = new EpisodeActionReader();
$episodeActions = $reader->fromString('[EpisodeAction{podcast=\'https://feeds.simplecast.com/wEl4UUJZ\', episode=\'https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ\', action=PLAY, timestamp=Tue May 18 23:45:11 GMT+02:00 2021, started=31, position=36, total=2474}]');
$episodeAction = $episodeActions[0];
$this->assertSame("https://feeds.simplecast.com/wEl4UUJZ", $episodeAction->getPodcast());
$this->assertSame("https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ", $episodeAction->getEpisode());
$this->assertSame("PLAY", $episodeAction->getAction());
$this->assertSame("Tue May 18 23:45:11 GMT+02:00 2021", $episodeAction->getTimestamp());
$this->assertNull($episodeAction->getGuid());
$this->assertSame(31, $episodeAction->getStarted());
$this->assertSame(36, $episodeAction->getPosition());
$this->assertSame(2474, $episodeAction->getTotal());
}
public function testCreateFromStringWithGuid(): void {
$reader = new EpisodeActionReader();
$episodeActions = $reader->fromString("[EpisodeAction{podcast='https://rss.art19.com/dr-death-s3-miracle-man', episode='https://dts.podtrac.com/redirect.mp3/chrt.fm/track/9EE2G/pdst.fm/e/rss.art19.com/episodes/db0e3500', guid='gid://art19-episode-locator/V0/KtdfKfLndyWetCZ6t2WhUEFOFArDXRExVIb4Z1fh-TU', action=PLAY, timestamp=Mon Aug 23 01:58:56 GMT+02:00 2021, started=47, position=54, total=2252}]");
$episodeAction = $episodeActions[0];
$this->assertSame("https://rss.art19.com/dr-death-s3-miracle-man", $episodeAction->getPodcast());
$this->assertSame("https://dts.podtrac.com/redirect.mp3/chrt.fm/track/9EE2G/pdst.fm/e/rss.art19.com/episodes/db0e3500", $episodeAction->getEpisode());
$this->assertSame("PLAY", $episodeAction->getAction());
$this->assertSame("Mon Aug 23 01:58:56 GMT+02:00 2021", $episodeAction->getTimestamp());
$this->assertSame("gid://art19-episode-locator/V0/KtdfKfLndyWetCZ6t2WhUEFOFArDXRExVIb4Z1fh-TU", $episodeAction->getGuid());
$this->assertSame(47, $episodeAction->getStarted());
$this->assertSame(54, $episodeAction->getPosition());
$this->assertSame(2252, $episodeAction->getTotal());
}
public function testCreateFromMultipleEpisodesString(): void {
$reader = new EpisodeActionReader();
$episodeActions = $reader->fromString('[EpisodeAction{podcast=\'https://example.com/feed.xml\', episode=\'https://example.com/episode1.mp3\', action=PLAY, timestamp=Tue May 18 23:45:11 GMT+02:00 2021, started=31, position=36, total=2474},EpisodeAction{podcast=\'https://example.com/feed.xml\', episode=\'https://example.com/episode2.mp3\', action=DOWNLOAD, timestamp=Tue May 18 23:46:42 GMT+02:00 2021, started=31, position=36, total=2474},EpisodeAction{podcast=\'https://example.org/feed.xml\', episode=\'https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ\', action=PLAY, timestamp=Tue May 18 23:45:14 GMT+02:00 2021, started=0, position=211, total=3121}]');
$this->assertSame("https://example.com/feed.xml", $episodeActions[0]->getPodcast());
$this->assertSame("https://example.com/episode1.mp3", $episodeActions[0]->getEpisode());
$this->assertSame("PLAY", $episodeActions[0]->getAction());
$this->assertSame("Tue May 18 23:45:11 GMT+02:00 2021", $episodeActions[0]->getTimestamp());
$this->assertSame(31, $episodeActions[0]->getStarted());
$this->assertSame(36, $episodeActions[0]->getPosition());
$this->assertSame(2474, $episodeActions[0]->getTotal());
$this->assertSame("https://example.com/feed.xml", $episodeActions[1]->getPodcast());
$this->assertSame("https://example.com/episode2.mp3", $episodeActions[1]->getEpisode());
$this->assertSame("DOWNLOAD", $episodeActions[1]->getAction());
$this->assertSame("Tue May 18 23:46:42 GMT+02:00 2021", $episodeActions[1]->getTimestamp());
$this->assertSame(31, $episodeActions[1]->getStarted());
$this->assertSame(36, $episodeActions[1]->getPosition());
$this->assertSame(2474, $episodeActions[1]->getTotal());
$this->assertSame("https://example.org/feed.xml", $episodeActions[2]->getPodcast());
$this->assertSame("https://chrt.fm/track/47G541/injector.simplecastaudio.com/f16c3da7-cf46-4a42-99b7-8467255c6086/episodes/e8e24c01-6157-40e8-9b5a-45d539aeb7e6/audio/128/default.mp3?aid=rss_feed&awCollectionId=f16c3da7-cf46-4a42-99b7-8467255c6086&awEpisodeId=e8e24c01-6157-40e8-9b5a-45d539aeb7e6&feed=wEl4UUJZ", $episodeActions[2]->getEpisode());
$this->assertSame("PLAY", $episodeActions[2]->getAction());
$this->assertSame("Tue May 18 23:45:14 GMT+02:00 2021", $episodeActions[2]->getTimestamp());
$this->assertSame(0, $episodeActions[2]->getStarted());
$this->assertSame(211, $episodeActions[2]->getPosition());
$this->assertSame(3121, $episodeActions[2]->getTotal());
}
public function testCreateFromArray(): void { public function testCreateFromArray(): void {
$reader = new EpisodeActionReader(); $reader = new EpisodeActionReader();
$episodeActions = $reader->fromArray([["podcast" => "https://example.org/feed.xml", "episode" => "https://example.org/episode1.mp3", "action" => "PLAY", "timestamp" => "Sun Oct 03 14:03:17 GMT+02:00 2021", "started" => 0, "position" => 50, "total"=> 3422]]); $episodeActions = $reader->fromArray([["podcast" => "https://example.org/feed.xml", "episode" => "https://example.org/episode1.mp3", "action" => "PLAY", "timestamp" => "Sun Oct 03 14:03:17 GMT+02:00 2021", "started" => 0, "position" => 50, "total"=> 3422]]);