Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
339 lines (312 sloc) 12.2 KB
<?php
namespace App\Meltir;
use App\Entity\GoogleTokenStorage;
use App\Entity\YtCategories;
use App\Entity\YtChannels;
use App\Entity\YtVideos;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Google_Client;
use Google_Service_YouTube;
use Doctrine\ORM\EntityManagerInterface;
/**
* Class youtube
* Import channels and vidoes using the youtube API
* Provides helpers for login functions
*
* @package App\Meltir
*/
class youtube {
public function __construct(Stopwatch $stopwatch,
LoggerInterface $logger,
Google_Client $google,
SessionInterface $session,
RequestStack $request,
RouterInterface $router,
EntityManagerInterface $manager)
{
$this->logger = $logger;
$this->stopwatch = $stopwatch;
$this->google = $google;
$this->session = $session;
$this->router = $router;
$this->manager = $manager;
if ($request->getCurrentRequest()) $this->current_route = $request->getCurrentRequest()->get('_route');
$this->google->setApplicationName('my website');
$this->google->setAccessType('offline');
$this->google->setIncludeGrantedScopes(true);
$this->google->addScope([
'https://www.googleapis.com/auth/youtube.readonly'
]);
$this->google->setRedirectUri($this->router->generate('youtube_auth',[],UrlGeneratorInterface::ABSOLUTE_URL));
$this->google_session = $session->get('google',[]);
}
/**
* @var EntityManagerInterface
*/
private $manager;
/**
* Current route is used when we need to authenticate and go back to the calling page
* @var string
*/
private $current_route = '';
/**
* @var UrlGeneratorInterface
*/
private $router;
/**
* array of
* code - return auth code recieved directly from google
* token - token generated by google from auth code
* return_route - when authorising, return to this url (this is the url that called for authorization initially)
* @var array|mixed
*/
private $google_session = [];
/**
* @var SessionInterface
*/
private $session;
/**
* @var Stopwatch
*/
private $stopwatch;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var Google_Client|null
*/
private $google = null;
/**
* @var Google_Service_YouTube|null
*/
private $youtube = null;
/**
* Check if you need to log into google or can use an existing code/token
* Returns false if you do not need to log in and can reuse existing auth
* Returns a string, which is the url of google authorization
*
* @param string $auth_route
* @return bool|string
* @todo handle errors and exceptions when processing login
*/
public function login($auth_route) {
if ($this->youtube) return false;
if ($this->checkExistingToken()) {
$this->logger->info('Refreshed or reused token');
}
$this->logger->info('Google session info:'.print_r($this->google_session,true));
if (empty($this->google_session['code']) && empty($this->google_session['token']) ) {
$this->logger->info('Google - no code or token found');
$this->google->setRedirectUri($this->router->generate($auth_route,[],UrlGeneratorInterface::ABSOLUTE_URL));
$this->google_session['return_route'] = $this->current_route;
$this->session->set('google',$this->google_session);
return $this->google->createAuthUrl();
}
if (!empty($this->google_session['token'])) {
$this->logger->info('Google - token found, authorising');
$this->google->setAccessToken($this->google_session['token']);
$this->youtube = new Google_Service_YouTube($this->google);
}
if (!empty($this->google_session['code']) && empty($this->google_session['token'])) {
$this->logger->info('Google - code found, generating token');
$this->google_session['token'] = $this->google->fetchAccessTokenWithAuthCode($this->google_session['code']);
$this->logger->info('Google - generated token'.print_r($this->google_session['token'],true));
$this->google->setAccessToken($this->google_session['token']);
$this->youtube = new Google_Service_YouTube($this->google);
$this->session->set('google',$this->google_session);
$this->storeToken($this->google_session['token']);
}
return false;
}
/**
* Put the token from google into the db
* @param $token
*/
private function storeToken($token) {
$db_token = new GoogleTokenStorage();
$db_token->setToken($token);
$db_token->setScope($token['scope']);
$this->manager->persist($db_token);
$this->manager->flush();
}
/**
* Try to fetch a token from the database, refresh it if it has expired
*
* @return bool true if token found, false if token failed to be generated
*/
private function checkExistingToken() {
if (!empty($this->google_session['token'])) return true;
$token_repo = $this->manager->getRepository(GoogleTokenStorage::class);
$last_token = $token_repo->getLastToken();
if (!$last_token) {
$this->logger->info('Have not found an existing token');
return false;
}
$this->logger->info('Found token of '.print_r($last_token->getToken(),true));
try {
$this->google->setAccessToken($last_token->getToken());
$this->youtube = new Google_Service_YouTube($this->google);
$this->google_session['token'] = $last_token->getToken();
$this->session->set('google',$this->google_session);
return true;
} catch (\Exception $e) {
try {
$refresh_token = $last_token->getToken()['refresh_token'];
$this->logger->info('Trying to refresh token with '.$refresh_token);
$token = $this->google->refreshToken($refresh_token);
$this->google_session['token'] = $token;
$this->google->setAccessToken($token);
$this->youtube = new Google_Service_YouTube($this->google);
$this->storeToken($token);
return true;
} catch (\Exception $e) {
$this->logger->error('Failed to refresh token with'.$e->getMessage());
}
}
return false;
}
/**
* Set the auth code in the session
* @param string $code
*/
public function setAuthCode($code) {
$this->logger->info('Google code set to:'.$code);
$this->google_session['code'] = $code;
$this->google_session['token'] = null;
$this->session->set('google',$this->google_session);
}
/**
* Return the route of the calling component
* @return string
*/
public function getReturnRoute() {
return $this->google_session['return_route'];
}
/**
* Read a list of subscribed channels and store them in the db,
* does not duplicate existing channels in the database
* if $nextPageToken is supplied, use this to get the particular page of the replies
* if not supplied fetches the first page of subscribed channels
* Returns null if this is the last (or only) page
*
* @param null|string $nextPageToken
* @return string|null
*/
public function updateChannelListFromYoutube($nextPageToken = null) {
$queryParams = [
'mine' => 'true',
'maxResults' => 50
];
if ($nextPageToken) {
$queryParams['pageToken'] = $nextPageToken;
}
$this->stopwatch->start('Google','listSubscriptions');
$response = $this->
youtube->
subscriptions->
listSubscriptions('snippet',$queryParams);
$this->stopwatch->stop('Google','listSubscriptions');
foreach ($response->getItems() as $item) {
$this->insertChannel($item->snippet);
}
$this->manager->flush();
$nextPageToken = $response->getNextPageToken();
return $nextPageToken;
}
/**
* Insert channel into db if does not already exists from youtube results snippet
* Fetches additional channel info (uploads playlist from youtube api)
*
* @param $snippet
* @return bool false if not inserted, true if inserted
*/
private function insertChannel($snippet) {
$chan_id = $snippet->resourceId->channelId;
$repository = $this->manager->getRepository(YtChannels::class);
$old_channel = $repository->findOneBy(['chan_id'=>$chan_id]);
if ($old_channel) return false;
$this->stopwatch->start('Google');
$contentDetails = $this->
youtube->
channels->
listChannels('contentDetails',['id'=>$chan_id]);
$this->stopwatch->stop('Google');
$upload_playlist = $contentDetails->
getItems()[0]->
contentDetails->
relatedPlaylists->
uploads;
$noneCat = $this->
manager->
getRepository(YtCategories::class)->
findOneBy(['slug'=>'todo']);
$channel = new YtChannels();
$channel->setCategory($noneCat);
$channel->setThumb($snippet->thumbnails->high->url);
$channel->setChanName($snippet->title);
$channel->setChanId($chan_id);
$channel->setUploadPlaylist($upload_playlist);
$channel->setChanDescription($snippet->description);
$this->manager->persist($channel);
return true;
}
/**
* Fetch latest videos for active youtube channels, starting from $page
*
* @param int $page
* @return bool true if there are more batches to process, false if this is the last batch to process
*/
public function addLatestVideos(int $page = 1) {
$channels_repo = $this->manager->getRepository(YtChannels::class);
$per_page = 10;
if ($page == 1) $startRow = 0;
else $startRow = ($page-1)*$per_page;
$channels = $channels_repo->findActiveChannels($startRow,$per_page);
foreach ($channels as $channel) {
$this->addLatestVideosForChannel($channel);
}
if (count($channels) < $per_page) return false;
return true;
}
/**
* Add videos to specified channel from youtube API
* Does not create duplicate videos
*
* @param YtChannels $channel
* @throws \Exception
*/
public function addLatestVideosForChannel(YtChannels $channel) {
$this->stopwatch->start('Google');
$playlist = $this->
youtube->
playlistItems->
listPlaylistItems('snippet',[
'playlistId'=>$channel->getUploadPlaylist(),
'maxResults'=>30
]);
$this->stopwatch->stop('Google');
$video_repository = $this->manager->getRepository(YtVideos::class);
foreach ($playlist->getItems() as $item) {
if (!$video_repository->findBy(['videoid'=>$item->snippet->resourceId->videoId])) {
$video = new YtVideos();
$img_url = $item->snippet->thumbnails->high->url;
if ($item->snippet->thumbnails->standard) $img_url = $item->snippet->thumbnails->standard->url;
$video->setThumb($img_url);
$video->setChannel($channel);
$video->setTitle($item->snippet->title);
$video->setVideoid($item->snippet->resourceId->videoId);
$video->setActive(true);
$video->setDatePublished(new \DateTime($item->snippet->publishedAt));
$video->setLiked(false);
$this->manager->persist($video);
}
}
$this->manager->flush();
}
}
You can’t perform that action at this time.