├── .editorconfig ├── .styleci.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── composer.json └── src ├── Auth └── AuthService.php ├── AuthenticateService.php ├── ChannelService.php ├── LiveStreamService.php ├── VideoService.php ├── YoutubeLaravelApiServiceProvider.php └── config └── google-config.php /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_size = 4 9 | indent_style = space 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mukesh201722@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/alchemyguy/YoutubeLaravelApi). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - Check the code style with ``$ composer check-style`` and fix it with ``$ composer fix-style``. 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | **Happy coding**! 26 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Detailed description 4 | 5 | Provide a detailed description of the change or addition you are proposing. 6 | 7 | Make it clear if the issue is a bug, an enhancement or just a question. 8 | 9 | ## Context 10 | 11 | Why is this change important to you? How would you use it? 12 | 13 | How can it benefit other users? 14 | 15 | ## Possible implementation 16 | 17 | Not obligatory, but suggest an idea for implementing addition or change. 18 | 19 | ## Your environment 20 | 21 | Include as many relevant details about the environment you experienced the bug in and how to reproduce it. 22 | 23 | * Version used (e.g. PHP 5.6, HHVM 3): 24 | * Operating system and version (e.g. Ubuntu 16.04, Windows 7): 25 | * Link to your project: 26 | * ... 27 | * ... 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mukesh Chandra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | The function **subscriptionByChannelId** didn't work properly. It was getting always the first 50 results and the user could not get more or less. The parameter 'maxResults' didn't do anything. 6 | 7 | I reactivated the function **parseSubscriptions** and fixed it to get the desired amount of results instead of all of them. 8 | 9 | To avoid confuision, the parameters is passed now as **totalResults** instead of **maxResults** since the last is the Google Api's parameter for each page, which can only be set up to 50 10 | 11 | The Read Me was updated 12 | 13 | ## Motivation and context 14 | 15 | User was not able to get results off the first page 16 | 17 | ## How has this been tested? 18 | 19 | I listed my own channel subscriptions 20 | 21 | ## Screenshots (if appropriate) 22 | 23 | ## Types of changes 24 | 25 | - [ ] Bug fix (non-breaking change which fixes an issue) 26 | - [X] New feature (non-breaking change which adds functionality) 27 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 28 | 29 | ## Checklist: 30 | 31 | - [X] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document. 32 | - [X] My pull request addresses exactly one patch/feature. 33 | - [X] I have created a branch for this patch/feature. 34 | - [X] Each individual commit in the pull request is meaningful. 35 | - [X] I have added tests to cover my changes. 36 | - [X] If my change requires a change to the documentation, I have updated it accordingly. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Youtube Laravel Api 2 | `PHP (Laravel) Package for Google / YouTube API V3 with Google Auth` 3 | 4 | ## Features 5 | 6 | - [x] [Google Auth](#google-auth) 7 | - [x] [Full Live Streaming API for Youtube](#full-live-streaming-api) 8 | - [x] [Full Youtube Channel API](#full-youtube-channel-api) 9 | - [x] [Full Youtube Video API](#full-youtube-video-api) 10 | 11 | 12 | ## Installation 13 | 14 | ```shell 15 | composer require alchemyguy/youtube-laravel-api 16 | ``` 17 | 18 | Add Service provider to config/app.php provider's array: 19 | ```php 20 | alchemyguy\YoutubeLaravelApi\YoutubeLaravelApiServiceProvider::class 21 | ``` 22 | 23 | Execute the following command to get the configurations: 24 | ```shell 25 | php artisan vendor:publish --tag='youtube-config' 26 | ``` 27 | 28 | ## Steps to create your google oauth credentials: 29 | 30 | 1. Goto `https://console.developers.google.com` 31 | 2. Login with your credentials & then create a new project. 32 | 3. Enable the following features while creating key 33 | - Youtube Data API 34 | - Youtube Analytics API 35 | - Youtube Reporting API 36 | 4. Then create `API key` from credentials tab. 37 | 5. Then in OAuth Consent Screen enter the `product name`(your site name). 38 | 6. create credentials > select OAuth Client ID. (here you will get client_id and client_secret) 39 | 7. Then in the Authorized Javascript Origins section add `you site url`. 40 | 8. In the Authorized Redirect URLs section add `add a url which you want the auth code to return`(login callback) 41 | 9. You will get values (to be exact - client_id, client_secret & api_key) 42 | 10. Then add these values - client_id, client_secret, api_key and redirect_url in the env file and you can start using the package now. 43 | 44 | 45 | ## Usage : 46 | 47 | ### Google Auth 48 | 49 | - **Add Code to call the api class** 50 | 51 | ```php 52 | 53 | getLoginUrl('email','identifier'); 67 | 68 | ``` 69 | 70 | - **Fetching the Auth Code and Identifier** 71 | Now once the user authorizes by visiting the url, the authcode will be redirected to the redirect_url specified in .env with params as code( this will be auth code) and state (this will be identifier we added during making the loginUrl) 72 | 73 | ```php 74 | $code = Input::get('code'); 75 | $identifier = Input::get('state'); 76 | 77 | ``` 78 | 79 | - **Auth-Token and Details For Channel** 80 | 81 | ```php 82 | $authObject = new AuthenticateService; 83 | $authResponse = $authObject->authChannelWithCode($code); 84 | ``` 85 | 86 | - **This will return an array**: 87 | ``` 88 | $authResponse['token'] (Channel Token) 89 | $authResponse['channel_details'] 90 | $authResponse['live_streaming_status'] (enabled or disabled) 91 | ``` 92 | 93 | ### Full Live Streaming API 94 | 95 | - **Add Code to call the api class** 96 | 97 | ```php 98 | "", 110 | "description" => "", 111 | "thumbnail_path" => "", // Optional 112 | "event_start_date_time" => "", 113 | "event_end_date_time" => "", // Optional 114 | "time_zone" => "", 115 | 'privacy_status' => "", // default: "public" OR "private" 116 | "language_name" => "", // default: "English" 117 | "tag_array" => "" // Optional and should not be more than 500 characters 118 | ); 119 | 120 | $ytEventObj = new LiveStreamService(); 121 | /** 122 | * The broadcast function returns array of details from YouTube. 123 | * Store this information & will be required to supply to youtube 124 | * for live streaming using encoder of your choice. 125 | */ 126 | $response = $ytEventObj->broadcast($authToken, $data); 127 | if ( !empty($response) ) { 128 | 129 | $youtubeEventId = $response['broadcast_response']['id']; 130 | $serverUrl = $response['stream_response']['cdn']->ingestionInfo->ingestionAddress; 131 | $serverKey = $response['stream_response']['cdn']->ingestionInfo->streamName; 132 | } 133 | 134 | ``` 135 | 136 | - **Updating a Youtube Event** 137 | 138 | ```php 139 | $ytEventObj = new LiveStreamService(); 140 | /** 141 | * The updateBroadcast response give details of the youtube_event_id,server_url and server_key. 142 | * The server_url & server_key gets updated in the process. (save the updated server_key and server_url). 143 | */ 144 | $response = $ytEventObj->updateBroadcast($authToken, $data, $youtubeEventId); 145 | 146 | // $youtubeEventId = $response['broadcast_response']['id']; 147 | // $serverUrl = $response['stream_response']['cdn']->ingestionInfo->ingestionAddress; 148 | // $serverKey = $response['stream_response']['cdn']->ingestionInfo->streamName 149 | ``` 150 | 151 | - **Deleting a Youtube Event** 152 | 153 | ```php 154 | $ytEventObj = new LiveStreamService(); 155 | 156 | # Deleting the event requires authentication token for the channel in which the event is created and the youtube_event_id 157 | $ytEventObj->deleteEvent($authToken, $youtubeEventId); 158 | ``` 159 | 160 | - Starting a Youtube Event Stream: 161 | 162 | ```php 163 | $ytEventObj = new LiveStreamService(); 164 | /** 165 | * $broadcastStatus - ["testing", "live"] 166 | * Starting the event takes place in 3 steps 167 | * 1. Start sending the stream to the server_url via server_key recieved as a response in creating the event via the encoder of your choice. 168 | * 2. Once stream sending has started, stream test should be done by passing $broadcastStatus="testing" & it will return response for stream status. 169 | * 3. If transitioEvent() returns successfull for testing broadcast status, then start live streaming your video by passing $broadcastStatus="live" 170 | * & in response it will return us the stream status. 171 | */ 172 | $streamStatus = $ytEventObj->transitionEvent($authToken, $youtubeEventId, $broadcastStatus); 173 | ``` 174 | 175 | - **Stopping a Youtube Event Stream** 176 | 177 | ```php 178 | $ytEventObj = new LiveStreamService(); 179 | /** 180 | * $broadcastStatus - ["complete"] 181 | * Once live streaming gets started succesfully. We can stop the streaming the video by passing broadcastStatus="complete" and in response it will give us the stream status. 182 | */ 183 | $ytEventObj->transitionEvent($authToken, $youtubeEventId, $broadcastStatus); // $broadcastStatus = ["complete"] 184 | ``` 185 | 186 | 187 | ### Full Youtube Channel API 188 | 189 | - **Add Code to call the api class** 190 | 191 | ```php 192 | 'GoogleDevelopers')] 207 | */ 208 | 209 | $part = 'id,snippet'; 210 | $params = array('id'=> 'channel_1_id,channel_2_id'); 211 | $channelServiceObject = new ChannelService; 212 | $channelDetails = $channelServiceObject->channelsListById($part, $params); 213 | 214 | ``` 215 | 216 | - **Channel Detail by Token** 217 | Channel Details of the users channel which has authorized token 218 | 219 | ```php 220 | $channelServiceObject = new ChannelService; 221 | $channelDetails = $channelServiceObject->getChannelDetails($authToken); 222 | ``` 223 | 224 | - **Channel Subscription List** 225 | List of subscriptions of the channel 226 | 227 | ```php 228 | 229 | /* 230 | * $params array('channelId'=>'', 'totalResults'= 10) 231 | * totalResults is different of maxResults from Google Api. 232 | * totalResults = the amount of results you want 233 | * maxResults = max of results PER PAGE. We don't need this parameter here since it will loop until it gets all the results you want. 234 | */ 235 | $channelServiceObject = new ChannelService; 236 | $channelDetails = $channelServiceObject->subscriptionByChannelId($params); 237 | ``` 238 | 239 | - **Add Subscriptions For Authorized Channel** 240 | 241 | ```php 242 | /* 243 | * properties array('snippet.resourceId.kind' => 'youtube#channel','snippet.resourceId.channelId' => 'UCqIOaYtQak4-FD2-yI7hFkw') 244 | */ 245 | $channelServiceObject = new ChannelService; 246 | $response = $channelServiceObject->addSubscriptions($properties, $token, $part='snippet', $params=[]); 247 | 248 | ``` 249 | 250 | -**Remove Subscriptions For Authorized Channel** 251 | To remove subscription we need subscription id which can be found from subscription list. 252 | ```php 253 | $response = $channelServiceObject->removeSubscription( $token, $subscriptionId); 254 | 255 | ``` 256 | 257 | - **Update Channel Branding Settings** 258 | Updates the channel details and preferences. 259 | 260 | ```php 261 | /* 262 | * $properties array('id' => '', 263 | * 'brandingSettings.channel.description' => '', 264 | * 'brandingSettings.channel.keywords' => '', 265 | * 'brandingSettings.channel.defaultLanguage' => '', 266 | * 'brandingSettings.channel.defaultTab' => '', 267 | * 'brandingSettings.channel.moderateComments' => '', 268 | * 'brandingSettings.channel.showRelatedChannels' => '', 269 | * 'brandingSettings.channel.showBrowseView' => '', 270 | * 'brandingSettings.channel.featuredChannelsTitle' => '', 271 | * 'brandingSettings.channel.featuredChannelsUrls[]' => '', 272 | * 'brandingSettings.channel.unsubscribedTrailer' => '') 273 | */ 274 | 275 | $channelServiceObject = new ChannelService; 276 | $response = $channelServiceObject->updateChannelBrandingSettings($googleToken, $properties); 277 | ``` 278 | 279 | ### Full Youtube Video API 280 | 281 | - **Add Code to call the api class** 282 | 283 | ```php 284 | 'xyzgh'); 295 | $videoServiceObject = new VideoService; 296 | $response = $videoServiceObject->videosListById($part, $params); 297 | ``` 298 | 299 | - **Upload Video To Your Channel** 300 | ```php 301 | 302 | /* 303 | * $videoPath path to the video 304 | * $data array('title'=>"", 305 | * 'description'=>"", 306 | * 'tags'=>"", 307 | * 'category_id'=>"", 308 | * 'video_status'=>"") 309 | */ 310 | 311 | $videoServiceObject = new VideoService; 312 | $response = $videoServiceObject->uploadVideo($googleToken, $videoPath, $data); 313 | ``` 314 | 315 | - **Delete Video To Your Channel** 316 | ```php 317 | $videoServiceObject = new VideoService; 318 | $response = $videoServiceObject->deleteVideo($googleToken, $videoId); 319 | ``` 320 | 321 | 322 | - **Rate Video** 323 | Adding a like, dislike or removing the response from video 324 | 325 | ```php 326 | # rating 'like' or 'dislike' or 'none' 327 | $videoServiceObject = new VideoService; 328 | $response = $videoServiceObject->videosRate($googleToken, $videoId, $rating); 329 | 330 | ``` 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alchemyguy/youtube-laravel-api", 3 | "description": "It is a wrapper over Youtube Api v3 which simplifies functionalities the laravel way.", 4 | "keywords": ["alchemyguy", "YoutubeLaravelApi", "Youtube LiveStream", "php", "Youtube Backup", "Youtube Video", "Youtube Api V3", "laravel", "Oauth", "google api", "Youtube Channel"], 5 | "homepage": "https://github.com/alchemyguy/YoutubeLaravelApi", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Mukesh Chandra", 10 | "email": "mukesh201722@gmail.com", 11 | "role": "Developer" 12 | } 13 | ], 14 | "require": { 15 | "google/apiclient": "^2.0" 16 | }, 17 | "require-dev": { 18 | "squizlabs/php_codesniffer": "^2.3" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "alchemyguy\\YoutubeLaravelApi\\": "src" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "alchemyguy\\YoutubeLaravelApi\\": "tests" 28 | } 29 | }, 30 | "scripts": { 31 | "check-style": 32 | "phpcs -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests", 33 | "fix-style": 34 | "phpcbf -p --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 src tests" 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.0-dev" 39 | }, 40 | "laravel": { 41 | "providers": [ 42 | "alchemyguy\\YoutubeLaravelApi\\YoutubeLaravelApiServiceProvider" 43 | ] 44 | } 45 | }, 46 | "config": { 47 | "sort-packages": true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Auth/AuthService.php: -------------------------------------------------------------------------------- 1 | client = new \Google_Client; 16 | 17 | $this->client->setClientId(\Config::get('google-config.client_id')); 18 | $this->client->setClientSecret(\Config::get('google-config.client_secret')); 19 | $this->client->setDeveloperKey(\Config::get('google-config.api_key')); 20 | $this->client->setRedirectUri(\Config::get('google-config.redirect_url')); 21 | 22 | $this->client->setScopes([ 23 | 'https://www.googleapis.com/auth/youtube', 24 | ]); 25 | 26 | $this->client->setAccessType('offline'); 27 | $this->client->setPrompt('consent'); 28 | $this->ytLanguage = \Config::get('google.yt_language'); 29 | 30 | } 31 | 32 | /** 33 | * [getToken -generate token from response code recived on visiting the login url generated] 34 | * @param [type] $code [code for auth] 35 | * @return [type] [authorization token] 36 | */ 37 | public function getToken($code) { 38 | try { 39 | 40 | $this->client->authenticate($code); 41 | $token = $this->client->getAccessToken(); 42 | return $token; 43 | 44 | } catch (\Google_Service_Exception $e) { 45 | 46 | throw new Exception($e->getMessage(), 1); 47 | 48 | } catch (\Google_Exception $e) { 49 | 50 | throw new Exception($e->getMessage(), 1); 51 | 52 | } catch (Exception $e) { 53 | 54 | throw new Exception($e->getMessage(), 1); 55 | 56 | } 57 | } 58 | 59 | /** 60 | * [getLoginUrl - generates the url login url to generate auth token] 61 | * @param [type] $youtube_email [account to be authenticated] 62 | * @param [type] $channelId [return identifier] 63 | * @return [type] [auth url to generate] 64 | */ 65 | public function getLoginUrl($youtube_email, $channelId = null) { 66 | try 67 | { 68 | if (!empty($channelId)) { 69 | $this->client->setState($channelId); 70 | } 71 | 72 | $this->client->setLoginHint($youtube_email); 73 | $authUrl = $this->client->createAuthUrl(); 74 | return $authUrl; 75 | 76 | } catch (\Google_Service_Exception $e) { 77 | 78 | throw new Exception($e->getMessage(), 1); 79 | 80 | } catch (\Google_Exception $e) { 81 | 82 | throw new Exception($e->getMessage(), 1); 83 | 84 | } catch (Exception $e) { 85 | 86 | throw new Exception($e->getMessage(), 1); 87 | } 88 | 89 | } 90 | 91 | /** 92 | * [setAccessToken -setting the access token to the client] 93 | * @param [type] $google_token [googel auth token] 94 | */ 95 | public function setAccessToken($google_token = null) { 96 | try { 97 | 98 | if (!is_null($google_token)) { 99 | $this->client->setAccessToken($google_token); 100 | } 101 | 102 | if (!is_null($google_token) && $this->client->isAccessTokenExpired()) { 103 | $refreshed_token = $this->client->getRefreshToken(); 104 | $this->client->fetchAccessTokenWithRefreshToken($refreshed_token); 105 | $newToken = $this->client->getAccessToken(); 106 | $newToken = json_encode($newToken); 107 | } 108 | 109 | return !$this->client->isAccessTokenExpired(); 110 | 111 | } catch (\Google_Service_Exception $e) { 112 | 113 | throw new Exception($e->getMessage(), 1); 114 | 115 | } catch (\Google_Exception $e) { 116 | 117 | throw new Exception($e->getMessage(), 1); 118 | 119 | } catch (Exception $e) { 120 | 121 | throw new Exception($e->getMessage(), 1); 122 | } 123 | } 124 | 125 | /** 126 | * [createResource creating a resource array and addind properties to it] 127 | * @param $properties [param properties to be added to channel] 128 | * @return [resource array] 129 | */ 130 | public function createResource($properties) { 131 | try { 132 | 133 | $resource = array(); 134 | foreach ($properties as $prop => $value) { 135 | 136 | if ($value) { 137 | /** 138 | * add property to resource 139 | */ 140 | $this->addPropertyToResource($resource, $prop, $value); 141 | } 142 | } 143 | 144 | return $resource; 145 | 146 | } catch (Exception $e) { 147 | throw new Exception($e->getMessage(), 1); 148 | } 149 | } 150 | 151 | /** 152 | * [addPropertyToResource description] 153 | * @param &$ref [using reference of array from createResource to add property to it] 154 | * @param $property [property to be inserted to resource array] 155 | */ 156 | public function addPropertyToResource(&$ref, $property, $value) { 157 | try { 158 | 159 | $keys = explode(".", $property); 160 | $isArray = false; 161 | foreach ($keys as $key) { 162 | 163 | /** 164 | * snippet.tags[] [convert to snippet.tags] 165 | * a boolean variable [to handle the value like an array] 166 | */ 167 | if (substr($key, -2) == "[]") { 168 | $key = substr($key, 0, -2); 169 | $isArray = true; 170 | } 171 | 172 | $ref = &$ref[$key]; 173 | } 174 | 175 | /** 176 | * Set the property value [ handling the array values] 177 | */ 178 | if ($isArray && $value) { 179 | 180 | $ref = $value; 181 | $ref = explode(",", $value); 182 | 183 | } elseif ($isArray) { 184 | 185 | $ref = array(); 186 | 187 | } else { 188 | 189 | $ref = $value; 190 | } 191 | 192 | } catch (Exception $e) { 193 | throw new Exception($e->getMessage(), 1); 194 | } 195 | } 196 | 197 | /** 198 | * [parseTime - parse the video time in to description format] 199 | * @param $time [youtube returned time format] 200 | * @return [string parsed time] 201 | */ 202 | public function parseTime($time) { 203 | $tempTime = str_replace("PT", " ", $time); 204 | $tempTime = str_replace('H', " Hours ", $tempTime); 205 | $tempTime = str_replace('M', " Minutes ", $tempTime); 206 | $tempTime = str_replace('S', " Seconds ", $tempTime); 207 | 208 | return $tempTime; 209 | } 210 | 211 | } -------------------------------------------------------------------------------- /src/AuthenticateService.php: -------------------------------------------------------------------------------- 1 | googleLiveBroadcastSnippet = new \Google_Service_YouTube_LiveBroadcastSnippet; 18 | $this->googleLiveBroadcastStatus = new \Google_Service_YouTube_LiveBroadcastStatus; 19 | $this->googleYoutubeLiveBroadcast = new \Google_Service_YouTube_LiveBroadcast; 20 | } 21 | 22 | public function authChannelWithCode($code) { 23 | $authResponse = []; 24 | 25 | $token = $this->getToken($code); 26 | if (!$token) { 27 | $authResponse['error'] = 'invalid token'; 28 | return $authResponse; 29 | } 30 | $authResponse['token'] = $token; 31 | $this->setAccessToken($authResponse['token']); 32 | $authResponse['channel_details'] = $this->channelDetails(); 33 | $authResponse['live_streaming_status'] = $this->liveStreamTest($token) ? 'enabled' : 'disbaled'; 34 | 35 | return $authResponse; 36 | } 37 | 38 | protected function channelDetails() { 39 | $params = array('mine' => true); 40 | $params = array_filter($params); 41 | $part = 'snippet'; 42 | $service = new \Google_Service_YouTube($this->client); 43 | return $service->channels->listChannels($part, $params); 44 | } 45 | 46 | protected function liveStreamTest($token) { 47 | try { 48 | $response = []; 49 | /** 50 | * [setAccessToken [setting accent token to client]] 51 | */ 52 | $setAccessToken = $this->setAccessToken($token); 53 | if (!$setAccessToken) { 54 | return false; 55 | } 56 | 57 | /** 58 | * [$service [instance of Google_Service_YouTube ]] 59 | * @var [type] 60 | */ 61 | $youtube = new \Google_Service_YouTube($this->client); 62 | 63 | $title = "test"; 64 | $description = "test live event"; 65 | $startdt = Carbon::now("Asia/Kolkata"); 66 | $startdtIso = $startdt->toIso8601String(); 67 | 68 | $privacy_status = "public"; 69 | $language = 'English'; 70 | 71 | /** 72 | * Create an object for the liveBroadcast resource [specify snippet's title, scheduled start time, and scheduled end time] 73 | */ 74 | $this->googleLiveBroadcastSnippet->setTitle($title); 75 | $this->googleLiveBroadcastSnippet->setDescription($description); 76 | $this->googleLiveBroadcastSnippet->setScheduledStartTime($startdtIso); 77 | 78 | /** 79 | * object for the liveBroadcast resource's status ["private, public or unlisted"] 80 | */ 81 | $this->googleLiveBroadcastStatus->setPrivacyStatus($privacy_status); 82 | 83 | /** 84 | * API Request [inserts the liveBroadcast resource] 85 | */ 86 | $this->googleYoutubeLiveBroadcast->setSnippet($this->googleLiveBroadcastSnippet); 87 | $this->googleYoutubeLiveBroadcast->setStatus($this->googleLiveBroadcastStatus); 88 | $this->googleYoutubeLiveBroadcast->setKind('youtube#liveBroadcast'); 89 | 90 | /** 91 | * Execute Insert LiveBroadcast Resource Api [return an object that contains information about the new broadcast] 92 | */ 93 | $broadcastsResponse = $youtube->liveBroadcasts->insert('snippet,status', $this->googleYoutubeLiveBroadcast, array()); 94 | $response['broadcast_response'] = $broadcastsResponse; 95 | $youtubeEventId = isset($broadcastsResponse['id']) ? $broadcastsResponse['id'] : false; 96 | 97 | if (!$youtubeEventId) { 98 | return false; 99 | } 100 | 101 | $this->deleteEvent($youtubeEventId); 102 | return true; 103 | 104 | } catch (\Google_Service_Exception $e) { 105 | /** 106 | * This error is thrown if the Service is 107 | * either not available or not enabled for the specific account 108 | */ 109 | return false; 110 | 111 | } catch (\Google_Exception $e) { 112 | 113 | throw new Exception($e->getMessage(), 1); 114 | 115 | } catch (Exception $e) { 116 | 117 | throw new Exception($e->getMessage(), 1); 118 | } 119 | 120 | } 121 | 122 | public function deleteEvent($youtubeEventId) { 123 | try { 124 | 125 | /** 126 | * [$service [instance of Google_Service_YouTube]] 127 | */ 128 | $youtube = new \Google_Service_YouTube($this->client); 129 | $deleteBroadcastsResponse = $youtube->liveBroadcasts->delete($youtubeEventId); 130 | return true; 131 | 132 | } catch (\Google_Service_Exception $e) { 133 | 134 | throw new Exception($e->getMessage(), 1); 135 | 136 | } catch (\Google_Exception $e) { 137 | 138 | throw new Exception($e->getMessage(), 1); 139 | 140 | } catch (Exception $e) { 141 | 142 | throw new Exception($e->getMessage(), 1); 143 | } 144 | } 145 | 146 | } -------------------------------------------------------------------------------- /src/ChannelService.php: -------------------------------------------------------------------------------- 1 | 'GoogleDevelopers')] 13 | * @return [json object of response] 14 | */ 15 | public function channelsListById($part, $params) { 16 | try { 17 | 18 | $params = array_filter($params); 19 | 20 | /** 21 | * [$service instance of Google_Service_YouTube] 22 | * [$response object of channel lists][making api call to list channels] 23 | * @var [type] 24 | */ 25 | 26 | $service = new \Google_Service_YouTube($this->client); 27 | $respone = $service->channels->listChannels($part, $params); 28 | 29 | return $service->channels->listChannels($part, $params); 30 | 31 | } catch (\Google_Service_Exception $e) { 32 | throw new Exception($e->getMessage(), 1); 33 | 34 | } catch (\Google_Exception $e) { 35 | throw new Exception($e->getMessage(), 1); 36 | 37 | } catch (Exception $e) { 38 | \Log::info(json_encode($e->getMessage())); 39 | throw new Exception(json_encode($e->getMessage()), 1); 40 | } 41 | } 42 | 43 | public function getChannelDetails($token) { 44 | try { 45 | if (!$this->setAccessToken($token)) { 46 | return false; 47 | } 48 | $part = "snippet,contentDetails,statistics,brandingSettings"; 49 | $params = array('mine' => true); 50 | $service = new \Google_Service_YouTube($this->client); 51 | $response = $service->channels->listChannels($part, $params); 52 | 53 | $response = json_decode(json_encode($response), true); 54 | return $response['items'][0]; 55 | 56 | } catch (\Google_Service_Exception $e) { 57 | throw new Exception($e->getMessage(), 1); 58 | 59 | } catch (\Google_Exception $e) { 60 | throw new Exception($e->getMessage(), 1); 61 | 62 | } catch (Exception $e) { 63 | throw new Exception($e->getMessage(), 1); 64 | } 65 | } 66 | /** 67 | * [updateChannelBrandingSettings update channel details] 68 | * @param $google_token [auth token for the channel] 69 | * @param $properties ['id' => '', 70 | * 'brandingSettings.channel.description' => '', 71 | * 'brandingSettings.channel.keywords' => '', 72 | * 'brandingSettings.channel.defaultLanguage' => '', 73 | * 'brandingSettings.channel.defaultTab' => '', 74 | * 'brandingSettings.channel.moderateComments' => '', 75 | * 'brandingSettings.channel.showRelatedChannels' => '', 76 | * 'brandingSettings.channel.showBrowseView' => '', 77 | * 'brandingSettings.channel.featuredChannelsTitle' => '', 78 | * 'brandingSettings.channel.featuredChannelsUrls[]' => '', 79 | * 'brandingSettings.channel.unsubscribedTrailer' => '') 80 | * ] 81 | * @param $part [ brandingSettings ] 82 | * @param $params ['onBehalfOfContentOwner' => ''] 83 | * @return [boolean ] 84 | */ 85 | public function updateChannelBrandingSettings($googleToken, $properties, $part, $params) { 86 | try { 87 | $params = array_filter($params); 88 | 89 | /** 90 | * [$service description] 91 | * @var [type] 92 | */ 93 | $service = new \Google_Service_YouTube($this->client); 94 | $propertyObject = $this->createResource($properties); 95 | 96 | $resource = new \Google_Service_YouTube_Channel($propertyObject); 97 | $service->channels->update($part, $resource, $params); 98 | 99 | return true; 100 | 101 | } catch (\Google_Service_Exception $e) { 102 | throw new Exception($e->getMessage(), 1); 103 | 104 | } catch (\Google_Exception $e) { 105 | throw new Exception($e->getMessage(), 1); 106 | 107 | } catch (Exception $e) { 108 | throw new Exception($e->getMessage(), 1); 109 | } 110 | } 111 | 112 | /** 113 | * [parseSubscriptions working] 114 | * @param [type] $part 115 | * @return [type] $params array('channelId'= '', 'totalResults'= '') 116 | */ 117 | public function subscriptionByChannelId($params, $part = 'snippet') { 118 | try { 119 | 120 | $params = array_filter($params); 121 | 122 | $service = new \Google_Service_YouTube($this->client); 123 | return $this->parseSubscriptions($params); 124 | 125 | } catch (\Google_Service_Exception $e) { 126 | throw new Exception($e->getMessage(), 1); 127 | 128 | } catch (\Google_Exception $e) { 129 | throw new Exception($e->getMessage(), 1); 130 | 131 | } catch (Exception $e) { 132 | throw new Exception($e->getMessage(), 1); 133 | } 134 | } 135 | 136 | /** 137 | * [parseSubscriptions working] 138 | * @param [type] $channelId [description] 139 | * @return [type] [description] 140 | */ 141 | public function parseSubscriptions($params) { 142 | $channelId = $params['channelId']; 143 | $totalResults = $params['totalResults']; 144 | $maxResultsPerPage = 50; 145 | if($totalResults < 1){$totalResults = 0;} 146 | $maxPages = ($totalResults - ($totalResults % $maxResultsPerPage))/$maxResultsPerPage + 1; 147 | $i = 0; 148 | try { 149 | $service = new \Google_Service_YouTube($this->client); 150 | $part = 'snippet'; 151 | $params = array('channelId' => $channelId, 'maxResults' => $maxResultsPerPage); 152 | $nextPageToken = 1; 153 | $subscriptions = []; 154 | while ($nextPageToken and $i < $maxPages) { 155 | if($i == $maxPages-1){ 156 | $params['maxResults'] = $totalResults % $maxResultsPerPage + 2; 157 | } 158 | 159 | $response = $service->subscriptions->listSubscriptions($part, $params); 160 | $response = json_decode(json_encode($response), true); 161 | $sub = array_column($response['items'], 'snippet'); 162 | $sub2 = array_column($sub, 'resourceId'); 163 | $subscriptions = array_merge($subscriptions, $sub2); 164 | $nextPageToken = isset($response['nextPageToken']) ? $response['nextPageToken'] : false; 165 | 166 | $params['pageToken'] = $nextPageToken; 167 | $i++; 168 | } 169 | 170 | return $subscriptions; 171 | 172 | } catch (\Google_Service_Exception $e) { 173 | throw new Exception($e->getMessage(), 1); 174 | 175 | } catch (\Google_Exception $e) { 176 | throw new Exception($e->getMessage(), 1); 177 | 178 | } catch (Exception $e) { 179 | throw new Exception($e->getMessage(), 1); 180 | } 181 | 182 | } 183 | 184 | /** 185 | * 186 | * properties - array('snippet.resourceId.kind' => 'youtube#channel','snippet.resourceId.channelId' => 'UCqIOaYtQak4-FD2-yI7hFkw'), 187 | * part = 'snippet' 188 | * @param string $value [description] 189 | */ 190 | public function addSubscriptions($properties, $token, $part = 'snippet', $params = []) { 191 | try { 192 | 193 | $setAccessToken = $this->setAccessToken($token); 194 | 195 | if (!$setAccessToken) { 196 | return false; 197 | } 198 | 199 | $service = new \Google_Service_YouTube($this->client); 200 | 201 | $params = array_filter($params); 202 | $propertyObject = $this->createResource($properties); 203 | 204 | $resource = new \Google_Service_YouTube_Subscription($propertyObject); 205 | $response = $service->subscriptions->insert($part, $resource, $params); 206 | return $response; 207 | 208 | } catch (\Google_Service_Exception $e) { 209 | throw new Exception($e->getMessage(), 1); 210 | 211 | } catch (\Google_Exception $e) { 212 | throw new Exception($e->getMessage(), 1); 213 | 214 | } catch (Exception $e) { 215 | throw new Exception($e->getMessage(), 1); 216 | } 217 | 218 | } 219 | 220 | public function removeSubscription($token, $subscriptionId, $params = []) { 221 | try { 222 | 223 | $setAccessToken = $this->setAccessToken($token); 224 | 225 | if (!$setAccessToken) { 226 | return false; 227 | } 228 | 229 | $service = new \Google_Service_YouTube($this->client); 230 | 231 | $params = array_filter($params); 232 | 233 | $response = $service->subscriptions->delete($subscriptionId, $params); 234 | 235 | } catch (\Google_Service_Exception $e) { 236 | throw new Exception($e->getMessage(), 1); 237 | 238 | } catch (\Google_Exception $e) { 239 | throw new Exception($e->getMessage(), 1); 240 | 241 | } catch (Exception $e) { 242 | throw new Exception($e->getMessage(), 1); 243 | } 244 | 245 | } 246 | 247 | } -------------------------------------------------------------------------------- /src/LiveStreamService.php: -------------------------------------------------------------------------------- 1 | googleLiveBroadcastSnippet = new \Google_Service_YouTube_LiveBroadcastSnippet; 25 | $this->googleLiveBroadcastStatus = new \Google_Service_YouTube_LiveBroadcastStatus; 26 | $this->googleYoutubeLiveBroadcast = new \Google_Service_YouTube_LiveBroadcast; 27 | $this->googleYoutubeLiveStreamSnippet = new \Google_Service_YouTube_LiveStreamSnippet; 28 | $this->googleYoutubeCdnSettings = new \Google_Service_YouTube_CdnSettings; 29 | $this->googleYoutubeLiveStream = new \Google_Service_YouTube_LiveStream; 30 | $this->googleYoutubeVideoRecordingDetails = new \Google_Service_YouTube_VideoRecordingDetails; 31 | 32 | } 33 | 34 | /** 35 | * [broadcast creating the event on youtube] 36 | * @param [type] $token [auth token for youtube channel] 37 | * @param [type] $data [array of the event details] 38 | * @return [type] [response array of broadcast ] 39 | */ 40 | public function broadcast($token, $data = null) { 41 | try { 42 | $response = []; 43 | if (count($data) < 1 || empty($data) || !isset($data['title']) || !isset($data['description'])) { 44 | return false; 45 | } 46 | 47 | /** 48 | * [setAccessToken [setting accent token to client]] 49 | */ 50 | $setAccessToken = $this->setAccessToken($token); 51 | if (!$setAccessToken) { 52 | return false; 53 | } 54 | 55 | /** 56 | * [$service [instance of Google_Service_YouTube ]] 57 | * @var [type] 58 | */ 59 | $youtube = new \Google_Service_YouTube($this->client); 60 | 61 | $title = $data["title"]; 62 | $description = $data["description"]; 63 | $thumbnail_path = isset($data["thumbnail_path"]) ? $data["thumbnail_path"] : null; 64 | $startdt = Carbon::createFromFormat('Y-m-d H:i:s', $data["event_start_date_time"], $data["time_zone"]); 65 | $startdt = ($startdt < Carbon::now($data["time_zone"])) ? Carbon::now($data["time_zone"]) : $startdt; 66 | $startdtIso = $startdt->toIso8601String(); 67 | 68 | if (count($data["tag_array"]) > 0) { 69 | $tags = substr(str_replace(", ,", ",", implode(',', $data["tag_array"])), 0, 498); 70 | $tags = (substr($tags, -1) == ',') ? substr($tags, 0, -1) : $tags; 71 | $data["tag_array"] = explode(',', $tags); 72 | } else { 73 | $data["tag_array"] = []; 74 | } 75 | 76 | $privacy_status = isset($data['privacy_status']) ? $data['privacy_status'] : "public"; 77 | $language = isset($data["language_name"]) ? $data["language_name"] : 'English'; 78 | 79 | /** 80 | * Create an object for the liveBroadcast resource [specify snippet's title, scheduled start time, and scheduled end time] 81 | */ 82 | $this->googleLiveBroadcastSnippet->setTitle($title); 83 | $this->googleLiveBroadcastSnippet->setDescription($description); 84 | $this->googleLiveBroadcastSnippet->setScheduledStartTime($startdtIso); 85 | 86 | /** 87 | * object for the liveBroadcast resource's status ["private, public or unlisted"] 88 | */ 89 | $this->googleLiveBroadcastStatus->setPrivacyStatus($privacy_status); 90 | 91 | /** 92 | * API Request [inserts the liveBroadcast resource] 93 | */ 94 | $this->googleYoutubeLiveBroadcast->setSnippet($this->googleLiveBroadcastSnippet); 95 | $this->googleYoutubeLiveBroadcast->setStatus($this->googleLiveBroadcastStatus); 96 | $this->googleYoutubeLiveBroadcast->setKind('youtube#liveBroadcast'); 97 | 98 | /** 99 | * Execute Insert LiveBroadcast Resource Api [return an object that contains information about the new broadcast] 100 | */ 101 | $broadcastsResponse = $youtube->liveBroadcasts->insert('snippet,status', $this->googleYoutubeLiveBroadcast, array()); 102 | $response['broadcast_response'] = $broadcastsResponse; 103 | 104 | $youtubeEventId = $broadcastsResponse['id']; 105 | 106 | /** 107 | * set thumbnail to the event 108 | */ 109 | if (!is_null($thumbnail_path)) { 110 | $thumb = $this->uploadThumbnail($thumbnail_path, $youtubeEventId); 111 | } 112 | 113 | /** 114 | * Call the API's videos.list method to retrieve the video resource. 115 | */ 116 | $listResponse = $youtube->videos->listVideos("snippet", array('id' => $youtubeEventId)); 117 | $video = $listResponse[0]; 118 | 119 | /** 120 | * update the tags and language via video resource 121 | */ 122 | $videoSnippet = $video['snippet']; 123 | $videoSnippet['tags'] = $data["tag_array"]; 124 | if (!is_null($language)) { 125 | $temp = isset($this->ytLanguage[$language]) ? $this->ytLanguage[$language] : "en"; 126 | $videoSnippet['defaultAudioLanguage'] = $temp; 127 | $videoSnippet['defaultLanguage'] = $temp; 128 | } 129 | 130 | $video['snippet'] = $videoSnippet; 131 | 132 | /** 133 | * Update video resource [videos.update() method.] 134 | */ 135 | $updateResponse = $youtube->videos->update("snippet", $video); 136 | $response['video_response'] = $updateResponse; 137 | 138 | /** 139 | * object of livestream resource [snippet][title] 140 | */ 141 | $this->googleYoutubeLiveStreamSnippet->setTitle($title); 142 | 143 | /** 144 | * object for content distribution [stream's format,ingestion type.] 145 | */ 146 | $this->googleYoutubeCdnSettings->setFormat("720p"); 147 | $this->googleYoutubeCdnSettings->setIngestionType('rtmp'); 148 | 149 | /** 150 | * API request [inserts liveStream resource.] 151 | */ 152 | $this->googleYoutubeLiveStream->setSnippet($this->googleYoutubeLiveStreamSnippet); 153 | $this->googleYoutubeLiveStream->setCdn($this->googleYoutubeCdnSettings); 154 | $this->googleYoutubeLiveStream->setKind('youtube#liveStream'); 155 | 156 | /** 157 | * execute the insert request [return an object that contains information about new stream] 158 | */ 159 | $streamsResponse = $youtube->liveStreams->insert('snippet,cdn', $this->googleYoutubeLiveStream, array()); 160 | $response['stream_response'] = $streamsResponse; 161 | 162 | /** 163 | * Bind the broadcast to the live stream 164 | */ 165 | $bindBroadcastResponse = $youtube->liveBroadcasts->bind( 166 | $broadcastsResponse['id'], 'id,contentDetails', 167 | array( 168 | 'streamId' => $streamsResponse['id'], 169 | )); 170 | 171 | $response['bind_broadcast_response'] = $bindBroadcastResponse; 172 | 173 | return $response; 174 | 175 | } catch (\Google_Service_Exception $e) { 176 | 177 | throw new Exception($e->getMessage(), 1); 178 | 179 | } catch (\Google_Exception $e) { 180 | 181 | throw new Exception($e->getMessage(), 1); 182 | 183 | } catch (Exception $e) { 184 | 185 | throw new Exception($e->getMessage(), 1); 186 | } 187 | 188 | } 189 | 190 | /** 191 | * [uploadThumbnail upload thumbnail for the event] 192 | * @param string $url [path to image] 193 | * @param [type] $videoId [eventId] 194 | * @return [type] [thumbnail url] 195 | */ 196 | public function uploadThumbnail($url = '', $videoId) { 197 | if ($this->client->getAccessToken()) { 198 | try { 199 | /** 200 | * [$service [instance of Google_Service_YouTube ]] 201 | * @var [type] 202 | */ 203 | $youtube = new \Google_Service_YouTube($this->client); 204 | 205 | $videoId = $videoId; 206 | $imagePath = $url; 207 | 208 | /** 209 | * size of chunk to be uploaded in bytes [default 1 * 1024 * 1024] (Set a higher value for reliable connection as fewer chunks lead to faster uploads) 210 | */ 211 | $chunkSizeBytes = 1 * 1024 * 1024; 212 | $this->client->setDefer(true); 213 | 214 | /** 215 | * Setting the defer flag to true tells the client to return a request which can be called with ->execute(); instead of making the API call immediately 216 | */ 217 | $setRequest = $youtube->thumbnails->set($videoId); 218 | 219 | /** 220 | * MediaFileUpload object [resumable uploads] 221 | */ 222 | $media = new \Google_Http_MediaFileUpload( 223 | $this->client, 224 | $setRequest, 225 | 'image/png', 226 | null, 227 | true, 228 | $chunkSizeBytes 229 | ); 230 | $media->setFileSize(filesize($imagePath)); 231 | 232 | /** 233 | * Read the media file [to upload chunk by chunk] 234 | */ 235 | $status = false; 236 | $handle = fopen($imagePath, "rb"); 237 | while (!$status && !feof($handle)) { 238 | $chunk = fread($handle, $chunkSizeBytes); 239 | $status = $media->nextChunk($chunk); 240 | } 241 | 242 | fclose($handle); 243 | 244 | /** 245 | * set defer to false [to make other calls after the file upload] 246 | */ 247 | $this->client->setDefer(false); 248 | $thumbnailUrl = $status['items'][0]['default']['url']; 249 | return $thumbnailUrl; 250 | 251 | } catch (\Google_Exception $e) { 252 | 253 | throw new Exception($e->getMessage(), 1); 254 | } 255 | } 256 | } 257 | 258 | /** 259 | * [updateTags description] 260 | * @param [type] $videoId [eventID] 261 | * @param array $tagsArray [array of tags] 262 | */ 263 | public function updateTags($videoId, $tagsArray = []) { 264 | if ($this->client->getAccessToken()) { 265 | try { 266 | 267 | /** 268 | * [$service [instance of Google_Service_YouTube ]] 269 | * @var [type] 270 | */ 271 | $youtube = new \Google_Service_YouTube($this->client); 272 | $videoId = $videoId; 273 | 274 | /** 275 | * [$listResponse videos.list method to retrieve the video resource.] 276 | */ 277 | $listResponse = $youtube->videos->listVideos("snippet", 278 | array('id' => $videoId)); 279 | $video = $listResponse[0]; 280 | 281 | $videoSnippet = $video['snippet']; 282 | $videoSnippet['tags'] = $data["tag_array"]; 283 | $video['snippet'] = $videoSnippet; 284 | 285 | /** 286 | * [$updateResponse calling the videos.update() method.] 287 | */ 288 | $updateResponse = $youtube->videos->update("snippet", $video); 289 | 290 | } catch (\Google_Exception $e) { 291 | throw new Exception($e->getMessage(), 1); 292 | } 293 | } 294 | } 295 | 296 | /** 297 | * [transitionEvent transition the state of event [test, start streaming , stop streaming]] 298 | * @param [type] $token [auth token for the channel] 299 | * @param [type] $youtubeEventId [eventId] 300 | * @param [type] $broadcastStatus [transition state - ["testing", "live", "complete"]] 301 | * @return [type] [transition status] 302 | */ 303 | public function transitionEvent($token, $youtubeEventId, $broadcastStatus) { 304 | try { 305 | 306 | if (!empty($token)) { 307 | return false; 308 | } 309 | 310 | /** 311 | * [setAccessToken [setting accent token to client]] 312 | */ 313 | $setAccessToken = $this->setAccessToken($token); 314 | if (!$setAccessToken) { 315 | return false; 316 | } 317 | 318 | $part = "status, id, snippet"; 319 | /** 320 | * [$service [instance of Google_Service_YouTube ]] 321 | * @var [type] 322 | */ 323 | $youtube = new \Google_Service_YouTube($this->client); 324 | $liveBroadcasts = $youtube->liveBroadcasts; 325 | $transition = $liveBroadcasts->transition($broadcastStatus, $youtubeEventId, $part); 326 | return $transition; 327 | 328 | } catch (\Google_Exception $e) { 329 | 330 | throw new Exception($e->getMessage(), 1); 331 | 332 | } catch (Exception $e) { 333 | 334 | throw new Exception($e->getMessage(), 1); 335 | } 336 | } 337 | 338 | /** 339 | * [updateBroadcast update the already created event on youtunbe channel] 340 | * @param [type] $token [channel auth token] 341 | * @param [type] $data [event details] 342 | * @param [type] $youtubeEventId [eventID] 343 | * @return [type] [response array for various process in the update] 344 | */ 345 | public function updateBroadcast($token, $data, $youtubeEventId) { 346 | try { 347 | /** 348 | * [setAccessToken [setting accent token to client]] 349 | */ 350 | $setAccessToken = $this->setAccessToken($token); 351 | if (!$setAccessToken) { 352 | return false; 353 | } 354 | 355 | /** 356 | * [$service [instance of Google_Service_YouTube ]] 357 | * @var [type] 358 | */ 359 | $youtube = new \Google_Service_YouTube($this->client); 360 | 361 | if (count($data) < 1 || empty($data)) { 362 | return false; 363 | } 364 | 365 | $title = $data["title"]; 366 | $description = $data['description']; 367 | $thumbnail_path = isset($data['thumbnail_path']) ? $data['thumbnail_path'] : null; 368 | 369 | /** 370 | * parsing event start date 371 | */ 372 | $startdt = Carbon::createFromFormat('Y-m-d H:i:s', $data['event_start_date_time'], $data['time_zone']); 373 | $startdt = ($startdt < Carbon::now($data['time_zone'])) ? Carbon::now($data['time_zone']) : $startdt; 374 | $startdtIso = $startdt->toIso8601String(); 375 | $privacy_status = isset($data['privacy_status']) ? $data['privacy_status'] : "public"; 376 | 377 | /** 378 | * parsing event end date 379 | */ 380 | if (isset($data['event_end_date_time'])) { 381 | $enddt = Carbon::createFromFormat('Y-m-d H:i:s', $data['event_end_date_time'], $data['time_zone']); 382 | $enddt = ($enddt < Carbon::now($data['time_zone'])) ? Carbon::now($data['time_zone']) : $enddt; 383 | $enddtIso = $enddt->toIso8601String(); 384 | } 385 | 386 | $tags = substr(str_replace(", ,", ",", implode(',', $data['tag_array'])), 0, 498); 387 | $tags = (substr($tags, -1) == ',') ? substr($tags, 0, -1) : $tags; 388 | $data['tag_array'] = explode(',', $tags); 389 | 390 | $language = $data['language_name']; 391 | 392 | /** 393 | * Create an object for the liveBroadcast resource's snippet [snippet's title, scheduled start time, and scheduled end time.] 394 | */ 395 | $this->googleLiveBroadcastSnippet->setTitle($title); 396 | $this->googleLiveBroadcastSnippet->setDescription($description); 397 | $this->googleLiveBroadcastSnippet->setScheduledStartTime($startdtIso); 398 | 399 | if (isset($data['event_end_date_time'])) { 400 | $this->googleLiveBroadcastSnippet->setScheduledEndTime($enddtIso); 401 | } 402 | 403 | /** 404 | * Create an object for the liveBroadcast resource's status ["private, public or unlisted".] 405 | */ 406 | $this->googleLiveBroadcastStatus->setPrivacyStatus($privacy_status); 407 | 408 | /** 409 | * Create the API request [inserts the liveBroadcast resource.] 410 | */ 411 | $this->googleYoutubeLiveBroadcast->setSnippet($this->googleLiveBroadcastSnippet); 412 | $this->googleYoutubeLiveBroadcast->setStatus($this->googleLiveBroadcastStatus); 413 | $this->googleYoutubeLiveBroadcast->setKind('youtube#liveBroadcast'); 414 | $this->googleYoutubeLiveBroadcast->setId($youtubeEventId); 415 | 416 | /** 417 | * Execute the request [return info about the new broadcast ] 418 | */ 419 | $broadcastsResponse = $youtube->liveBroadcasts->update('snippet,status', 420 | $this->googleYoutubeLiveBroadcast, array()); 421 | 422 | /** 423 | * set thumbnail 424 | */ 425 | if (!is_null($thumbnail_path)) { 426 | $thumb = $this->uploadThumbnail($thumbnail_path, $youtubeEventId); 427 | } 428 | 429 | /** 430 | * Call the API's videos.list method [retrieve the video resource] 431 | */ 432 | $listResponse = $youtube->videos->listVideos("snippet", 433 | array('id' => $youtubeEventId)); 434 | $video = $listResponse[0]; 435 | $videoSnippet = $video['snippet']; 436 | $videoSnippet['tags'] = $data['tag_array']; 437 | 438 | /** 439 | * set Language and other details 440 | */ 441 | if (!is_null($language)) { 442 | $temp = isset($this->ytLanguage[$language]) ? $this->ytLanguage[$language] : "en"; 443 | $videoSnippet['defaultAudioLanguage'] = $temp; 444 | $videoSnippet['defaultLanguage'] = $temp; 445 | } 446 | 447 | $videoSnippet['title'] = $title; 448 | $videoSnippet['description'] = $description; 449 | $videoSnippet['scheduledStartTime'] = $startdtIso; 450 | $video['snippet'] = $videoSnippet; 451 | 452 | /** 453 | * Update the video resource [call videos.update() method] 454 | */ 455 | $updateResponse = $youtube->videos->update("snippet", $video); 456 | 457 | $response['broadcast_response'] = $updateResponse; 458 | 459 | $youtubeEventId = $updateResponse['id']; 460 | 461 | $this->googleYoutubeLiveStreamSnippet->setTitle($title); 462 | 463 | /** 464 | * object for content distribution [stream's format,ingestion type.] 465 | */ 466 | 467 | $this->googleYoutubeCdnSettings->setFormat("720p"); 468 | $this->googleYoutubeCdnSettings->setIngestionType('rtmp'); 469 | 470 | /** 471 | * API request [inserts liveStream resource.] 472 | */ 473 | $this->googleYoutubeLiveStream->setSnippet($this->googleYoutubeLiveStreamSnippet); 474 | $this->googleYoutubeLiveStream->setCdn($this->googleYoutubeCdnSettings); 475 | $this->googleYoutubeLiveStream->setKind('youtube#liveStream'); 476 | 477 | /** 478 | * execute the insert request [return an object that contains information about new stream] 479 | */ 480 | $streamsResponse = $youtube->liveStreams->insert('snippet,cdn', $this->googleYoutubeLiveStream, array()); 481 | $response['stream_response'] = $streamsResponse; 482 | 483 | /** 484 | * Bind the broadcast to the live stream 485 | */ 486 | $bindBroadcastResponse = $youtube->liveBroadcasts->bind( 487 | $updateResponse['id'], 'id,contentDetails', 488 | array( 489 | 'streamId' => $streamsResponse['id'], 490 | )); 491 | 492 | $response['bind_broadcast_response'] = $bindBroadcastResponse; 493 | 494 | return $response; 495 | 496 | } catch (\Google_Service_Exception $e) { 497 | 498 | throw new Exception($e->getMessage(), 1); 499 | 500 | } catch (\Google_Exception $e) { 501 | 502 | throw new Exception($e->getMessage(), 1); 503 | 504 | } catch (Exception $e) { 505 | 506 | throw new Exception($e->getMessage(), 1); 507 | } 508 | 509 | } 510 | 511 | /** 512 | * [deleteEvent delete an event created in youtube] 513 | * @param [type] $token [auth token for channel] 514 | * @param [type] $youtubeEventId [eventID] 515 | * @return [type] [deleteBroadcastsResponse] 516 | */ 517 | public function deleteEvent($token, $youtubeEventId) { 518 | try { 519 | /** 520 | * [setAccessToken [setting accent token to client]] 521 | */ 522 | $setAccessToken = $this->setAccessToken($token); 523 | if (!$setAccessToken) { 524 | return false; 525 | } 526 | 527 | /** 528 | * [$service [instance of Google_Service_YouTube ]] 529 | * @var [type] 530 | */ 531 | $youtube = new \Google_Service_YouTube($this->client); 532 | $deleteBroadcastsResponse = $youtube->liveBroadcasts->delete($youtubeEventId); 533 | 534 | return $deleteBroadcastsResponse; 535 | 536 | } catch (\Google_Service_Exception $e) { 537 | 538 | throw new Exception($e->getMessage(), 1); 539 | 540 | } catch (\Google_Exception $e) { 541 | 542 | throw new Exception($e->getMessage(), 1); 543 | 544 | } catch (Exception $e) { 545 | 546 | throw new Exception($e->getMessage(), 1); 547 | } 548 | } 549 | } -------------------------------------------------------------------------------- /src/VideoService.php: -------------------------------------------------------------------------------- 1 | client); 21 | return $service->videos->listVideos($part, $params); 22 | 23 | } catch (\Google_Service_Exception $e) { 24 | throw new Exception($e->getMessage(), 1); 25 | 26 | } catch (\Google_Exception $e) { 27 | throw new Exception($e->getMessage(), 1); 28 | 29 | } catch (Exception $e) { 30 | throw new Exception($e->getMessage(), 1); 31 | } 32 | } 33 | 34 | /** 35 | * [searchListByKeyword -get youtube search results by keyoword ] 36 | * @param $part [snippet,id] 37 | * @param $params ['maxResults','q','type','pageToken'] 38 | * @return [json object or response] 39 | */ 40 | public function searchListByKeyword($part, $params) { 41 | try { 42 | 43 | $params = array_filter($params); 44 | 45 | $service = new \Google_Service_YouTube($this->client); 46 | return $service->search->listSearch($part, $params); 47 | 48 | } catch (\Google_Service_Exception $e) { 49 | throw new Exception($e->getMessage(), 1); 50 | 51 | } catch (\Google_Exception $e) { 52 | throw new Exception($e->getMessage(), 1); 53 | 54 | } catch (Exception $e) { 55 | throw new Exception($e->getMessage(), 1); 56 | } 57 | } 58 | 59 | /** 60 | * [relatedToVideoId - gets realted videos to a particular video id] 61 | * @param $part [ sinppet, id] 62 | * @param $params [ regionCode,relatedToVideoId,relevanceLanguage,videoCategoryId, videoDefinition, videoDimension, type(video or channel)] 63 | * @return [json Object of response] 64 | */ 65 | public function relatedToVideoId($part, $params) { 66 | try { 67 | 68 | $params = array_filter($params); 69 | 70 | $service = new \Google_Service_YouTube($this->client); 71 | return $service->search->listSearch($part, $params); 72 | 73 | } catch (\Google_Service_Exception $e) { 74 | throw new Exception($e->getMessage(), 1); 75 | 76 | } catch (\Google_Exception $e) { 77 | throw new Exception($e->getMessage(), 1); 78 | 79 | } catch (Exception $e) { 80 | throw new Exception($e->getMessage(), 1); 81 | } 82 | } 83 | 84 | /** 85 | * [uploadVideo upload a video to youtube channel] 86 | * @param $google_token [authorization token for the youtube channel ] 87 | * @param $videoPath [path of the video to be uploaded][max video size 128 GB] 88 | * @param $data [video details] 89 | * @return [boolean] 90 | */ 91 | public function uploadVideo($googleToken, $videoPath, $data) { 92 | try { 93 | 94 | if (!isset($data['title']) || !isset($data['description']) || !isset($data['tags']) || !isset($data['category_id']) || !isset($data['video_status'])) { 95 | throw new Exception($e->getMessage(), 1); 96 | return false; 97 | } 98 | 99 | /** 100 | * [setAccessToken [setting accent token to client]] 101 | */ 102 | $setAccessToken = $this->setAccessToken($googleToken); 103 | if (!$setAccessToken) { 104 | return false; 105 | } 106 | 107 | /** 108 | * [youtube [instance of Google_Service_YouTube] ] 109 | */ 110 | $youtube = new \Google_Service_YouTube($this->client); 111 | 112 | /** 113 | * snippet [title, description, tags and category ID] 114 | * asset resource [snippet metadata and type.] 115 | */ 116 | $snippet = new \Google_Service_YouTube_VideoSnippet(); 117 | 118 | $snippet->setTitle($data['title']); 119 | $snippet->setDescription($data['description']); 120 | $snippet->setTags($data['tags']); 121 | $snippet->setCategoryId($data['category_id']); 122 | 123 | /** 124 | * video status ["public", "private", "unlisted"] 125 | */ 126 | $status = new \Google_Service_YouTube_VideoStatus(); 127 | $status->privacyStatus = $data['video_status']; 128 | 129 | /** 130 | * snippet and status [link with new video resource.] 131 | */ 132 | $video = new \Google_Service_YouTube_Video(); 133 | $video->setSnippet($snippet); 134 | $video->setStatus($status); 135 | 136 | /** 137 | * size of chunk to be uploaded in bytes [default 1 * 1024 * 1024] (Set a higher value for reliable connection as fewer chunks lead to faster uploads) 138 | */ 139 | if (isset($data['chunk_size'])) { 140 | $chunkSizeBytes = $data['chunk_size']; 141 | } else { 142 | $chunkSizeBytes = 1 * 1024 * 1024; 143 | } 144 | 145 | /** 146 | * Setting the defer flag to true tells the client to return a request which can be called with ->execute(); instead of making the API call immediately 147 | */ 148 | $this->client->setDefer(true); 149 | 150 | /** 151 | * request [API's videos.insert method] [ to create and upload the video] 152 | */ 153 | $insertRequest = $youtube->videos->insert("status,snippet", $video); 154 | 155 | /** 156 | * MediaFileUpload object [resumable uploads] 157 | */ 158 | $media = new \Google_Http_MediaFileUpload( 159 | $this->client, 160 | $insertRequest, 161 | 'video/*', 162 | null, 163 | true, 164 | $chunkSizeBytes 165 | ); 166 | 167 | $media->setFileSize(filesize($videoPath)); 168 | 169 | /** 170 | * Read the media file [to upload chunk by chunk] 171 | */ 172 | $status = false; 173 | $handle = fopen($videoPath, "rb"); 174 | while (!$status && !feof($handle)) { 175 | 176 | $chunk = fread($handle, $chunkSizeBytes); 177 | $status = $media->nextChunk($chunk); 178 | } 179 | 180 | fclose($handle); 181 | 182 | /** 183 | * set defer to false [to make other calls after the file upload] 184 | */ 185 | $this->client->setDefer(false); 186 | return true; 187 | 188 | } catch (\Google_Service_Exception $e) { 189 | throw new Exception($e->getMessage(), 1); 190 | 191 | } catch (\Google_Exception $e) { 192 | throw new Exception($e->getMessage(), 1); 193 | 194 | } catch (Exception $e) { 195 | throw new Exception($e->getMessage(), 1); 196 | } 197 | } 198 | 199 | /** 200 | * [videosDelete delete a youtube video] 201 | * @param $google_token [auth token for the channel owning the video] 202 | * @param $id [video id] 203 | * @param $params [onbelhalf of owner] 204 | * @return [json obj response] 205 | */ 206 | public function deleteVideo($googleToken, $id, $params = []) { 207 | try { 208 | 209 | /** 210 | * [setAccessToken [setting accent token to client]] 211 | */ 212 | $setAccessToken = $this->setAccessToken($googleToken); 213 | if (!$setAccessToken) { 214 | return false; 215 | } 216 | 217 | /** 218 | * [$service (instance of Google_Service_YouTube)] 219 | */ 220 | $params = array_filter($params); 221 | 222 | $service = new \Google_Service_YouTube($this->client); 223 | return $service->videos->delete($id, $params); 224 | 225 | } catch (\Google_Service_Exception $e) { 226 | throw new Exception($e->getMessage(), 1); 227 | 228 | } catch (\Google_Exception $e) { 229 | throw new Exception($e->getMessage(), 1); 230 | 231 | } catch (Exception $e) { 232 | throw new Exception($e->getMessage(), 1); 233 | } 234 | } 235 | 236 | /* 237 | * [adds like dislike or remove ratiing] 238 | */ 239 | public function videosRate($googleToken, $id, $rating = 'like', $params = []) { 240 | 241 | try { 242 | 243 | $setAccessToken = $this->setAccessToken($googleToken); 244 | if (!$setAccessToken) { 245 | return false; 246 | } 247 | 248 | $service = new Google_Service_YouTube($client); 249 | 250 | $params = array_filter($params); 251 | $response = $service->videos->rate( 252 | $id, $rating, 253 | $params 254 | ); 255 | 256 | } catch (\Google_Service_Exception $e) { 257 | throw new Exception($e->getMessage(), 1); 258 | 259 | } catch (\Google_Exception $e) { 260 | throw new Exception($e->getMessage(), 1); 261 | 262 | } catch (Exception $e) { 263 | throw new Exception($e->getMessage(), 1); 264 | } 265 | } 266 | 267 | } -------------------------------------------------------------------------------- /src/YoutubeLaravelApiServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes(array(__DIR__ . '/config/google-config.php' => config_path('google-config.php')), 'youtube-config'); 15 | } 16 | 17 | /** 18 | * Register any package services. 19 | * 20 | * @return void 21 | */ 22 | public function register() { 23 | // 24 | } 25 | } -------------------------------------------------------------------------------- /src/config/google-config.php: -------------------------------------------------------------------------------- 1 | 2 | env('app_name'), 5 | 'client_id' => env('client_id'), 6 | 'client_secret' => env('client_secret'), 7 | 'api_key' => env('api_key'), 8 | 'redirect_url' => env('redirect_url'), 9 | 'yt_language' => array("Afrikaans" => "af", 10 | "Azerbaijani" => "az", 11 | "Indonesian" => "id", 12 | "Malay" => "ms", 13 | "Bosnian" => "bs", 14 | "Catalan" => "ca", 15 | "Czech" => "cs", 16 | "Danish" => "da", 17 | "German" => "de", 18 | "Estonian" => "et", 19 | "English (United Kingdom)" => "en-GB", 20 | "English" => "en", 21 | "Spanish (Spain)" => "es", 22 | "Spanish (Latin America)" => "es-419", 23 | "Spanish (United States)" => "es-US", 24 | "Basque" => "eu", 25 | "Filipino" => "fil", 26 | "French" => "fr", 27 | "French (Canada)" => "fr-CA", 28 | "Galician" => "gl", 29 | "Croatian" => "hr", 30 | "Zulu" => "zu", 31 | "Icelandic" => "is", 32 | "Italian" => "it", 33 | "Swahili" => "sw", 34 | "Latvian" => "lv", 35 | "Lithuanian" => "lt", 36 | "Hungarian" => "hu", 37 | "Dutch" => "nl", 38 | "Norwegian" => "no", 39 | "Uzbek" => "uz", 40 | "Polish" => "pl", 41 | "Portuguese (Portugal)" => "pt-PT", 42 | "Portuguese (Brazil)" => "pt", 43 | "Romanian" => "ro", 44 | "Albanian" => "sq", 45 | "Slovak" => "sk", 46 | "Slovenian" => "sl", 47 | "Serbian (Latin)" => "sr-Latn", 48 | "Finnish" => "fi", 49 | "Swedish" => "sv", 50 | "Vietnamese" => "vi", 51 | "Turkish" => "tr", 52 | "Belarusian" => "be", 53 | "Bulgarian" => "bg", 54 | "Kyrgyz" => "ky", 55 | "Kazakh" => "kk", 56 | "Macedonian" => "mk", 57 | "Mongolian" => "mn", 58 | "Russian" => "ru", 59 | "Serbian" => "sr", 60 | "Ukrainian" => "uk", 61 | "Greek" => "el", 62 | "Armenian" => "hy", 63 | "Hebrew" => "iw", 64 | "Urdu" => "ur", 65 | "Arabic" => "ar", 66 | "Persian" => "fa", 67 | "Nepali" => "ne", 68 | "Marathi" => "mr", 69 | "Hindi" => "hi", 70 | "Bangla" => "bn", 71 | "Punjabi" => "pa", 72 | "Gujarati" => "gu", 73 | "Tamil" => "ta", 74 | "Telugu" => "te", 75 | "Kannada" => "kn", 76 | "Malayalam" => "ml", 77 | "Sinhala" => "si", 78 | "Thai" => "th", 79 | "Lao" => "lo", 80 | "Myanmar (Burmese)" => "my", 81 | "Georgian" => "ka", 82 | "Amharic" => "am", 83 | "Khmer" => "km", 84 | "Chinese" => "zh-CN", 85 | "Chinese (Taiwan)" => "zh-TW", 86 | "Chinese (Hong Kong)" => "zh-HK", 87 | "Japanese" => "ja", 88 | "Korean" => "ko") 89 | ]; --------------------------------------------------------------------------------