diff options
| author | Horus3 | 2014-02-24 16:42:14 +0100 |
|---|---|---|
| committer | Horus3 | 2014-02-24 16:42:14 +0100 |
| commit | 06f945f27840b53e57795dadbc38e76f7e11ab1c (patch) | |
| tree | 689d5c7f4ffa15460c7e90f47c6a7dd59ce4e8bd /zend/demos/Zend/Gdata/YouTubeVideoApp | |
| download | random-06f945f27840b53e57795dadbc38e76f7e11ab1c.tar.gz | |
init
Diffstat (limited to 'zend/demos/Zend/Gdata/YouTubeVideoApp')
| -rw-r--r-- | zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt | 44 | ||||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/index.php | 193 | ||||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg | bin | 0 -> 1345 bytes | |||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php | 1097 | ||||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php | 66 | ||||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css | 236 | ||||
| -rwxr-xr-x | zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js | 582 |
7 files changed, 2218 insertions, 0 deletions
diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt b/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt new file mode 100644 index 0000000..3cea242 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/README.txt @@ -0,0 +1,44 @@ +== YouTube data API Video App in PHP == + +PHP sample code for the YouTube data API. Utilizes the Zend Framework +Zend_Gdata component to communicate with the YouTube data API. + +Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11 +This sample is run from within a web browser. These files are required: + +session_details.php - a script to view log output and session variables +operations.php - the main logic, which interfaces with the YouTube API +index.php - the HTML to represent the web UI, contains some PHP +video_app.css - the CSS to define the interface style +video_app.js - the JavaScript used to provide the video list AJAX interface + +-------------- + +NOTE: If using in production, some additional precautions with regards +to filtering the input data should be used. This code is designed only +for demonstration purposes. + +-------------- + +Please be sure to obtain a Developer Key from YouTube prior to using +this application by visiting this site: + +http://code.google.com/apis/youtube/dashboard/ + +More information on the YouTube Data API and Tools is available here: + +http://code.google.com/apis/youtube + +For a video explaining the basics of how this application works, please +visit this link: + +http://www.youtube.com/watch?v=iIp7OnHXBlo + +To see this application running live, please visit: + +http://googlecodesamples.com + +== UPDATES == + +3/2009 - Removed functionality to set the Developer Key in a form. Instead, + it is now hard-coded in the index.php page. This reduces complexity. diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php new file mode 100755 index 0000000..45cee26 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/index.php @@ -0,0 +1,193 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Gdata + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +/** + * PHP sample code for the YouTube data API. Utilizes the Zend Framework + * Zend_Gdata component to communicate with the YouTube data API. + * + * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11 + * This sample is run from within a web browser. These files are required: + * session_details.php - a script to view log output and session variables + * operations.php - the main logic, which interfaces with the YouTube API + * index.php - the HTML to represent the web UI, contains some PHP + * video_app.css - the CSS to define the interface style + * video_app.js - the JavaScript used to provide the video list AJAX interface + * + * NOTE: If using in production, some additional precautions with regards + * to filtering the input data should be used. This code is designed only + * for demonstration purposes. + */ +session_start(); + +/** + * Set your developer key here. + * + * NOTE: In a production application you may want to store this information in + * an external file. + */ +$_SESSION['developerKey'] = '<YOUR DEVELOPER KEY>'; + +/** + * Convert HTTP status into normal text. + * + * @param number $status HTTP status received after posting syndicated upload + * @param string $code Alphanumeric description of error + * @param string $videoId (optional) Video id received back to which the status + * code refers to + */ +function uploadStatus($status, $code = null, $videoId = null) +{ + switch ($status) { + case $status < 400: + echo 'Success ! Entry created (id: '. $videoId . + ') <a href="#" onclick=" ytVideoApp.checkUploadDetails(\''. + $videoId .'\'); ">(check details)</a>'; + break; + default: + echo 'There seems to have been an error: '. $code . + '<a href="#" onclick=" ytVideoApp.checkUploadDetails(\''. + $videoId . '\'); ">(check details)</a>'; + } +} + +/** + * Helper function to check whether a session token has been set + * + * @return boolean Returns true if a session token has been set + */ +function authenticated() +{ + if (isset($_SESSION['sessionToken'])) { + return true; + } +} + +/** + * Helper function to print a list of authenticated actions for a user. + */ +function printAuthenticatedActions() +{ + print <<<END + <div id="actions"><h3>Authenticated Actions</h3> + <ul> + <li><a href="#" onclick="ytVideoApp.listVideos('search_owner', '', 1); + return false;">retrieve my videos</a></li> + <li><a href="#" onclick="ytVideoApp.prepareUploadForm(); + return false;">upload a video</a><br /> + <div id="syndicatedUploadDiv"></div><div id="syndicatedUploadStatusDiv"> + </div></li> + <li><a href="#" onclick="ytVideoApp.retrievePlaylists(); + return false;">manage my playlists</a><br /></li> + </ul></div> +END; +} +?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> + <title>YouTube data API Video Browser in PHP</title> + <link href="video_app.css" type="text/css" rel="stylesheet" /> + <script src="video_app.js" type="text/javascript"></script> +</head> + +<body> + <div id="main"> + <div id="titleBar"> + <h2>YouTube data API Video App in PHP</h2> + <a href="session_details.php">click to examine session variables</a><br/> + <div id="searchBox"> + <form id="searchForm" onsubmit="ytVideoApp.listVideos(this.queryType.value, this.searchTerm.value, 1); return false;" action="javascript:void();" > + <div id="searchBoxTop"><select name="queryType" onchange="ytVideoApp.queryTypeChanged(this.value, this.form.searchTerm);" > + <option value="search_all" selected="selected">All Videos</option> + <option value="search_top_rated">Top Rated Videos</option> + <option value="search_most_viewed">Most Viewed Videos</option> + <option value="search_recently_featured">Recently Featured Videos</option> + <option value="search_username">Videos from a specific user</option> + <?php + if (authenticated()) { + echo '<option value="search_owner">Display my videos</option>'; + } + ?> + </select></div> + <div><input name="searchTerm" type="text" value="YouTube Data API" /> + <input type="submit" value="Search" /></div> + </form> + </div> + <br /> + + </div> + <br /> + <!-- Authentication status --> + <div id="authStatus">Authentication status: + <?php + if (authenticated()) { + print <<<END + authenticated <br /> +END; + } else { + print <<<END + <div id="generateAuthSubLink"><a href="#" + onclick="ytVideoApp.presentAuthLink(); + return false;">Click here to generate authentication link</a> + </div> +END; + } + ?> + </div> + <!-- end Authentication status --> + <br clear="all" /> + <?php + // if $_GET['status'] is populated then we have a response + // about a syndicated upload from YouTube's servers + if (isset($_GET['status'])) { + (isset($_GET['code']) ? $code = $_GET['code'] : $code = null); + (isset($_GET['id']) ? $id = $_GET['id'] : $id = null); + print '<div id="generalStatus">' . + uploadStatus($_GET['status'], $code, $id) . + '<div id="detailedUploadStatus"></div></div>'; + } + ?> + <!-- General status --> + <?php + if (authenticated()) { + printAuthenticatedActions(); + } + ?> + <!-- end General status --> + <br clear="all" /> + <div id="searchResults"> + <div id="searchResultsListColumn"> + <div id="searchResultsVideoList"></div> + <div id="searchResultsNavigation"> + <form id="navigationForm" action="javascript:void();"> + <input type="button" id="previousPageButton" onclick="ytVideoApp.listVideos(ytVideoApp.previousQueryType, ytVideoApp.previousSearchTerm, ytVideoApp.previousPage);" value="Back" style="display: none;" /> + <input type="button" id="nextPageButton" onclick="ytVideoApp.listVideos(ytVideoApp.previousQueryType, ytVideoApp.previousSearchTerm, ytVideoApp.nextPage);" value="Next" style="display: none;" /> + </form> + </div> + </div> + <div id="searchResultsVideoColumn"> + <div id="videoPlayer"></div> + </div> + </div> +</div> +</body> +</html> diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg b/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg Binary files differnew file mode 100755 index 0000000..76ec81c --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/notfound.jpg diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php new file mode 100755 index 0000000..c149583 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/operations.php @@ -0,0 +1,1097 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Gdata + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +/** + * PHP sample code for the YouTube data API. Utilizes the Zend Framework + * Zend_Gdata component to communicate with the YouTube data API. + * + * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11 + * This sample is run from within a web browser. These files are required: + * session_details.php - a script to view log output and session variables + * operations.php - the main logic, which interfaces with the YouTube API + * index.php - the HTML to represent the web UI, contains some PHP + * video_app.css - the CSS to define the interface style + * video_app.js - the JavaScript used to provide the video list AJAX interface + * + * NOTE: If using in production, some additional precautions with regards + * to filtering the input data should be used. This code is designed only + * for demonstration purposes. + */ +require_once 'Zend/Loader.php'; +Zend_Loader::loadClass('Zend_Gdata_YouTube'); +Zend_Loader::loadClass('Zend_Gdata_AuthSub'); +Zend_Loader::loadClass('Zend_Gdata_App_Exception'); + +/* + * The main controller logic. + * + * POST used for all authenticated requests + * otherwise use GET for retrieve and supplementary values + */ +session_start(); +setLogging('on'); +generateUrlInformation(); + +if (!isset($_POST['operation'])) { + // if a GET variable is set then process the token upgrade + if (isset($_GET['token'])) { + updateAuthSubToken($_GET['token']); + } else { + if (loggingEnabled()) { + logMessage('reached operations.php without $_POST or $_GET variables set', 'error'); + header('Location: index.php'); + } + } +} + +$operation = $_POST['operation']; + +switch ($operation) { + + case 'create_upload_form': + createUploadForm($_POST['videoTitle'], + $_POST['videoDescription'], + $_POST['videoCategory'], + $_POST['videoTags']); + break; + + case 'edit_meta_data': + editVideoData($_POST['newVideoTitle'], + $_POST['newVideoDescription'], + $_POST['newVideoCategory'], + $_POST['newVideoTags'], + $_POST['videoId']); + break; + + case 'check_upload_status': + checkUpload($_POST['videoId']); + break; + + case 'delete_video': + deleteVideo($_POST['videoId']); + break; + + case 'auth_sub_request': + generateAuthSubRequestLink(); + break; + + case 'auth_sub_token_upgrade': + updateAuthSubToken($_GET['token']); + break; + + case 'clear_session_var': + clearSessionVar($_POST['name']); + break; + + case 'retrieve_playlists': + retrievePlaylists(); + break; + + case 'create_playlist': + createPlaylist($_POST['playlistTitle'], $_POST['playlistDescription']); + break; + + case 'delete_playlist': + deletePlaylist($_POST['playlistTitle']); + break; + + case 'update_playlist': + updatePlaylist($_POST['newPlaylistTitle'], + $_POST['newPlaylistDescription'], + $_POST['oldPlaylistTitle']); + break; + + case (strcmp(substr($operation, 0, 7), 'search_') == 0): + // initialize search specific information + $searchType = substr($operation, 7); + searchVideos($searchType, $_POST['searchTerm'], $_POST['startIndex'], + $_POST['maxResults']); + break; + + case 'show_video': + echoVideoPlayer($_POST['videoId']); + break; + + default: + unsupportedOperation($_POST); + break; +} + +/** + * Perform a search on youtube. Passes the result feed to echoVideoList. + * + * @param string $searchType The type of search to perform. + * If set to 'owner' then attempt to authenticate. + * @param string $searchTerm The term to search on. + * @param string $startIndex Start retrieving search results from this index. + * @param string $maxResults The number of results to retrieve. + * @return void + */ +function searchVideos($searchType, $searchTerm, $startIndex, $maxResults) +{ + // create an unauthenticated service object + $youTubeService = new Zend_Gdata_YouTube(); + $query = $youTubeService->newVideoQuery(); + $query->setQuery($searchTerm); + $query->setStartIndex($startIndex); + $query->setMaxResults($maxResults); + + switch ($searchType) { + case 'most_viewed': + $query->setFeedType('most viewed'); + $query->setTime('this_week'); + $feed = $youTubeService->getVideoFeed($query); + break; + case 'most_recent': + $query->setFeedType('most recent'); + $query->setTime('this_week'); + $feed = $youTubeService->getVideoFeed($query); + break; + case 'recently_featured': + $query->setFeedType('recently featured'); + $feed = $youTubeService->getVideoFeed($query); + break; + case 'top_rated': + $query->setFeedType('top rated'); + $query->setTime('this_week'); + $feed = $youTubeService->getVideoFeed($query); + break; + case 'username': + $feed = $youTubeService->getUserUploads($searchTerm); + break; + case 'all': + $feed = $youTubeService->getVideoFeed($query); + break; + case 'owner': + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + try { + $feed = $youTubeService->getUserUploads('default'); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), + 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not retrieve users video feed: ' + . $e->getMessage() . '<br />'; + return; + } + echoVideoList($feed, true); + return; + + default: + echo 'ERROR - Unknown search type - \'' . $searchType . '\''; + return; + } + + if (loggingEnabled()) { + $httpClient = $youTubeService->getHttpClient(); + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + echoVideoList($feed); +} + +/** + * Finds the URL for the flash representation of the specified video. + * + * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry + * @return (string|null) The URL or null, if the URL is not found + */ +function findFlashUrl($entry) +{ + foreach ($entry->mediaGroup->content as $content) { + if ($content->type === 'application/x-shockwave-flash') { + return $content->url; + } + } + return null; +} + +/** + * Check the upload status of a video + * + * @param string $videoId The video to check. + * @return string A message about the video's status. + */ +function checkUpload($videoId) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + + $feed = $youTubeService->getuserUploads('default'); + $message = 'No further status information available yet.'; + + foreach($feed as $videoEntry) { + if ($videoEntry->getVideoId() == $videoId) { + // check if video is in draft status + try { + $control = $videoEntry->getControl(); + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - not able to retrieve control element ' + . $e->getMessage(); + return; + } + + if ($control instanceof Zend_Gdata_App_Extension_Control) { + if (($control->getDraft() != null) && + ($control->getDraft()->getText() == 'yes')) { + $state = $videoEntry->getVideoState(); + if ($state instanceof Zend_Gdata_YouTube_Extension_State) { + $message = 'Upload status: ' . $state->getName() . ' ' + . $state->getText(); + } else { + print $message; + } + } + } + } + } + print $message; +} + +/** + * Store location of the demo application into session variables. + * + * @return void + */ +function generateUrlInformation() +{ + if (!isset($_SESSION['operationsUrl']) || !isset($_SESSION['homeUrl'])) { + $_SESSION['operationsUrl'] = 'http://'. $_SERVER['HTTP_HOST'] + . $_SERVER['PHP_SELF']; + $path = explode('/', $_SERVER['PHP_SELF']); + $path[count($path)-1] = 'index.php'; + $_SESSION['homeUrl'] = 'http://'. $_SERVER['HTTP_HOST'] + . implode('/', $path); + } +} + +/** + * Log a message to the session variable array. + * + * @param string $message The message to log. + * @param string $messageType The type of message to log. + * @return void + */ +function logMessage($message, $messageType) +{ + if (!isset($_SESSION['log_maxLogEntries'])) { + $_SESSION['log_maxLogEntries'] = 20; + } + + if (!isset($_SESSION['log_currentCounter'])) { + $_SESSION['log_currentCounter'] = 0; + } + + $currentCounter = $_SESSION['log_currentCounter']; + $currentCounter++; + + if ($currentCounter > $_SESSION['log_maxLogEntries']) { + $_SESSION['log_currentCounter'] = 0; + } + + $logLocation = 'log_entry_'. $currentCounter . '_' . $messageType; + $_SESSION[$logLocation] = $message; + $_SESSION['log_currentCounter'] = $currentCounter; +} + +/** + * Update an existing video's meta-data. + * + * @param string $newVideoTitle The new title for the video entry. + * @param string $newVideoDescription The new description for the video entry. + * @param string $newVideoCategory The new category for the video entry. + * @param string $newVideoTags The new set of tags for the video entry (whitespace separated). + * @param string $videoId The video id for the video to be edited. + * @return void + */ +function editVideoData($newVideoTitle, $newVideoDescription, $newVideoCategory, $newVideoTags, $videoId) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getVideoFeed('https://gdata.youtube.com/feeds/users/default/uploads'); + $videoEntryToUpdate = null; + + foreach($feed as $entry) { + if ($entry->getVideoId() == $videoId) { + $videoEntryToUpdate = $entry; + break; + } + } + + if (!$videoEntryToUpdate instanceof Zend_Gdata_YouTube_VideoEntry) { + print 'ERROR - Could not find a video entry with id ' . $videoId + . '<br />' . printCacheWarning(); + return; + } + + try { + $putUrl = $videoEntryToUpdate->getEditLink()->getHref(); + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not obtain video entry\'s edit link: ' + . $e->getMessage() . '<br />'; + return; + } + + $videoEntryToUpdate->setVideoTitle($newVideoTitle); + $videoEntryToUpdate->setVideoDescription($newVideoDescription); + $videoEntryToUpdate->setVideoCategory($newVideoCategory); + + // convert tags from space separated to comma separated + $videoTagsArray = explode(' ', trim($newVideoTags)); + + // strip out empty array elements + foreach($videoTagsArray as $key => $value) { + if (strlen($value) < 2) { + unset($videoTagsArray[$key]); + } + } + + $videoEntryToUpdate->setVideoTags(implode(', ', $videoTagsArray)); + + try { + $updatedEntry = $youTubeService->updateEntry($videoEntryToUpdate, $putUrl); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not post video meta-data: ' . $e->getMessage(); + return; + } + print 'Entry updated successfully.<br /><a href="#" onclick="' + . 'ytVideoApp.presentFeed(\'search_owner\', 5, 0, \'none\'); ' + . 'ytVideoApp.refreshSearchResults();" >' + . '(refresh your video listing)</a><br />' + . printCacheWarning(); +} + +/** + * Create upload form by sending the incoming video meta-data to youtube and + * retrieving a new entry. Prints form HTML to page. + * + * @param string $VideoTitle The title for the video entry. + * @param string $VideoDescription The description for the video entry. + * @param string $VideoCategory The category for the video entry. + * @param string $VideoTags The set of tags for the video entry (whitespace separated). + * @param string $nextUrl (optional) The URL to redirect back to after form upload has completed. + * @return void + */ +function createUploadForm($videoTitle, $videoDescription, $videoCategory, $videoTags, $nextUrl = null) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $newVideoEntry = new Zend_Gdata_YouTube_VideoEntry(); + + $newVideoEntry->setVideoTitle($videoTitle); + $newVideoEntry->setVideoDescription($videoDescription); + + //make sure first character in category is capitalized + $videoCategory = strtoupper(substr($videoCategory, 0, 1)) + . substr($videoCategory, 1); + $newVideoEntry->setVideoCategory($videoCategory); + + // convert videoTags from whitespace separated into comma separated + $videoTagsArray = explode(' ', trim($videoTags)); + $newVideoEntry->setVideoTags(implode(', ', $videoTagsArray)); + + $tokenHandlerUrl = 'https://gdata.youtube.com/action/GetUploadToken'; + try { + $tokenArray = $youTubeService->getFormUploadToken($newVideoEntry, $tokenHandlerUrl); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not retrieve token for syndicated upload. ' + . $e->getMessage() + . '<br /><a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } + + $tokenValue = $tokenArray['token']; + $postUrl = $tokenArray['url']; + + // place to redirect user after upload + if (!$nextUrl) { + $nextUrl = $_SESSION['homeUrl']; + } + + print <<< END + <br /><form action="${postUrl}?nexturl=${nextUrl}" + method="post" enctype="multipart/form-data"> + <input name="file" type="file"/> + <input name="token" type="hidden" value="${tokenValue}"/> + <input value="Upload Video File" type="submit" /> + </form> +END; +} + +/** + * Deletes a Video. + * + * @param string $videoId Id of the video to be deleted. + * @return void + */ +function deleteVideo($videoId) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getVideoFeed('https://gdata.youtube.com/feeds/users/default/uploads'); + $videoEntryToDelete = null; + + foreach($feed as $entry) { + if ($entry->getVideoId() == $videoId) { + $videoEntryToDelete = $entry; + break; + } + } + + // check if videoEntryToUpdate was found + if (!$videoEntryToDelete instanceof Zend_Gdata_YouTube_VideoEntry) { + print 'ERROR - Could not find a video entry with id ' . $videoId . '<br />'; + return; + } + + try { + $httpResponse = $youTubeService->delete($videoEntryToDelete); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not delete video: '. $e->getMessage(); + return; + } + + print 'Entry deleted succesfully.<br />' . $httpResponse->getBody() + . '<br /><a href="#" onclick="' + . 'ytVideoApp.presentFeed(\'search_owner\', 5, 0, \'none\');"' + . '">(refresh your video listing)</a><br />' + . printCacheWarning(); +} + +/** + * Enables logging. + * + * @param string $loggingOption 'on' to turn logging on, 'off' to turn logging off. + * @param integer|null $maxLogItems Maximum items to log, default is 10. + * @return void + */ +function setLogging($loggingOption, $maxLogItems = 10) +{ + switch ($loggingOption) { + case 'on' : + $_SESSION['logging'] = 'on'; + $_SESSION['log_currentCounter'] = 0; + $_SESSION['log_maxLogEntries'] = $maxLogItems; + break; + + case 'off': + $_SESSION['logging'] = 'off'; + break; + } +} + +/** + * Check whether logging is enabled. + * + * @return boolean Return true if session variable for logging is set to 'on'. + */ +function loggingEnabled() +{ + if ($_SESSION['logging'] == 'on') { + return true; + } +} + +/** + * Unset a specific session variable. + * + * @param string $name Name of the session variable to delete. + * @return void + */ +function clearSessionVar($name) +{ + if (isset($_SESSION[$name])) { + unset($_SESSION[$name]); + } + header('Location: session_details.php'); +} + +/** + * Generate an AuthSub request Link and print it to the page. + * + * @param string $nextUrl URL to redirect to after performing the authentication. + * @return void + */ +function generateAuthSubRequestLink($nextUrl = null) +{ + $scope = 'https://gdata.youtube.com'; + $secure = false; + $session = true; + + if (!$nextUrl) { + generateUrlInformation(); + $nextUrl = $_SESSION['operationsUrl']; + } + + $url = Zend_Gdata_AuthSub::getAuthSubTokenUri($nextUrl, $scope, $secure, $session); + echo '<a href="' . $url + . '"><strong>Click here to authenticate with YouTube</strong></a>'; +} + +/** + * Upgrade the single-use token to a session token. + * + * @param string $singleUseToken A valid single use token that is upgradable to a session token. + * @return void + */ +function updateAuthSubToken($singleUseToken) +{ + try { + $sessionToken = Zend_Gdata_AuthSub::getAuthSubSessionToken($singleUseToken); + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Token upgrade for ' . $singleUseToken + . ' failed : ' . $e->getMessage(); + return; + } + + $_SESSION['sessionToken'] = $sessionToken; + generateUrlInformation(); + header('Location: ' . $_SESSION['homeUrl']); +} + +/** + * Convenience method to obtain an authenticted Zend_Http_Client object. + * + * @return Zend_Http_Client An authenticated client. + */ +function getAuthSubHttpClient() +{ + try { + $httpClient = Zend_Gdata_AuthSub::getHttpClient($_SESSION['sessionToken']); + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not obtain authenticated Http client object. ' + . $e->getMessage(); + return; + } + $httpClient->setHeaders('X-GData-Key', 'key='. $_SESSION['developerKey']); + return $httpClient; +} + +/** + * Echo img tags for the first thumbnail representing each video in the + * specified video feed. Upon clicking the thumbnails, the video should + * be presented. + * + * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed + * @return void + */ +function echoThumbnails($feed) +{ + foreach ($feed as $entry) { + $videoId = $entry->getVideoId(); + $firstThumbnail = htmlspecialchars( + $entry->mediaGroup->thumbnail[0]->url); + echo '<img id="' . $videoId . '" class="thumbnail" src="' + . $firstThumbnail .'" width="130" height="97" onclick="' + . 'ytVideoApp.presentVideo(\'' . $videoId . '\', 1);" ' + . 'title="click to watch: ' . + htmlspecialchars($entry->getVideoTitle()) . '" />'; + } +} + +/** + * Echo the list of videos in the specified feed. + * + * @param Zend_Gdata_YouTube_VideoFeed $feed The video feed. + * @param boolean|null $authenticated If true then the videoList will + * attempt to create additional forms to edit video meta-data. + * @return void + */ +function echoVideoList($feed, $authenticated = false) +{ + $table = '<table id="videoResultList" class="videoList"><tbody>'; + $results = 0; + + foreach ($feed as $entry) { + $videoId = $entry->getVideoId(); + $thumbnailUrl = 'notfound.jpg'; + if (count($entry->mediaGroup->thumbnail) > 0) { + $thumbnailUrl = htmlspecialchars( + $entry->mediaGroup->thumbnail[0]->url); + } + + $videoTitle = htmlspecialchars($entry->getVideoTitle()); + $videoDescription = htmlspecialchars($entry->getVideoDescription()); + $videoCategory = htmlspecialchars($entry->getVideoCategory()); + $videoTags = $entry->getVideoTags(); + + $table .= '<tr id="video_' . $videoId . '">' + . '<td width="130"><img onclick="ytVideoApp.presentVideo(\'' + . $videoId. '\')" src="' . $thumbnailUrl. '" /></td>' + . '<td><a href="#" onclick="ytVideoApp.presentVideo(\'' + . $videoId . '\')">'. stripslashes($videoTitle) . '</a>' + . '<p class="videoDescription">' + . stripslashes($videoDescription) . '</p>' + . '<p class="videoCategory">category: ' . $videoCategory + . '</p><p class="videoTags">tagged: ' + . htmlspecialchars(implode(', ', $videoTags)) . '</p>'; + + if ($authenticated) { + $table .= '<p class="edit">' + . '<a onclick="ytVideoApp.presentMetaDataEditForm(\'' + . addslashes($videoTitle) . '\', \'' + . addslashes($videoDescription) . '\', \'' + . $videoCategory . '\', \'' + . addslashes(implode(', ', $videoTags)) . '\', \'' + . $videoId . '\');" href="#">edit video data</a> | ' + . '<a href="#" onclick="ytVideoApp.confirmDeletion(\'' + . $videoId + . '\');">delete this video</a></p><br clear="all">'; + } + + $table .= '</td></tr>'; + $results++; + } + + if ($results < 1) { + echo '<br />No results found<br /><br />'; + } else { + echo $table .'</tbody></table><br />'; + } +} + +/** + * Echo the video embed code, related videos and videos owned by the same user + * as the specified videoId. + * + * @param string $videoId The video + * @return void + */ +function echoVideoPlayer($videoId) +{ + $youTubeService = new Zend_Gdata_YouTube(); + + try { + $entry = $youTubeService->getVideoEntry($videoId); + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } + + $videoTitle = htmlspecialchars($entry->getVideoTitle()); + $videoUrl = htmlspecialchars(findFlashUrl($entry)); + $relatedVideoFeed = getRelatedVideos($entry->getVideoId()); + $topRatedFeed = getTopRatedVideosByUser($entry->author[0]->name); + + print <<<END + <b>$videoTitle</b><br /> + <object width="425" height="350"> + <param name="movie" value="${videoUrl}&autoplay=1"></param> + <param name="wmode" value="transparent"></param> + <embed src="${videoUrl}&autoplay=1" type="application/x-shockwave-flash" wmode="transparent" + width="425" height="350"></embed> + </object> +END; + + echo '<br />'; + echoVideoMetadata($entry); + echo '<br /><b>Related:</b><br />'; + echoThumbnails($relatedVideoFeed); + echo '<br /><b>Top rated videos by user:</b><br />'; + echoThumbnails($topRatedFeed); +} + +/** + * Returns a feed of videos related to the specified video + * + * @param string $videoId The video + * @return Zend_Gdata_YouTube_VideoFeed The feed of related videos + */ +function getRelatedVideos($videoId) +{ + $youTubeService = new Zend_Gdata_YouTube(); + $ytQuery = $youTubeService->newVideoQuery(); + // show videos related to the specified video + $ytQuery->setFeedType('related', $videoId); + // order videos by rating + $ytQuery->setOrderBy('rating'); + // retrieve a maximum of 5 videos + $ytQuery->setMaxResults(5); + // retrieve only embeddable videos + $ytQuery->setFormat(5); + return $youTubeService->getVideoFeed($ytQuery); +} + +/** + * Returns a feed of top rated videos for the specified user + * + * @param string $user The username + * @return Zend_Gdata_YouTube_VideoFeed The feed of top rated videos + */ +function getTopRatedVideosByUser($user) +{ + $userVideosUrl = 'https://gdata.youtube.com/feeds/users/' . + $user . '/uploads'; + $youTubeService = new Zend_Gdata_YouTube(); + $ytQuery = $youTubeService->newVideoQuery($userVideosUrl); + // order by the rating of the videos + $ytQuery->setOrderBy('rating'); + // retrieve a maximum of 5 videos + $ytQuery->setMaxResults(5); + // retrieve only embeddable videos + $ytQuery->setFormat(5); + return $youTubeService->getVideoFeed($ytQuery); +} + +/** + * Echo video metadata + * + * @param Zend_Gdata_YouTube_VideoEntry $entry The video entry + * @return void + */ +function echoVideoMetadata($entry) +{ + $title = htmlspecialchars($entry->getVideoTitle()); + $description = htmlspecialchars($entry->getVideoDescription()); + $authorUsername = htmlspecialchars($entry->author[0]->name); + $authorUrl = 'http://www.youtube.com/profile?user=' . + $authorUsername; + $tags = htmlspecialchars(implode(', ', $entry->getVideoTags())); + $duration = htmlspecialchars($entry->getVideoDuration()); + $watchPage = htmlspecialchars($entry->getVideoWatchPageUrl()); + $viewCount = htmlspecialchars($entry->getVideoViewCount()); + $rating = 0; + if (isset($entry->rating->average)) { + $rating = $entry->rating->average; + } + $numRaters = 0; + if (isset($entry->rating->numRaters)) { + $numRaters = $entry->rating->numRaters; + } + $flashUrl = htmlspecialchars(findFlashUrl($entry)); + print <<<END + <b>Title:</b> ${title}<br /> + <b>Description:</b> ${description}<br /> + <b>Author:</b> <a href="${authorUrl}">${authorUsername}</a><br /> + <b>Tags:</b> ${tags}<br /> + <b>Duration:</b> ${duration} seconds<br /> + <b>View count:</b> ${viewCount}<br /> + <b>Rating:</b> ${rating} (${numRaters} ratings)<br /> + <b>Flash:</b> <a href="${flashUrl}">${flashUrl}</a><br /> + <b>Watch page:</b> <a href="${watchPage}">${watchPage}</a> <br /> +END; +} + +/** + * Print message about YouTube caching. + * + * @return string A message + */ +function printCacheWarning() +{ + return '<p class="note">' + . 'Please note that the change may not be reflected in the API ' + . 'immediately due to caching.<br/>' + . 'Please refer to the API documentation for more details.</p>'; +} + +/** + * Retrieve playlists for the currently authenticated user and print. + * @return void + */ +function retrievePlaylists() +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getPlaylistListFeed('default'); + + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + + if (!$feed instanceof Zend_Gdata_YouTube_PlaylistListFeed) { + print 'ERROR - Could not retrieve playlists<br />'. + printCacheWarning(); + return; + } + + $playlistEntries = '<ul>'; + $entriesFound = 0; + foreach($feed as $entry) { + $playlistTitle = $entry->getTitleValue(); + $playlistDescription = $entry->getDescription()->getText(); + $playlistEntries .= '<li><h3>' . $playlistTitle + . '</h3>' . $playlistDescription . ' | ' + . '<a href="#" onclick="ytVideoApp.prepareUpdatePlaylistForm(\'' + . $playlistTitle . '\', \'' . $playlistDescription + . '\'); ">update</a> | ' + . '<a href="#" onclick="ytVideoApp.confirmPlaylistDeletion(\'' + . $playlistTitle . '\');">delete</a></li>'; + $entriesFound++; + } + + $playlistEntries .= '</ul><br /><a href="#" ' + . 'onclick="ytVideoApp.prepareCreatePlaylistForm(); ' + . 'return false;">' + . 'Add new playlist</a><br />' + . '<div id="addNewPlaylist"></div>'; + + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + if ($entriesFound > 0) { + print $playlistEntries; + } else { + print 'No playlists found'; + } +} + +/** + * Create a new playlist for the currently authenticated user + * + * @param string $playlistTitle Title of the new playlist + * @param string $playlistDescription Description for the new playlist + * @return void + */ +function createPlaylist($playlistTitle, $playlistDescription) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getPlaylistListFeed('default'); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + + $newPlaylist = $youTubeService->newPlaylistListEntry(); + $newPlaylist->description = $youTubeService->newDescription()->setText($playlistDescription); + $newPlaylist->title = $youTubeService->newTitle()->setText($playlistDescription); + + if (!$feed instanceof Zend_Gdata_YouTube_PlaylistListFeed) { + print 'ERROR - Could not retrieve playlists<br />' + . printCacheWarning(); + return; + } + + $playlistFeedUrl = 'https://gdata.youtube.com/feeds/users/default/playlists'; + + try { + $updatedEntry = $youTubeService->insertEntry($newPlaylist, $playlistFeedUrl); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not create new playlist: ' . $e->getMessage(); + return; + } + + print 'Playlist added succesfully.<br /><a href="#" onclick="' + . 'ytVideoApp.retrievePlaylists();"' + . '">(refresh your playlist listing)</a><br />' + . printCacheWarning(); +} + +/** + * Delete a playlist + * + * @param string $playlistTitle Title of the playlist to be deleted + * @return void + */ +function deletePlaylist($playlistTitle) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getPlaylistListFeed('default'); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + + $playlistEntryToDelete = null; + + foreach($feed as $playlistEntry) { + if ($playlistEntry->getTitleValue() == $playlistTitle) { + $playlistEntryToDelete = $playlistEntry; + break; + } + } + + if (!$playlistEntryToDelete instanceof Zend_Gdata_YouTube_PlaylistListEntry) { + print 'ERROR - Could not retrieve playlist to be deleted<br />' + . printCacheWarning(); + return; + } + + try { + $response = $playlistEntryToDelete->delete(); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not delete the playlist: ' . $e->getMessage(); + return; + } + + print 'Playlist deleted succesfully.<br />' + . '<a href="#" onclick="ytVideoApp.retrievePlaylists();">' + . '(refresh your playlist listing)</a><br />' . printCacheWarning(); +} + +/** + * Delete a playlist + * + * @param string $newplaylistTitle New title for the playlist to be updated + * @param string $newPlaylistDescription New description for the playlist to be updated + * @param string $oldPlaylistTitle Title of the playlist to be updated + * @return void + */ +function updatePlaylist($newPlaylistTitle, $newPlaylistDescription, $oldPlaylistTitle) +{ + $httpClient = getAuthSubHttpClient(); + $youTubeService = new Zend_Gdata_YouTube($httpClient); + $feed = $youTubeService->getPlaylistListFeed('default'); + + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + + $playlistEntryToDelete = null; + + foreach($feed as $playlistEntry) { + if ($playlistEntry->getTitleValue() == $oldplaylistTitle) { + $playlistEntryToDelete = $playlistEntry; + break; + } + } + + if (!$playlistEntryToDelete instanceof Zend_Gdata_YouTube_PlaylistListEntry) { + print 'ERROR - Could not retrieve playlist to be updated<br />' + . printCacheWarning(); + return; + } + + try { + $response = $playlistEntryToDelete->delete(); + if (loggingEnabled()) { + logMessage($httpClient->getLastRequest(), 'request'); + logMessage($httpClient->getLastResponse()->getBody(), 'response'); + } + } catch (Zend_Gdata_App_HttpException $httpException) { + print 'ERROR ' . $httpException->getMessage() + . ' HTTP details<br /><textarea cols="100" rows="20">' + . $httpException->getRawResponseBody() + . '</textarea><br />' + . '<a href="session_details.php">' + . 'click here to view details of last request</a><br />'; + return; + } catch (Zend_Gdata_App_Exception $e) { + print 'ERROR - Could not delete the playlist: ' . $e->getMessage(); + return; + } + + print 'Playlist deleted succesfully.<br /><a href="#" onclick="' . + 'ytVideoApp.retrievePlaylists();"'. + '">(refresh your playlist listing)</a><br />'. + printCacheWarning(); +} + +/** + * Helper function if an unsupported operation is passed into this files main loop. + * + * @param array $post (Optional) The post variables that accompanied the operation, if available. + * @return void + */ +function unsupportedOperation($post) +{ + $message = 'ERROR An unsupported operation has been called - post variables received ' + . print_r($post, true); + + if (loggingEnabled()) { + logMessage($message, 'error'); + } + print $message; +} + +?> diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php b/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php new file mode 100755 index 0000000..dd4e996 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/session_details.php @@ -0,0 +1,66 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Gdata + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +/** + * PHP sample code for the YouTube data API. Utilizes the Zend Framework + * Zend_Gdata component to communicate with the YouTube data API. + * + * Requires the Zend Framework Zend_Gdata component and PHP >= 5.2.11 + * This sample is run from within a web browser. These files are required: + * session_details.php - a script to view log output and session variables + * operations.php - the main logic, which interfaces with the YouTube API + * index.php - the HTML to represent the web UI, contains some PHP + * video_app.css - the CSS to define the interface style + * video_app.js - the JavaScript used to provide the video list AJAX interface + * + * NOTE: If using in production, some additional precautions with regards + * to filtering the input data should be used. This code is designed only + * for demonstration purposes. + */ +session_start(); +?> +<html> +<head> + <title>YouTube data API Video Browser in PHP - Session Viewer</title> + <link href="video_app.css" type="text/css" rel="stylesheet"/> + <script src="video_app.js" type="text/javascript"></script> +</head> +<body> +<div id="mainSessions"> + <div id="titleBar"> + <div id="titleText"><h3>Session variables</h3></div><br clear="all" /> + </div> +<?php + +$session_copy = $_SESSION; +ksort($session_copy); + +foreach($session_copy as $key => $value) { + + print '<h3>'. $key . '</h3><div id="sessionVariable" >'. $value .'</div><br />'. + '<form method="POST" action="operations.php">' . + '<input type="hidden" value="clear_session_var" name="operation"/>'. + '<input type="hidden" name="name" value="'. $key .'"/>'. + '<input type="submit" value="click to delete"/></form><hr />'; +} +?> +<br clear="both" /> +<a href="index.php">back</a> + </div></body></html> diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css new file mode 100755 index 0000000..0e4a743 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.css @@ -0,0 +1,236 @@ +body { + background-color: #fff; + color: #232323; + font-family: Arial, sans-serif; + font-size: small; + margin: 8px; + margin-top: 3px; +} + +/* TODO jhartman --> swap out with css from app engine apps +*/ + + +img { + border: 0; +} + +table { + border-collapse: collapse; +} + +th, td { + padding: 0; + vertical-align: top; + text-align: left; +} + +a:link { + color: #0000cc; +} + +a:active { + color: #cc0000; +} + +a:visited { + color: #551a8b; +} + +h1 { + font-size: x-large; + margin-top: 0px; + margin-bottom: 5px; +} + +h2 { + font-size: large; +} + +h3 { + font-size: medium; +} + +h4 { + font-size: small; +} + +form { + display: inline; + margin: 0; + padding: 0; +} + +li { + margin-bottom: 0.25em; +} + +pre, code { + color: #007000; + font-family: "bogus font here", monospace; + font-size: 100%; +} + +pre { + border: 1px solid silver; + background-color: #f5f5f5; + padding: 0.5em; + overflow: auto; + margin: 2em; +} + +pre ins { + color: #cc0000; + font-weight: bold; + text-decoration: none; +} + +/* forms */ +textarea { + width: 600px; + border: 1px solid #ddd; + padding: 5px; +} + +.submit { + border: 1px solid #ddd; +} + +input, select{ + border: 1px solid #ddd; + margin-bottom: 2px; +} + +hr { + border: none; + border-bottom: 1px solid #ddd; +} + +/* "Selected" links */ +a.selected, .selected a, .selected { + color: black; + font-weight: bold; + text-decoration: none; +} + +a.selected:visited, .selected a:visited { + color: black; +} + +p.videoDescription { + margin: 0; + padding: 0; + overflow: scroll; + font-size: small; +} + +p.videoCategory { + margin: 0; + padding: 0; + /* overflow: scroll; */ + font-size: x-small; +} + +p.videoTags { + margin: 0; + padding: 0; + /* overflow: scroll; */ + font-size: x-small; +} + +p.edit { + font-size: small; +} + +.note { + padding: 2px; + background-color: yellow; + color: #000; +} + +#editForm { + font-size: small; +} + +table.videoList { + width: 100%; +} + +.videoList td { + padding: 10px 0px 5px 5px; + border-bottom: 1px solid silver; +} + +#titleBar { + border: 1px solid silver; + background-color: #e5ecf9; + margin: 0; + padding: 0; + padding-top: 5px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + margin-top: 5px; + margin-bottom: 15px; +} + +#titleText { + float: left; +} + +#searchBox { + float: right; +} + +#authStatus { + border-bottom: 1px solid #ddd; + padding: 2px; + margin-bottom: 10px; + +} + +#main { + margin: 10px; +} + +#mainSessions { + background-color: #ddd; + padding: 10px; +} + +#searchResults { + width: 100%; + background-color: silver; +} + +#searchResultsListColumn { + float: left; + width: 47%; + margin-bottom: 20px; + padding-right: 2px; +} + +#searchResultsVideoColumn { + float: right; + width: 47%; + padding-left: 5px; + border-left: 1px solid #ddd; + +} + +#sessionVariable { + font-family: Courier, monospace; + background-color: #fff; + padding: 10px; + width: 80%; + overflow: scroll; +} + +.thumbnail { + padding: 0px 0px 0px 2px; +} + +#imageLoadThumbnail { + padding: 4px; + background-color: #333; +} diff --git a/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js new file mode 100755 index 0000000..c01d806 --- /dev/null +++ b/zend/demos/Zend/Gdata/YouTubeVideoApp/video_app.js @@ -0,0 +1,582 @@ +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Gdata + * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ + +/** + * @fileoverview Provides functions for browsing and searching YouTube + * data API feeds, as well as performing authentication, syndicated uploads + * and playlist management using a PHP backend powered by the Zend_Gdata component + * of Zend Framework. + */ + +/** + * provides namespacing for the YouTube Video Application PHP version (ytVideoApp) + */ +var ytVideoApp = {}; + +/** + * maximum number of results to return for list of videos + * @type Number + */ +ytVideoApp.MAX_RESULTS_LIST = 5; + +/** + * navigation button id used to page to the previous page of + * results in the list of videos + * @type String + */ +ytVideoApp.PREVIOUS_PAGE_BUTTON = 'previousPageButton'; + +/** + * navigation button id used to page to the next page of + * results in the list of videos + * @type String + */ +ytVideoApp.NEXT_PAGE_BUTTON = 'nextPageButton'; + +/** + * container div for navigation elements + * @type String + */ +ytVideoApp.NAVIGATION_DIV = 'navigationForm'; + +/** + * container div id used to hold list of videos + * @type String + */ +ytVideoApp.VIDEO_LIST_CONTAINER_DIV = 'searchResultsVideoList'; + +/** + * container div id used to hold video search results + * @type String + */ +ytVideoApp.VIDEO_SEARCH_RESULTS_DIV = 'searchResultsVideoColumn'; + +/** + * container div id used to hold the video player + * @type String + */ +ytVideoApp.VIDEO_PLAYER_DIV = 'videoPlayer'; + +/** + * container div id used to hold the search box displayed at the top of + * the browser after one search has already been performed + * @type String + */ +ytVideoApp.TOP_SEARCH_CONTAINER_DIV = 'searchBox'; + +/** container div to show detailed upload status + * @type String + */ +ytVideoApp.VIDEO_UPLOAD_STATUS = 'detailedUploadStatus'; + +/** + * container div to hold the form for syndicated upload + * @type String + */ +ytVideoApp.SYNDICATED_UPLOAD_DIV = 'syndicatedUploadDiv'; + +/** + * container div to hold the form to edit video meta-data + * @type String + */ +ytVideoApp.VIDEO_DATA_EDIT_DIV = 'editForm'; + +/** + * containder div to hold authentication link in special cases where auth gets + * set prior to developer key + * @type String + */ +ytVideoApp.AUTHSUB_REQUEST_DIV = 'generateAuthSubLink'; + +/** + * container div to hold the form for editing video meta-data + * @type String + */ +ytVideoApp.VIDEO_META_DATA_EDIT_DIV = 'editVideoMetaDataDiv'; + +/** + * container div to hold the form for adding a new playlist + * @type String + */ +ytVideoApp.PLAYLIST_ADD_DIV = 'addNewPlaylist'; + +/** + * the page number to use for the next page navigation button + * @type Number + */ +ytVideoApp.nextPage = 2; + +/** + * the page number to use for the previous page navigation button + * @type Number + */ +ytVideoApp.previousPage = 0; + +/** + * the last search term used to query - allows for the navigation + * buttons to know what string query to perform when clicked + * @type String + */ +ytVideoApp.previousSearchTerm = ''; + +/** + * the last query type used for querying - allows for the navigation + * buttons to know what type of query to perform when clicked + * @type String + */ +ytVideoApp.previousQueryType = 'all'; + +/** + * Retrieves a list of videos matching the provided criteria. The list of + * videos can be restricted to a particular standard feed or search criteria. + * @param {String} op The type of action to be done. + * for querying all videos, or the name of a standard feed. + * @param {String} searchTerm The search term(s) to use for querying as the + * 'vq' query parameter value + * @param {Number} page The 1-based page of results to return. + */ +ytVideoApp.listVideos = function(op, searchTerm, page) { + ytVideoApp.previousSearchTerm = searchTerm; + ytVideoApp.previousQueryType = op; + var maxResults = ytVideoApp.MAX_RESULTS_LIST; + var startIndex = (((page - 1) * ytVideoApp.MAX_RESULTS_LIST) + 1); + ytVideoApp.presentFeed(op, maxResults, startIndex, searchTerm); + ytVideoApp.updateNavigation(page); +}; + +/** + * Sends an AJAX request to the server to retrieve a list of videos or + * the video player/metadata. Sends the request to the specified filePath + * on the same host, passing the specified params, and filling the specified + * resultDivName with the resutls upon success. + * @param {String} filePath The path to which the request should be sent + * @param {String} params The URL encoded POST params + * @param {String} resultDivName The name of the DIV used to hold the results + */ +ytVideoApp.sendRequest = function(filePath, params, resultDivName) { + if (window.XMLHttpRequest) { + var xmlhr = new XMLHttpRequest(); + } else { + var xmlhr = new ActiveXObject('MSXML2.XMLHTTP.3.0'); + } + + xmlhr.open('POST', filePath); + xmlhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + xmlhr.onreadystatechange = function() { + var resultDiv = document.getElementById(resultDivName); + if (xmlhr.readyState == 1) { + resultDiv.innerHTML = '<b>Loading...</b>'; + } else if (xmlhr.readyState == 4 && xmlhr.status == 200) { + if (xmlhr.responseText) { + resultDiv.innerHTML = xmlhr.responseText; + } + } else if (xmlhr.readyState == 4) { + alert('Invalid response received - Status: ' + xmlhr.status); + } + } + xmlhr.send(params); +} + +/** + * Uses ytVideoApp.sendRequest to display a YT video player and metadata for the + * specified video ID. + * @param {String} videoId The ID of the YouTube video to show + */ +ytVideoApp.presentVideo = function(videoId, updateThumbnail) { + var params = 'operation=show_video&videoId=' + videoId; + var filePath = 'operations.php'; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_PLAYER_DIV); +} + +/** + * Creates a form to enter video meta-data in preparation for syndicated upload. + */ +ytVideoApp.prepareUploadForm = function() { + var metaDataForm = ['<br clear="all"><form id="uploadForm" ', + 'onsubmit="ytVideoApp.prepareSyndicatedUpload(', + 'this.videoTitle.value, ', + 'this.videoDescription.value, ', + 'this.videoCategory.value, ', + 'this.videoTags.value); ', + 'return false;">', + 'Enter video title:<br /><input size="50" name="videoTitle" ', + 'type="text" /><br />', + 'Enter video description:<br /><textarea cols="50" ', + 'name="videoDescription"></textarea><br />', + 'Select a category: <select name="videoCategory">', + '<option value="Autos">Autos & Vehicles</option>', + '<option value="Music">Music</option>', + '<option value="Animals">Pets & Animals</option>', + '<option value="Sports">Sports</option>', + '<option value="Travel">Travel & Events</option>', + '<option value="Games">Gadgets & Games</option>', + '<option value="Comedy">Comedy</option>', + '<option value="People">People & Blogs</option>', + '<option value="News">News & Politics</option>', + '<option value="Entertainment">Entertainment</option>', + '<option value="Education">Education</option>', + '<option value="Howto">Howto & Style</option>', + '<option value="Nonprofit">Nonprofit & Activism</option>', + '<option value="Tech">Science & Technology</option>', + '</select><br />', + 'Enter some tags to describe your video ', + '<em>(separated by spaces)</em>:<br />', + '<input name="videoTags" type="text" size="50" value="video" /><br />', + '<input type="submit" value="go">', + '</form>'].join(''); + + document.getElementById(ytVideoApp.SYNDICATED_UPLOAD_DIV).innerHTML = metaDataForm; +} + +/** + * Uses ytVideoApp.sendRequest to prepare a syndicated upload. + * + * @param {String} videoTitle The title for new video + * @param {String} videoDescription The video's description + * @param {String} videoCategory The category for the video + * @param {String} videoTags A white-space separated string of Tags + */ +ytVideoApp.prepareSyndicatedUpload = function(videoTitle, videoDescription, videoCategory, videoTags) { + var filePath = 'operations.php'; + var params = 'operation=create_upload_form' + + '&videoTitle=' + videoTitle + + '&videoDescription=' + videoDescription + + '&videoCategory=' + videoCategory + + '&videoTags=' + videoTags; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.SYNDICATED_UPLOAD_DIV); +} + +/** + * Uses ytVideoApp.sendRequest to create the authSub link. + */ +ytVideoApp.presentAuthLink = function() { + var filePath = 'operations.php'; + var params = 'operation=auth_sub_request'; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.AUTHSUB_REQUEST_DIV); +} + + +/** + * Uses ytVideoApp.sendRequest to check a videos upload status. + * + * @param {String} videoId The id of the video to check + */ +ytVideoApp.checkUploadDetails = function(videoId) { + var filePath = 'operations.php'; + var params = 'operation=check_upload_status' + + '&videoId=' + videoId; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_UPLOAD_STATUS); +} + + +/** + * Creates an HTML form to edit a video's meta-data, populated with the + * videos current meta-data. + * + * @param {String} oldVideoTitle The old title of the video + * @param {String} oldVideoDescription The old description of the video + * @param {String} oldVideoCategory The old category of the video + * @param {String} oldVideoTags The old tags for the video (separated by white-space) + * @param {String} videoId The id of the video to be edited + */ +ytVideoApp.presentMetaDataEditForm = function(oldVideoTitle, oldVideoDescription, oldVideoCategory, oldVideoTags, videoId) { + // split oldVideoTags by comma and present as whitespace separated + var oldVideoTagsArray = oldVideoTags.split(','); + oldVideoTags = oldVideoTagsArray.join(' '); + var editMetaDataForm = ['<form id="editForm" ', + 'onsubmit="ytVideoApp.editMetaData(', + 'this.newVideoTitle.value, ', + 'this.newVideoDescription.value, ', + 'this.newVideoCategory.value, ', + 'this.newVideoTags.value, ', + 'this.videoId.value);', + 'return false;">', + 'Enter a new video title:<br />', + '<input size="50" name="newVideoTitle" ', + 'type="text" value="', + oldVideoTitle, + '"/><br />', + 'Enter a new video description:<br />', + '<textarea cols="50" name="newVideoDescription">', + oldVideoDescription, + '</textarea><br />', + 'Select a new category: <select ', + 'name="newVideoCategory">', + '<option value="Autos">Autos & Vehicles</option>', + '<option value="Music">Music</option>', + '<option value="Animals">Pets & Animals</option>', + '<option value="Sports">Sports</option>', + '<option value="Travel">Travel & Events</option>', + '<option value="Games">Gadgets & Games</option>', + '<option value="Comedy">Comedy</option>', + '<option value="People">People & Blogs</option>', + '<option value="News">News & Politics</option>', + '<option value="Entertainment">Entertainment</option>', + '<option value="Education">Education</option>', + '<option value="Howto">Howto & Style</option>', + '<option value="Nonprofit">Nonprofit & Activism</option>', + '<option value="Tech">Science & Technology</option>', + '</select><br />', + 'Enter some new tags to describe your video ', + '<em>(separated by spaces)</em>:<br />', + '<input name="newVideoTags" type="text" size="50" ', + 'value="', + oldVideoTags, + '"/><br />', + '<input name="videoId" type="hidden" value="', + videoId, + '" /><br />', + '<input type="submit" value="go">', + '</form>'].join(''); + + document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = editMetaDataForm; +} + +/** + * Uses ytVideoApp.sendRequest to submit updated video meta-data. + * + * @param {String} newVideoTitle The new title of the video + * @param {String} newVideoDescription The new description of the video + * @param {String} newVideoCategory The new category of the video + * @param {String} newVideoTags The new tags for the video (separated by white-space) + * @param {String} videoId The id of the video to be edited + */ +ytVideoApp.editMetaData = function(newVideoTitle, newVideoDescription, newVideoCategory, newVideoTags, videoId) { + var filePath = 'operations.php'; + var params = 'operation=edit_meta_data' + + '&newVideoTitle=' + newVideoTitle + + '&newVideoDescription=' + newVideoDescription + + '&newVideoCategory=' + newVideoCategory + + '&newVideoTags=' + newVideoTags + + '&videoId=' + videoId; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV); +}; + + +/** + * Confirms whether user wants to delete a video. + * @param {String} videoId The video Id to be deleted + */ +ytVideoApp.confirmDeletion = function(videoId) { + var answer = confirm('Do you really want to delete the video with id: ' + videoId + ' ?'); + if (answer) { + ytVideoApp.prepareDeletion(videoId); + } +} + +/** + * Uses ytVideoApp.sendRequest to request a video to be deleted. + * @param {String} videoId The video Id to be deleted + */ +ytVideoApp.prepareDeletion = function(videoId) { + var filePath = 'operations.php'; + var params = 'operation=delete_video' + + '&videoId=' + videoId; + + var table = document.getElementById('videoResultList'); + var indexOfRowToBeDeleted = -1; + var tableRows = document.getElementsByTagName('TR'); + for (var i = 0, tableRow; tableRow = tableRows[i]; i++) { + if (tableRow.id == videoId) { + indexOfRowToBeDeleted = i; + } + } + if (indexOfRowToBeDeleted > -1) { + table.deleteRow(indexOfRowToBeDeleted); + } + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV); +} + +/** + * Uses ytVideoApp.sendRequest to display a list of of YT videos. + * @param {String} op The operation to perform to retrieve a feed + * @param {Number} maxResults The maximum number of videos to list + * @param {Number} startIndex The first video to include in the list + * @param {String} searchTerm The search terms to pass to the specified feed + */ +ytVideoApp.presentFeed = function(op, maxResults, startIndex, searchTerm){ + var params = 'operation=' + op + + '&maxResults=' + maxResults + + '&startIndex=' + startIndex + + '&searchTerm=' + searchTerm; + var filePath = 'operations.php'; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV); +}; + +/** + * Updates the variables used by the navigation buttons and the 'enabled' + * status of the buttons based upon the current page number passed in. + * @param {Number} page The current page number + */ +ytVideoApp.updateNavigation = function(page) { + ytVideoApp.nextPage = page + 1; + ytVideoApp.previousPage = page - 1; + document.getElementById(ytVideoApp.NEXT_PAGE_BUTTON).style.display = 'inline'; + document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).style.display = 'inline'; + if (ytVideoApp.previousPage < 1) { + document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).disabled = true; + } else { + document.getElementById(ytVideoApp.PREVIOUS_PAGE_BUTTON).disabled = false; + } + document.getElementById(ytVideoApp.NEXT_PAGE_BUTTON).disabled = false; +}; + +/** + * Hides the navigation. + */ +ytVideoApp.hideNavigation = function() { + document.getElementById(ytVideoApp.NAVIGATION_DIV).style.display = 'none'; +}; + +/** + * Update video results div + */ +ytVideoApp.refreshSearchResults = function() { + document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = ''; +} + +/** + * Method called when the query type has been changed. Clears out the + * value of the search term input box by default if one of the standard + * feeds is selected. This is to improve usability, as many of the standard + * feeds may not include results for even fairly popular search terms. + * @param {String} op The operation to perform. + * for querying all videos, or the name of one of the standard feeds. + * @param {Node} searchTermInputElement The HTML input element for the input + * element. + */ +ytVideoApp.queryTypeChanged = function(op, searchTermInputElement) { + if (op == 'search_username') { + searchTermInputElement.value = '-- enter username --'; + } else if (op != 'search_all') { + searchTermInputElement.value = ''; + } +}; + +/** + * Create a basic HTML form to use for creating a new playlist. + */ +ytVideoApp.prepareCreatePlaylistForm = function() { + var newPlaylistForm = ['<br /><form id="addPlaylist" ', + 'onsubmit="ytVideoApp.createNewPlaylist(this.newPlaylistTitle.value, ', + 'this.newPlaylistDescription.value); ">', + 'Enter a title for the new playlist:<br />', + '<input size="50" name="newPlaylistTitle" type="text" /><br />', + 'Enter a description:<br />', + '<textarea cols="25" name="newPlaylistDescription" >', + '</textarea><br />', + '<input type="submit" value="go">', + '</form>'].join(''); + + document.getElementById(ytVideoApp.PLAYLIST_ADD_DIV).innerHTML = newPlaylistForm; +} + +/** +* Uses ytVideoApp.sendRequest to create a new playlist. +* +* @param {String} playlistTitle The title of the new playlist +* @param {String} playlistDescription A description of the new playlist +*/ +ytVideoApp.createNewPlaylist = function(playlistTitle, playlistDescription) { + var filePath = 'operations.php'; + var params = 'operation=create_playlist' + + '&playlistTitle=' + playlistTitle + + '&playlistDescription=' + playlistDescription; + ytVideoApp.hideNavigation(); + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV); +} + +/** + * Confirm user wants to delete a playlist + * + * @param {String} playlistTitle The title of the playlist to be deleted + */ +ytVideoApp.confirmPlaylistDeletion = function(playlistTitle) { + var answer = confirm('Do you really want to delete the playlist titled : ' + + playlistTitle + ' ?'); + if (answer) { + ytVideoApp.deletePlaylist(playlistTitle); + } +} + +/** +* Uses ytVideoApp.sendRequest to delete a playlist. +* +* @param {String} playlistTitle The title of the new playlist +*/ +ytVideoApp.deletePlaylist = function(playlistTitle) { + var filePath = 'operations.php'; + var params = 'operation=delete_playlist' + + '&playlistTitle=' + playlistTitle; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_SEARCH_RESULTS_DIV); +} + +/** + * Create a basic HTML form to use for modifying a playlist. + * + * @param {String} oldPlaylistTitle The old title of the playlist + * @param {String} oldPlaylistDescription The old description of the playlist + */ +ytVideoApp.prepareUpdatePlaylistForm = function(oldPlaylistTitle, oldPlaylistDescription) { + var playlistUpdateForm = ['<br /><form id="updatePlaylist" ', + 'onsubmit="ytVideoApp.updatePlaylist(this.newPlaylistTitle.value, ', + 'this.newPlaylistDescription.value, this.oldPlaylistTitle.value);">', + 'Enter a title for the new playlist:<br />', + '<input size="50" name="newPlaylistTitle" type="text" value="', + oldPlaylistTitle, + '"/><br />', + 'Enter a description:<br />', + '<textarea cols="25" name="newPlaylistDescription" >', + oldPlaylistDescription, + '</textarea><br />', + '<input type="submit" value="go" />', + '<input type="hidden" value="', + oldPlaylistTitle, + '" name="oldPlaylistTitle" />', + '</form>'].join(''); + + document.getElementById(ytVideoApp.VIDEO_SEARCH_RESULTS_DIV).innerHTML = playlistUpdateForm; +} + +/** +* Uses ytVideoApp.sendRequest to update a playlist. +* +* @param {String} newPlaylistTitle The new title of the playlist +* @param {String} newPlaylistDescription A new description of the playlist +*/ +ytVideoApp.updatePlaylist = function(newPlaylistTitle, newPlaylistDescription, oldPlaylistTitle) { + var filePath = 'operations.php'; + var params = 'operation=update_playlist' + + '&newPlaylistTitle=' + newPlaylistTitle + + '&newPlaylistDescription=' + newPlaylistDescription + + '&oldPlaylistTitle=' + oldPlaylistTitle; + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV); +} + +/** +* Uses ytVideoApp.sendRequest to retrieve a users playlist. +* +*/ +ytVideoApp.retrievePlaylists = function() { + var filePath = 'operations.php'; + var params = 'operation=retrieve_playlists'; + ytVideoApp.hideNavigation(); + ytVideoApp.sendRequest(filePath, params, ytVideoApp.VIDEO_LIST_CONTAINER_DIV); +} |
