Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

christophrumpel/larastreamers

238 stars
3 code files
View christophrumpel/larastreamers on GitHub

app/Services/YoutubeClient.php

Open in GitHub
use App\Services\Youtube\ChannelData;
use App\Services\Youtube\StreamData;
use App\Services\Youtube\YoutubeException;
use Carbon\Carbon;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
 
class YoutubeClient
{
public function channel(string $id): ChannelData
{
$result = $this->fetch('channels', ['id' => $id, 'part' => 'snippet'], 'items.0');
 
if (empty($result)) {
throw YoutubeException::unknownChannel($id);
}
 
return new ChannelData(
platformId: data_get($result, 'id'),
slug: data_get($result, 'snippet.customUrl'),
name: data_get($result, 'snippet.title'),
description: data_get($result, 'snippet.description'),
onPlatformSince: $this->toCarbon(data_get($result, 'snippet.publishedAt')),
thumbnailUrl: last(data_get($result, 'snippet.thumbnails'))['url'] ?? null,
country: data_get($result, 'snippet.country'),
);
}
 
public function upcomingStreams(string $channelId): Collection
{
$videoIds = $this->fetch('search', [
'channelId' => $channelId,
'eventType' => 'upcoming',
'type' => 'video',
'part' => 'id',
'maxResults' => 25,
], 'items.*.id.videoId');
 
if (empty($videoIds)) {
return collect();
}
 
return $this->videos($videoIds);
}
 
public function video(string $id): StreamData
{
return $this->videos($id)
->whenEmpty(fn() => throw YoutubeException::unknownVideo($id))
->first();
}
 
public function videos(iterable|string $videoIds): Collection
{
return collect($this->fetch('videos', [
'id' => is_string($videoIds) ? $videoIds : collect($videoIds)->implode(','),
'part' => 'snippet,statistics,liveStreamingDetails',
], 'items'))
->map(fn(array $item) => new StreamData(
videoId: data_get($item, 'id'),
title: data_get($item, 'snippet.title'),
channelTitle: data_get($item, 'snippet.channelTitle'),
description: data_get($item, 'snippet.description'),
thumbnailUrl: last(data_get($item, 'snippet.thumbnails'))['url'] ?? null,
publishedAt: $this->toCarbon(data_get($item, 'snippet.publishedAt')),
plannedStart: $this->getPlannedStart($item),
status: data_get($item, 'snippet.liveBroadcastContent'),
));
}
 
protected function getPlannedStart(array $data): Carbon
{
return $this->toCarbon(data_get($data, 'liveStreamingDetails.scheduledStartTime'))
?? $this->toCarbon(data_get($data, 'snippet.publishedAt'));
}
 
protected function fetch(string $url, array $params = [], string $key = null): array
{
return Http::asJson()
->baseUrl('https://youtube.googleapis.com/youtube/v3/')
->get($url, array_merge($params, [
'key' => config('services.youtube.key'),
]))
->onError(fn(Response $response) => throw YoutubeException::general($response->status()))
->json($key, []);
}
 
protected function toCarbon(?string $string): ?Carbon
{
if (empty($string)) {
return null;
}
 
return Carbon::parse($string);
}
}

app/Http/Livewire/ImportYoutubeChannel.php

Open in GitHub
use App\Facades\Youtube;
use App\Services\Youtube\YoutubeException;
use Livewire\Component;
 
class ImportYoutubeChannel extends Component
{
public $youtubeChannelId;
 
public function importChannel(): void
{
try {
$channelData = Youtube::channel($this->youtubeChannelId);
} catch (YoutubeException $exception) {
$this->addError('channel', $exception->getMessage());
 
return;
}
//
}

app/Jobs/ImportYoutubeChannelStreamsJob.php

Open in GitHub
use App\Facades\Youtube;
use App\Models\Stream;
use App\Services\Youtube\StreamData;
 
class ImportYoutubeChannelStreamsJob
{
public function __construct(public string $youtubeChannelId, public string $languageCode = 'en')
{}
 
public function handle(): void
{
$streams = Youtube::upcomingStreams($this->youtubeChannelId);
 
$streams->map(function(StreamData $streamData) {
Stream::updateOrCreate(['youtube_id' => $streamData->videoId], [
'youtube_id' => $streamData->videoId,
'title' => $streamData->title,
'description' => $streamData->description,
'channel_title' => $streamData->channelTitle,
'thumbnail_url' => $streamData->thumbnailUrl,
'scheduled_start_time' => $streamData->plannedStart,
'language_code' => $this->languageCode,
'status' => $streamData->status,
]);
});
}
}

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.