Skip to content

Commit

Permalink
feat: add YouTube service api
Browse files Browse the repository at this point in the history
  • Loading branch information
abourtnik committed Dec 14, 2023
1 parent c948a4b commit d029419
Show file tree
Hide file tree
Showing 14 changed files with 289 additions and 20 deletions.
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
<h1 align="center">
<a href="https://www.clip-zone.com">
<img src="https://www.clip-zone.com/images/logo.png" alt="ClipZone logo" height="200">
</a>
<br>
ClipZone
<br>
</h1>

<h3 align="center">Open Source Youtube Clone</h3>
ClipZone
=====

<p align="center">
<a href="https://github.com/abourtnik/clip-zoneactions">
<img src="https://github.com/abourtnik/clip-zone/actions/workflows/CI-CD.yml/badge.svg" alt="Build Status">
</a>
<a href="https://www.paypal.com/donate/?hosted_button_id=P4KH8VMKM6XMJ">
<img src="https://img.shields.io/badge/Donate-blue?logo=paypal" alt="Donate Paypal">
</a>

Open Source Youtube Clone

<p align="center">
<img src="https://www.clip-zone.com/images/logo.png" alt="ClipZone logo" height="200">
</p>

## Technical detail
Expand Down
3 changes: 2 additions & 1 deletion app/Console/Commands/CreateUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public function handle() : int
'avatar' => null,
'show_subscribers' => true,
'country' => null,
'description' => null
'description' => null,
'website' => null
]);

return Command::SUCCESS;
Expand Down
131 changes: 131 additions & 0 deletions app/Console/Commands/SyncComments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

namespace App\Console\Commands;

use App\Models\Comment;
use App\Models\User;
use App\Models\Video;
use App\Services\YoutubeService;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;

class SyncComments extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'comments:sync {id : ClipZone video id} {youtubeId : Youtube video id}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Get comments from Youtube for specific video';

/**
* Execute the console command.
*
* @param YoutubeService $youtubeService
* @return int
*/
public function handle(YoutubeService $youtubeService) : int
{
list('id' => $id, 'youtubeId' => $youtubeId) = $this->arguments();

$video = Video::findOrFail($id);

$video->comments->each->delete();

$data = $youtubeService->getComments($youtubeId);

$this->saveComments($data['items'], $video, null);

return Command::SUCCESS;
}

private function saveComments (array $items, Video $video, ?Comment $parent) {

$usedUsers = [];

$count = count($items);

$randomCount = rand($count - 5, $count);

foreach ($items as $item) {

$comment = $item['snippet']['topLevelComment']['snippet'] ?? $item['snippet'];

$date = Carbon::create($comment['publishedAt']);

$users = $this->getUsers([$video->user_id], $date)->diff($usedUsers);

$userId = $users->random();

$usedUsers[] = $userId;

$savedComment = Comment::withoutEvents(function () use ($video, $comment, $userId, $date, $parent) {
$data = [
'content' => $comment['textOriginal'],
'ip' => '37.67.157.29',
'user_id' => $userId,
'created_at' => $date,
'updated_at' => Carbon::create($comment['updatedAt']),
];

if ($parent) {
$data['parent_id'] = $parent->id;
}

return $video->comments()->create($data);
});

// Add Comment interaction
$this->generateInteraction($savedComment, $randomCount, $date);
$randomCount = rand($randomCount - 5, $randomCount - 1);

$replies = $item['replies']['comments'] ?? [];

if($replies) {
$this->saveComments($replies, $video, $savedComment);
}
}

}

private function getUsers (array $excludeIds, Carbon $createdBefore): Collection {

return User::query()
->active()
->when($excludeIds, fn($q) => $q->whereNotIn('id', $excludeIds))
->where('created_at', '<', $createdBefore)
->whereNotIn('id', [6, 7, 9, 10, 12, 14, 15, 16, 19, 20, 21, 23])
->inRandomOrder()
//->limit(5)
->get()
->pluck('id');
}

private function generateInteraction(Comment $comment, int $count, Carbon $afterDate) {

$usedUsers = [];

for ($i = $count; $i > 0; $i--) {

$users = $this->getUsers([], $afterDate)->diff($usedUsers);

$userId = $users->random();

$usedUsers[] = $userId;

$comment->interactions()->create([
'user_id' => $userId,
'status' => fake()->boolean(93),
'perform_at' => fake()->dateTimeBetween($afterDate)
]);
}
}
}
93 changes: 93 additions & 0 deletions app/Console/Commands/SyncVideosInteractions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace App\Console\Commands;

use App\Models\Comment;
use App\Models\User;
use App\Models\Video;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;

class SyncVideosInteractions extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'interactions:sync {id : ClipZone video id} {count : Interaction number}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate interactions for specific video';

/**
* Execute the console command.
*
* @return int
*/
public function handle() : int
{
list('id' => $id, 'count' => $count) = $this->arguments();

$video = Video::findOrFail($id);

$video->interactions()->delete();

$usedUsers = [];

for ($i = $count; $i > 0; $i--) {

$users = $this->getUsers([], $video->publication_date)->diff($usedUsers);

$userId = $users->random();

$usedUsers[] = $userId;

$video->interactions()->create([
'user_id' => $userId,
'status' => fake()->boolean(93),
'perform_at' => fake()->dateTimeBetween($video->publication_date)
]);
}

return Command::SUCCESS;
}

private function getUsers (array $excludeIds, Carbon $createdBefore): Collection {

return User::query()
->active()
->when($excludeIds, fn($q) => $q->whereNotIn('id', $excludeIds))
->where('created_at', '<', $createdBefore)
->whereNotIn('id', [6, 7, 9, 10, 12, 14, 15, 16, 19, 20, 21, 23])
->inRandomOrder()
//->limit(5)
->get()
->pluck('id');
}

private function generateInteraction(Comment $comment, int $count, Carbon $afterDate) {

$usedUsers = [];

for ($i = $count; $i > 0; $i--) {

$users = $this->getUsers([], $afterDate)->diff($usedUsers);

$userId = $users->random();

$usedUsers[] = $userId;

$comment->interactions()->create([
'user_id' => $userId,
'status' => fake()->boolean(93),
'perform_at' => fake()->dateTimeBetween($afterDate)
]);
}
}
}
1 change: 1 addition & 0 deletions app/Http/Controllers/CommentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public function list(Request $request) : ResourceCollection {
'likes as liked_by_auth_user' => fn($q) => $q->where('user_id', Auth::id()),
'dislikes as disliked_by_auth_user' => fn($q) => $q->where('user_id', Auth::id()),
])
->orderByRaw('likes_count - dislikes_count DESC')
->latest();
},
'reportByAuthUser'
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function show (User $user) : View {
->limit(8)
])
->active()
->latest('created_at')
->latest('updated_at')
->limit(6);
},
'pinned_video' => fn($q) => $q->withCount('views'),
Expand Down
3 changes: 3 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Enums\ReportReason;
use App\Http\Resources\NotificationResource;
use App\Models\Category;
use App\Services\YoutubeService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
Expand All @@ -30,6 +31,8 @@ class AppServiceProvider extends ServiceProvider
public function register() : void
{
Cashier::ignoreMigrations();

$this->app->singleton(YoutubeService::class, fn() => new YoutubeService(config('services.youtube.api_key')));
}

/**
Expand Down
30 changes: 30 additions & 0 deletions app/Services/YoutubeService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;

class YoutubeService
{
const API_ENDPOINT = 'https://www.googleapis.com/youtube/v3';

private string $apiKey;

public function __construct(string $apiKey)
{
$this->apiKey = $apiKey;
}

public function getComments(string $videoID) : array|null
{
$response = Http::get(self::API_ENDPOINT. '/commentThreads', [
'key' => $this->apiKey,
'part' => 'snippet,replies',
'videoId' => $videoID,
'order' => 'relevance',
'maxResults' => 20
]);

return $response->json();
}
}
3 changes: 3 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
'client_id' => env('FACEBOOK_CLIENT_ID'),
'client_secret' => env('FACEBOOK_CLIENT_SECRET'),
'redirect' => env('APP_URL') . '/oauth/callback/facebook',
],
'youtube' => [
'api_key' => env('YOUTUBE_API_KEY'),
]
];
2 changes: 1 addition & 1 deletion database/factories/UserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class UserFactory extends Factory
*/
public function definition() : array
{
$date = fake()->dateTimeBetween('-1 year');
$date = fake()->dateTimeBetween('-14 years');

return [
'username' => fake()->unique()->userName(),
Expand Down
2 changes: 1 addition & 1 deletion resources/views/components/layout.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<meta property="og:url" content="{{url()->full()}}" />
<meta property="og:title" content="@yield('title') - {{config('app.name')}}" />
<meta property="og:description" content="@yield('description')" />
<meta property="og:image" content="{{asset('images/logo.png')}}" />
<meta property="og:image" content="@yield('image', asset('images/logo.png'))" />
<meta property="og:language" content="{{ str_replace('_', '-', app()->getLocale()) }}" />

<link rel="shortcut icon" href="{{asset('favicon.ico')}}">
Expand Down
14 changes: 10 additions & 4 deletions resources/views/layouts/menus/sidebars/front.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,16 @@
<small>Copyright © {{now()->format('Y')}} {{config('app.name')}}</small>
<small>All Rights Reserved</small>
</div>
<a class="btn bg-dark text-white btn-sm mt-2" href="https://github.com/abourtnik/clip-zone" target="_blank">
<i class="fa-brands fa-github mr-2"></i>
<span>Github</span>
</a>
<div class="d-flex align-items-center gap-2 mt-2">
<a class="btn bg-dark text-white btn-sm mt-2" href="https://github.com/abourtnik/clip-zone" target="_blank">
<i class="fa-brands fa-github mr-2"></i>
<span>Github</span>
</a>
<a class="btn text-white btn-sm mt-2" href="https://www.paypal.com/donate/?hosted_button_id=P4KH8VMKM6XMJ" target="_blank" style="background-color: #0079C1">
<i class="fa-brands fa-paypal mr-2"></i>
<span>Donate</span>
</a>
</div>
<a class="text-sm fw-bold text-decoration-none d-block mt-2" href="https://antonbourtnik.fr" target="_blank">Anton Bourtnik</a>
</div>
</nav>
Expand Down
3 changes: 2 additions & 1 deletion resources/views/users/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

@section('title', $user->username)
@section('description', Str::limit($user->description, 155))
@section('image', asset($user->avatar_url))

@section('class', 'px-0')
@section('style', 'margin-top: 0 !important')
Expand Down Expand Up @@ -183,7 +184,7 @@ class="link-primary bg-transparent text-decoration-none d-flex align-items-cente
@endif
</div>
<div class="tab-pane" id="playlists" role="tabpanel" aria-labelledby="playlists-tab">
@if($user->playlists->count())
@if($user->playlists_count)
<div class="row gx-3 gy-4 mt-0">
@each('playlists.card', $user->playlists, 'playlist')
</div>
Expand Down
Loading

0 comments on commit d029419

Please sign in to comment.