diff --git a/app/Models/Podcast.php b/app/Models/Podcast.php index f4a77663..a3372ee8 100644 --- a/app/Models/Podcast.php +++ b/app/Models/Podcast.php @@ -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 */ public function toSearchableArray(): array { diff --git a/app/Models/Song.php b/app/Models/Song.php index 4d62aa20..15ded62f 100644 --- a/app/Models/Song.php +++ b/app/Models/Song.php @@ -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 diff --git a/app/Services/PodcastService.php b/app/Services/PodcastService.php index 42f4914d..ca5c0bdc 100644 --- a/app/Services/PodcastService.php +++ b/app/Services/PodcastService.php @@ -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 diff --git a/resources/assets/js/stores/songStore.ts b/resources/assets/js/stores/songStore.ts index 550422b7..cf062b33 100644 --- a/resources/assets/js/stores/songStore.ts +++ b/resources/assets/js/stores/songStore.ts @@ -149,13 +149,13 @@ export const songStore = { getShareableUrl: (song: Playable) => `${window.BASE_URL}#/song/${song.id}`, syncWithVault (playables: MaybeArray) { - 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)