chore(perf): improve podcast adding performance

This commit is contained in:
Phan An 2024-06-07 14:11:45 +02:00
parent 1afd8ba7d5
commit 7f3692b65e
4 changed files with 32 additions and 24 deletions

View file

@ -16,7 +16,6 @@ use Illuminate\Support\Str;
use Laravel\Scout\Searchable;
use PhanAn\Poddle\Values\CategoryCollection;
use PhanAn\Poddle\Values\ChannelMetadata;
use PhanAn\Poddle\Values\Episode as EpisodeDTO;
/**
* @property-read string $id
@ -82,21 +81,6 @@ class Podcast extends Model
->withTimestamps();
}
public function addEpisodeByDTO(EpisodeDTO $dto): Episode
{
return $this->episodes()->create([
'title' => $dto->title,
'lyrics' => '',
'path' => $dto->enclosure->url,
'created_at' => $dto->metadata->pubDate ?: now(),
'episode_metadata' => $dto->metadata,
'episode_guid' => $dto->guid,
'length' => $dto->metadata->duration ?? 0,
'mtime' => time(),
'is_public' => true,
]);
}
/** @return array<mixed> */
public function toSearchableArray(): array
{

View file

@ -99,9 +99,7 @@ class Song extends Model
protected static function booted(): void
{
static::creating(static function (self $song): void {
$song->id ??= Str::uuid()->toString();
});
static::creating(static fn (Song $song) => $song->id ??= Str::uuid()->toString());
}
public static function query(?PlayableType $type = null, ?User $user = null): SongBuilder

View file

@ -17,6 +17,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use PhanAn\Poddle\Poddle;
use PhanAn\Poddle\Values\Episode as EpisodeValue;
use PhanAn\Poddle\Values\EpisodeCollection;
@ -118,13 +119,38 @@ class PodcastService
private function synchronizeEpisodes(Podcast $podcast, EpisodeCollection $episodeCollection): void
{
$existingEpisodeGuids = $this->songRepository->getEpisodeGuidsByPodcast($podcast);
$records = [];
$ids = [];
/** @var EpisodeValue $episodeValue */
foreach ($episodeCollection as $episodeValue) {
if (!in_array($episodeValue->guid->value, $existingEpisodeGuids, true)) {
$podcast->addEpisodeByDTO($episodeValue);
$id = Str::uuid()->toString();
$ids[] = $id;
$records[] = [
'id' => $id,
'podcast_id' => $podcast->id,
'title' => $episodeValue->title,
'lyrics' => '',
'path' => $episodeValue->enclosure->url,
'created_at' => $episodeValue->metadata->pubDate ?: now(),
'updated_at' => $episodeValue->metadata->pubDate ?: now(),
'episode_metadata' => $episodeValue->metadata->toJson(),
'episode_guid' => $episodeValue->guid,
'length' => $episodeValue->metadata->duration ?? 0,
'mtime' => time(),
'is_public' => true,
];
}
}
// We use insert() instead of $podcast->episodes()->createMany() for better performance,
// as the latter would trigger a separate query for each episode.
Episode::insert($records);
// Since insert() doesn't trigger model events, Scout operations will not be called.
// We have to manually update the search index.
Episode::query()->whereIn('id', $ids)->searchable();
}
private function subscribeUserToPodcast(User $user, Podcast $podcast): void

View file

@ -149,13 +149,13 @@ export const songStore = {
getShareableUrl: (song: Playable) => `${window.BASE_URL}#/song/${song.id}`,
syncWithVault (playables: MaybeArray<Playable>) {
return arrayify(playables).map(song => {
let local = this.byId(song.id)
return arrayify(playables).map(playable => {
let local = this.byId(playable.id)
if (local) {
merge(local, song)
merge(local, playable)
} else {
local = reactive(song)
local = reactive(playable)
local.playback_state = 'Stopped'
this.watchPlayCount(local)
this.vault.set(local.id, local)