From df651d060cf30ef287dbc3cadc75c86f907f7e99 Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Wed, 23 May 2018 16:32:49 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[add]=20cartfile=E3=81=AByoutube=20ios=20pl?= =?UTF-8?q?ayer=20helper=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cartfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Cartfile b/Cartfile index 2510155..a498951 100644 --- a/Cartfile +++ b/Cartfile @@ -6,3 +6,4 @@ github "gilesvangruisen/Swift-YouTube-Player" github "Alamofire/AlamofireImage" ~> 3.3 github "xmartlabs/XLPagerTabStrip" ~> 8.0 github "malcommac/ScrollingStackContainer" ~> 0.5 +github "youtube/youtube-ios-player-helper" From 16273e6e24c12b97eee7a62111cbe538a2f650f7 Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Wed, 23 May 2018 16:33:01 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[add]=20=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cartfile.resolved | 1 + .../youtube-ios-player-helper/.gitignore | 6 + .../youtube-ios-player-helper/CHANGELOG.md | 19 + .../youtube-ios-player-helper/CONTRIBUTING.md | 59 ++ .../Classes/YTPlayerView.h | 693 ++++++++++++++ .../Classes/YTPlayerView.m | 885 ++++++++++++++++++ .../youtube-ios-player-helper/LICENSE | 13 + .../youtube-player-ios-example/.gitignore | 23 + .../youtube-player-ios-example/Podfile | 19 + .../project.pbxproj | 569 +++++++++++ .../youtube-player-ios-example/AppDelegate.h | 19 + .../youtube-player-ios-example/AppDelegate.m | 26 + .../AppIcon.appiconset/Contents.json | 28 + .../LaunchImage.launchimage/Contents.json | 23 + .../Main.storyboard | 314 +++++++ .../PlaylistViewController.h | 29 + .../PlaylistViewController.m | 80 ++ .../SingleVideoViewController.h | 36 + .../SingleVideoViewController.m | 102 ++ .../en.lproj/InfoPlist.strings | 2 + .../youtube-player-ios-example/main.m | 23 + .../youtube-player-ios-example-Info.plist | 40 + .../youtube-player-ios-example-Prefix.pch | 16 + .../en.lproj/InfoPlist.strings | 2 + ...youtube-player-ios-exampleTests-Info.plist | 22 + .../youtube_player_ios_exampleTests.m | 415 ++++++++ .../youtube-ios-player-helper/README.md | 45 + .../youtube-ios-player-helper/Rakefile | 162 ++++ .../youtube-ios-player-helper.podspec | 54 ++ .../project.pbxproj | 295 ++++++ .../youtube-ios-player-helper.xcscheme | 80 ++ .../Assets/YTPlayerView-iframe-player.html | 90 ++ .../youtube-ios-player-helper/Info.plist | 26 + .../YouTubeiOSPlayerHelper.h | 19 + 34 files changed, 4235 insertions(+) create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/.gitignore create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/CHANGELOG.md create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/CONTRIBUTING.md create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.h create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/LICENSE create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/.gitignore create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/Podfile create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example.xcodeproj/project.pbxproj create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.h create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Main.storyboard create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.h create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.h create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/en.lproj/InfoPlist.strings create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/main.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Info.plist create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Prefix.pch create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/en.lproj/InfoPlist.strings create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube_player_ios_exampleTests.m create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/README.md create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/Rakefile create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.podspec create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/project.pbxproj create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/xcshareddata/xcschemes/youtube-ios-player-helper.xcscheme create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Assets.bundle/Assets/YTPlayerView-iframe-player.html create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Info.plist create mode 100644 Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/YouTubeiOSPlayerHelper.h diff --git a/Cartfile.resolved b/Cartfile.resolved index e77853e..3bad277 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -6,3 +6,4 @@ github "danielrhodes/Swift-ActionCableClient" "v0.2.3" github "gilesvangruisen/Swift-YouTube-Player" "v0.4.0" github "malcommac/ScrollingStackContainer" "0.5.0" github "xmartlabs/XLPagerTabStrip" "8.0.1" +github "youtube/youtube-ios-player-helper" "0.1.6" diff --git a/Carthage/Checkouts/youtube-ios-player-helper/.gitignore b/Carthage/Checkouts/youtube-ios-player-helper/.gitignore new file mode 100644 index 0000000..a38bc70 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/.gitignore @@ -0,0 +1,6 @@ +# iOS +.DS_Store +Pods/ +Podfile.lock +xcuserdata/ +*.xcworkspace/ diff --git a/Carthage/Checkouts/youtube-ios-player-helper/CHANGELOG.md b/Carthage/Checkouts/youtube-ios-player-helper/CHANGELOG.md new file mode 100644 index 0000000..179aaa8 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/CHANGELOG.md @@ -0,0 +1,19 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# YouTube-Player-iOS-Helper CHANGELOG + +## 0.1.0 + +Initial release. diff --git a/Carthage/Checkouts/youtube-ios-player-helper/CONTRIBUTING.md b/Carthage/Checkouts/youtube-ios-player-helper/CONTRIBUTING.md new file mode 100644 index 0000000..b14ed9a --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# How to contribute # + +We'd love to accept your patches and contributions to this project. There are +a just a few small guidelines you need to follow. + + +## Contributor License Agreement ## + +Contributions to any Google project must be accompanied by a Contributor +License Agreement. This is not a copyright **assignment**, it simply gives +Google permission to use and redistribute your contributions as part of the +project. + + * If you are an individual writing original source code and you're sure you + own the intellectual property, then you'll need to sign an [individual + CLA][]. + + * If you work for a company that wants to allow you to contribute your work, + then you'll need to sign a [corporate CLA][]. + +You generally only need to submit a CLA once, so if you've already submitted +one (even if it was for a different project), you probably don't need to do it +again. + +[individual CLA]: https://developers.google.com/open-source/cla/individual +[corporate CLA]: https://developers.google.com/open-source/cla/corporate + + +## Submitting a patch ## + + 1. It's generally best to start by opening a new issue describing the bug or + feature you're intending to fix. Even if you think it's relatively minor, + it's helpful to know what people are working on. Mention in the initial + issue that you are planning to work on that bug or feature so that it can + be assigned to you. + + 1. Follow the normal process of [forking][] the project, and setup a new + branch to work in. It's important that each group of changes be done in + separate branches in order to ensure that a pull request only includes the + commits related to that bug or feature. + + 1. Test your code on a device if you can. There are a bunch of edge conditions + that can be triggered in `UIWebView` where behavior in the simulator is + going to differ from behavior on a physical device. + + 1. Any significant changes should almost always be accompanied by tests. The + project already has good test coverage, so look at some of the existing + tests if you're unsure how to go about it. + + 1. Do your best to have [well-formed commit messages][] for each change. + This provides consistency throughout the project, and ensures that commit + messages are able to be formatted properly by various git tools. + + 1. Finally, push the commits to your fork and submit a [pull request][]. + +[forking]: https://help.github.com/articles/fork-a-repo +[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits +[pull request]: https://help.github.com/articles/creating-a-pull-request diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.h b/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.h new file mode 100644 index 0000000..7923c15 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.h @@ -0,0 +1,693 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class YTPlayerView; + +/** These enums represent the state of the current video in the player. */ +typedef NS_ENUM(NSInteger, YTPlayerState) { + kYTPlayerStateUnstarted, + kYTPlayerStateEnded, + kYTPlayerStatePlaying, + kYTPlayerStatePaused, + kYTPlayerStateBuffering, + kYTPlayerStateQueued, + kYTPlayerStateUnknown +}; + +/** These enums represent the resolution of the currently loaded video. */ +typedef NS_ENUM(NSInteger, YTPlaybackQuality) { + kYTPlaybackQualitySmall, + kYTPlaybackQualityMedium, + kYTPlaybackQualityLarge, + kYTPlaybackQualityHD720, + kYTPlaybackQualityHD1080, + kYTPlaybackQualityHighRes, + kYTPlaybackQualityAuto, /** Addition for YouTube Live Events. */ + kYTPlaybackQualityDefault, + kYTPlaybackQualityUnknown /** This should never be returned. It is here for future proofing. */ +}; + +/** These enums represent error codes thrown by the player. */ +typedef NS_ENUM(NSInteger, YTPlayerError) { + kYTPlayerErrorInvalidParam, + kYTPlayerErrorHTML5Error, + kYTPlayerErrorVideoNotFound, // Functionally equivalent error codes 100 and + // 105 have been collapsed into |kYTPlayerErrorVideoNotFound|. + kYTPlayerErrorNotEmbeddable, // Functionally equivalent error codes 101 and + // 150 have been collapsed into |kYTPlayerErrorNotEmbeddable|. + kYTPlayerErrorUnknown +}; + +/** + * A delegate for ViewControllers to respond to YouTube player events outside + * of the view, such as changes to video playback state or playback errors. + * The callback functions correlate to the events fired by the IFrame API. + * For the full documentation, see the IFrame documentation here: + * https://developers.google.com/youtube/iframe_api_reference#Events + */ +@protocol YTPlayerViewDelegate + +@optional +/** + * Invoked when the player view is ready to receive API calls. + * + * @param playerView The YTPlayerView instance that has become ready. + */ +- (void)playerViewDidBecomeReady:(nonnull YTPlayerView *)playerView; + +/** + * Callback invoked when player state has changed, e.g. stopped or started playback. + * + * @param playerView The YTPlayerView instance where playback state has changed. + * @param state YTPlayerState designating the new playback state. + */ +- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToState:(YTPlayerState)state; + +/** + * Callback invoked when playback quality has changed. + * + * @param playerView The YTPlayerView instance where playback quality has changed. + * @param quality YTPlaybackQuality designating the new playback quality. + */ +- (void)playerView:(nonnull YTPlayerView *)playerView didChangeToQuality:(YTPlaybackQuality)quality; + +/** + * Callback invoked when an error has occured. + * + * @param playerView The YTPlayerView instance where the error has occurred. + * @param error YTPlayerError containing the error state. + */ +- (void)playerView:(nonnull YTPlayerView *)playerView receivedError:(YTPlayerError)error; + +/** + * Callback invoked frequently when playBack is plaing. + * + * @param playerView The YTPlayerView instance where the error has occurred. + * @param playTime float containing curretn playback time. + */ +- (void)playerView:(nonnull YTPlayerView *)playerView didPlayTime:(float)playTime; + +/** + * Callback invoked when setting up the webview to allow custom colours so it fits in + * with app color schemes. If a transparent view is required specify clearColor and + * the code will handle the opacity etc. + * + * @param playerView The YTPlayerView instance where the error has occurred. + * @return A color object that represents the background color of the webview. + */ +- (nonnull UIColor *)playerViewPreferredWebViewBackgroundColor:(nonnull YTPlayerView *)playerView; + +/** + * Callback invoked when initially loading the YouTube iframe to the webview to display a custom + * loading view while the player view is not ready. This loading view will be dismissed just before + * -playerViewDidBecomeReady: callback is invoked. The loading view will be automatically resized + * to cover the entire player view. + * + * The default implementation does not display any custom loading views so the player will display + * a blank view with a background color of (-playerViewPreferredWebViewBackgroundColor:). + * + * Note that the custom loading view WILL NOT be displayed after iframe is loaded. It will be + * handled by YouTube iframe API. This callback is just intended to tell users the view is actually + * doing something while iframe is being loaded, which will take some time if users are in poor networks. + * + * @param playerView The YTPlayerView instance where the error has occurred. + * @return A view object that will be displayed while YouTube iframe API is being loaded. + * Pass nil to display no custom loading view. Default implementation returns nil. + */ +- (nullable UIView *)playerViewPreferredInitialLoadingView:(nonnull YTPlayerView *)playerView; + +@end + +/** + * YTPlayerView is a custom UIView that client developers will use to include YouTube + * videos in their iOS applications. It can be instantiated programmatically, or via + * Interface Builder. Use the methods YTPlayerView::loadWithVideoId:, + * YTPlayerView::loadWithPlaylistId: or their variants to set the video or playlist + * to populate the view with. + */ +@interface YTPlayerView : UIView + +@property(nonatomic, strong, nullable, readonly) UIWebView *webView; + +/** A delegate to be notified on playback events. */ +@property(nonatomic, weak, nullable) id delegate; + +/** + * This method loads the player with the given video ID. + * This is a convenience method for calling YTPlayerView::loadPlayerWithVideoId:withPlayerVars: + * without player variables. + * + * This method reloads the entire contents of the UIWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire UIWebView, use the + * YTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. + * + * @param videoId The YouTube video ID of the video to load in the player view. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithVideoId:(nonnull NSString *)videoId; + +/** + * This method loads the player with the given playlist ID. + * This is a convenience method for calling YTPlayerView::loadWithPlaylistId:withPlayerVars: + * without player variables. + * + * This method reloads the entire contents of the UIWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire UIWebView, use the + * YTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: + * family of methods. + * + * @param playlistId The YouTube playlist ID of the playlist to load in the player view. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId; + +/** + * This method loads the player with the given video ID and player variables. Player variables + * specify optional parameters for video playback. For instance, to play a YouTube + * video inline, the following playerVars dictionary would be used: + * + * @code + * @{ @"playsinline" : @1 }; + * @endcode + * + * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), + * both strings and integers are valid values. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * This method reloads the entire contents of the UIWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire UIWebView, use the + * YTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. + * + * @param videoId The YouTube video ID of the video to load in the player view. + * @param playerVars An NSDictionary of player parameters. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars; + +/** + * This method loads the player with the given playlist ID and player variables. Player variables + * specify optional parameters for video playback. For instance, to play a YouTube + * video inline, the following playerVars dictionary would be used: + * + * @code + * @{ @"playsinline" : @1 }; + * @endcode + * + * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), + * both strings and integers are valid values. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * This method reloads the entire contents of the UIWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire UIWebView, use the + * YTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: + * family of methods. + * + * @param playlistId The YouTube playlist ID of the playlist to load in the player view. + * @param playerVars An NSDictionary of player parameters. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId playerVars:(nullable NSDictionary *)playerVars; + +/** + * This method loads an iframe player with the given player parameters. Usually you may want to use + * -loadWithVideoId:playerVars: or -loadWithPlaylistId:playerVars: instead of this method does not handle + * video_id or playlist_id at all. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters + * to instantiate the HTML5 player with. This differs depending on + * whether a single video or playlist is being loaded. + * @return YES if successful, NO if not. + */ +- (BOOL)loadWithPlayerParams:(nullable NSDictionary *)additionalPlayerParams; + +#pragma mark - Player controls + +// These methods correspond to their JavaScript equivalents as documented here: +// https://developers.google.com/youtube/iframe_api_reference#Playback_controls + +/** + * Starts or resumes playback on the loaded video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#playVideo + */ +- (void)playVideo; + +/** + * Pauses playback on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#pauseVideo + */ +- (void)pauseVideo; + +/** + * Stops playback on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#stopVideo + */ +- (void)stopVideo; + +/** + * Seek to a given time on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#seekTo + * + * @param seekToSeconds The time in seconds to seek to in the loaded video. + * @param allowSeekAhead Whether to make a new request to the server if the time is + * outside what is currently buffered. Recommended to set to YES. + */ +- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead; + +#pragma mark - Queuing videos + +// Queueing functions for videos. These methods correspond to their JavaScript +// equivalents as documented here: +// https://developers.google.com/youtube/iframe_api_reference#Queueing_Functions + +/** + * Cues a given video by its video ID for playback starting at the given time and with the + * suggested quality. Cueing loads a video, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoById + * + * @param videoId A video ID to cue. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Cueing loads a video, but does not start video playback. This + * method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoById + * + * @param videoId A video ID to cue. + * @param startSeconds Time in seconds to start the video when playVideo() is called. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting at the given time and with the + * suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoById + * + * @param videoId A video ID to load and begin playing. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoById + * + * @param videoId A video ID to load and begin playing. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its URL on YouTube.com for playback starting at the given time + * and with the suggested quality. Cueing loads a video, but does not start video playback. + * This method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl + * + * @param videoURL URL of a YouTube video to cue for playback. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its URL on YouTube.com for playback starting at the given time + * and with the suggested quality. Cueing loads a video, but does not start video playback. + * This method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl + * + * @param videoURL URL of a YouTube video to cue for playback. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting at the given time + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl + * + * @param videoURL URL of a YouTube video to load and play. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl + * + * @param videoURL URL of a YouTube video to load and play. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +#pragma mark - Queuing functions for playlists + +// Queueing functions for playlists. These methods correspond to +// the JavaScript methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playlist_Queueing_Functions + +/** + * Cues a given playlist with the given ID. The |index| parameter specifies the 0-indexed + * position of the first video to play, starting at the given time and with the + * suggested quality. Cueing loads a playlist, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist + * + * @param playlistId Playlist ID of a YouTube playlist to cue. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cuePlaylistByPlaylistId:(nonnull NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Cues a playlist of videos with the given video IDs. The |index| parameter specifies the + * 0-indexed position of the first video to play, starting at the given time and with the + * suggested quality. Cueing loads a playlist, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist + * + * @param videoIds An NSArray of video IDs to compose the playlist of. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)cuePlaylistByVideos:(nonnull NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a given playlist with the given ID. The |index| parameter specifies the 0-indexed + * position of the first video to play, starting at the given time and with the + * suggested quality. Loading a playlist starts video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist + * + * @param playlistId Playlist ID of a YouTube playlist to cue. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadPlaylistByPlaylistId:(nonnull NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Loads a playlist of videos with the given video IDs. The |index| parameter specifies the + * 0-indexed position of the first video to play, starting at the given time and with the + * suggested quality. Loading a playlist starts video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist + * + * @param videoIds An NSArray of video IDs to compose the playlist of. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when YTPlayerView::playVideo is called. + * @param suggestedQuality YTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadPlaylistByVideos:(nonnull NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality; + +#pragma mark - Playing a video in a playlist + +// These methods correspond to the JavaScript API as defined under the +// "Playing a video in a playlist" section here: +// https://developers.google.com/youtube/iframe_api_reference#Playback_status + +/** + * Loads and plays the next video in the playlist. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#nextVideo + */ +- (void)nextVideo; + +/** + * Loads and plays the previous video in the playlist. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#previousVideo + */ +- (void)previousVideo; + +/** + * Loads and plays the video at the given 0-indexed position in the playlist. + * Corresponds to this method from the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#playVideoAt + * + * @param index The 0-indexed position of the video in the playlist to load and play. + */ +- (void)playVideoAt:(int)index; + +#pragma mark - Setting the playback rate + +/** + * Gets the playback rate. The default value is 1.0, which represents a video + * playing at normal speed. Other values may include 0.25 or 0.5 for slower + * speeds, and 1.5 or 2.0 for faster speeds. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate + * + * @return An integer value between 0 and 100 representing the current volume. + */ +- (float)playbackRate; + +/** + * Sets the playback rate. The default value is 1.0, which represents a video + * playing at normal speed. Other values may include 0.25 or 0.5 for slower + * speeds, and 1.5 or 2.0 for faster speeds. To fetch a list of valid values for + * this method, call YTPlayerView::getAvailablePlaybackRates. This method does not + * guarantee that the playback rate will change. + * This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + * + * @param suggestedRate A playback rate to suggest for the player. + */ +- (void)setPlaybackRate:(float)suggestedRate; + +/** + * Gets a list of the valid playback rates, useful in conjunction with + * YTPlayerView::setPlaybackRate. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate + * + * @return An NSArray containing available playback rates. nil if there is an error. + */ +- (nullable NSArray *)availablePlaybackRates; + +#pragma mark - Setting playback behavior for playlists + +/** + * Sets whether the player should loop back to the first video in the playlist + * after it has finished playing the last video. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#loopPlaylist + * + * @param loop A boolean representing whether the player should loop. + */ +- (void)setLoop:(BOOL)loop; + +/** + * Sets whether the player should shuffle through the playlist. This method + * corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#shufflePlaylist + * + * @param shuffle A boolean representing whether the player should + * shuffle through the playlist. + */ +- (void)setShuffle:(BOOL)shuffle; + +#pragma mark - Playback status +// These methods correspond to the JavaScript methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playback_status + +/** + * Returns a number between 0 and 1 that specifies the percentage of the video + * that the player shows as buffered. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction + * + * @return A float value between 0 and 1 representing the percentage of the video + * already loaded. + */ +- (float)videoLoadedFraction; + +/** + * Returns the state of the player. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlayerState + * + * @return |YTPlayerState| representing the state of the player. + */ +- (YTPlayerState)playerState; + +/** + * Returns the elapsed time in seconds since the video started playing. This + * method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getCurrentTime + * + * @return Time in seconds since the video started playing. + */ +- (float)currentTime; + +#pragma mark - Playback quality + +// Playback quality. These methods correspond to the JavaScript +// methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playback_quality + +/** + * Returns the playback quality. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality + * + * @return YTPlaybackQuality representing the current playback quality. + */ +- (YTPlaybackQuality)playbackQuality; + +/** + * Suggests playback quality for the video. It is recommended to leave this setting to + * |default|. This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality + * + * @param quality YTPlaybackQuality value to suggest for the player. + */ +- (void)setPlaybackQuality:(YTPlaybackQuality)suggestedQuality; + +/** + * Gets a list of the valid playback quality values, useful in conjunction with + * YTPlayerView::setPlaybackQuality. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels + * + * @return An NSArray containing available playback quality levels. Returns nil if there is an error. + */ +- (nullable NSArray *)availableQualityLevels; + +#pragma mark - Retrieving video information + +// Retrieving video information. These methods correspond to the JavaScript +// methods defined here: +// https://developers.google.com/youtube/js_api_reference#Retrieving_video_information + +/** + * Returns the duration in seconds since the video of the video. This + * method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getDuration + * + * @return Length of the video in seconds. + */ +- (NSTimeInterval)duration; + +/** + * Returns the YouTube.com URL for the video. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoUrl + * + * @return The YouTube.com URL for the video. Returns nil if no video is loaded yet. + */ +- (nullable NSURL *)videoUrl; + +/** + * Returns the embed code for the current video. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode + * + * @return The embed code for the current video. Returns nil if no video is loaded yet. + */ +- (nullable NSString *)videoEmbedCode; + +#pragma mark - Retrieving playlist information + +// Retrieving playlist information. These methods correspond to the +// JavaScript defined here: +// https://developers.google.com/youtube/js_api_reference#Retrieving_playlist_information + +/** + * Returns an ordered array of video IDs in the playlist. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaylist + * + * @return An NSArray containing all the video IDs in the current playlist. |nil| on error. + */ +- (nullable NSArray *)playlist; + +/** + * Returns the 0-based index of the currently playing item in the playlist. + * This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaylistIndex + * + * @return The 0-based index of the currently playing item in the playlist. + */ +- (int)playlistIndex; + +#pragma mark - Exposed for Testing + +/** + * Removes the internal web view from this player view. + * Intended to use for testing, should not be used in production code. + */ +- (void)removeWebView; + +@end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.m b/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.m new file mode 100644 index 0000000..b2e2df4 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Classes/YTPlayerView.m @@ -0,0 +1,885 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "YTPlayerView.h" + +// These are instances of NSString because we get them from parsing a URL. It would be silly to +// convert these into an integer just to have to convert the URL query string value into an integer +// as well for the sake of doing a value comparison. A full list of response error codes can be +// found here: +// https://developers.google.com/youtube/iframe_api_reference +NSString static *const kYTPlayerStateUnstartedCode = @"-1"; +NSString static *const kYTPlayerStateEndedCode = @"0"; +NSString static *const kYTPlayerStatePlayingCode = @"1"; +NSString static *const kYTPlayerStatePausedCode = @"2"; +NSString static *const kYTPlayerStateBufferingCode = @"3"; +NSString static *const kYTPlayerStateCuedCode = @"5"; +NSString static *const kYTPlayerStateUnknownCode = @"unknown"; + +// Constants representing playback quality. +NSString static *const kYTPlaybackQualitySmallQuality = @"small"; +NSString static *const kYTPlaybackQualityMediumQuality = @"medium"; +NSString static *const kYTPlaybackQualityLargeQuality = @"large"; +NSString static *const kYTPlaybackQualityHD720Quality = @"hd720"; +NSString static *const kYTPlaybackQualityHD1080Quality = @"hd1080"; +NSString static *const kYTPlaybackQualityHighResQuality = @"highres"; +NSString static *const kYTPlaybackQualityAutoQuality = @"auto"; +NSString static *const kYTPlaybackQualityDefaultQuality = @"default"; +NSString static *const kYTPlaybackQualityUnknownQuality = @"unknown"; + +// Constants representing YouTube player errors. +NSString static *const kYTPlayerErrorInvalidParamErrorCode = @"2"; +NSString static *const kYTPlayerErrorHTML5ErrorCode = @"5"; +NSString static *const kYTPlayerErrorVideoNotFoundErrorCode = @"100"; +NSString static *const kYTPlayerErrorNotEmbeddableErrorCode = @"101"; +NSString static *const kYTPlayerErrorCannotFindVideoErrorCode = @"105"; +NSString static *const kYTPlayerErrorSameAsNotEmbeddableErrorCode = @"150"; + +// Constants representing player callbacks. +NSString static *const kYTPlayerCallbackOnReady = @"onReady"; +NSString static *const kYTPlayerCallbackOnStateChange = @"onStateChange"; +NSString static *const kYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange"; +NSString static *const kYTPlayerCallbackOnError = @"onError"; +NSString static *const kYTPlayerCallbackOnPlayTime = @"onPlayTime"; + +NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIReady = @"onYouTubeIframeAPIReady"; +NSString static *const kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad = @"onYouTubeIframeAPIFailedToLoad"; + +NSString static *const kYTPlayerEmbedUrlRegexPattern = @"^http(s)://(www.)youtube.com/embed/(.*)$"; +NSString static *const kYTPlayerAdUrlRegexPattern = @"^http(s)://pubads.g.doubleclick.net/pagead/conversion/"; +NSString static *const kYTPlayerOAuthRegexPattern = @"^http(s)://accounts.google.com/o/oauth2/(.*)$"; +NSString static *const kYTPlayerStaticProxyRegexPattern = @"^https://content.googleapis.com/static/proxy.html(.*)$"; +NSString static *const kYTPlayerSyndicationRegexPattern = @"^https://tpc.googlesyndication.com/sodar/(.*).html$"; + +@interface YTPlayerView() + +@property (nonatomic, strong) NSURL *originURL; +@property (nonatomic, weak) UIView *initialLoadingView; + +@end + +@implementation YTPlayerView + +- (BOOL)loadWithVideoId:(NSString *)videoId { + return [self loadWithVideoId:videoId playerVars:nil]; +} + +- (BOOL)loadWithPlaylistId:(NSString *)playlistId { + return [self loadWithPlaylistId:playlistId playerVars:nil]; +} + +- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars { + if (!playerVars) { + playerVars = @{}; + } + NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars }; + return [self loadWithPlayerParams:playerParams]; +} + +- (BOOL)loadWithPlaylistId:(NSString *)playlistId playerVars:(NSDictionary *)playerVars { + + // Mutable copy because we may have been passed an immutable config dictionary. + NSMutableDictionary *tempPlayerVars = [[NSMutableDictionary alloc] init]; + [tempPlayerVars setValue:@"playlist" forKey:@"listType"]; + [tempPlayerVars setValue:playlistId forKey:@"list"]; + if (playerVars) { + [tempPlayerVars addEntriesFromDictionary:playerVars]; + } + + NSDictionary *playerParams = @{ @"playerVars" : tempPlayerVars }; + return [self loadWithPlayerParams:playerParams]; +} + +#pragma mark - Player methods + +- (void)playVideo { + [self stringFromEvaluatingJavaScript:@"player.playVideo();"]; +} + +- (void)pauseVideo { + [self notifyDelegateOfYouTubeCallbackUrl:[NSURL URLWithString:[NSString stringWithFormat:@"ytplayer://onStateChange?data=%@", kYTPlayerStatePausedCode]]]; + [self stringFromEvaluatingJavaScript:@"player.pauseVideo();"]; +} + +- (void)stopVideo { + [self stringFromEvaluatingJavaScript:@"player.stopVideo();"]; +} + +- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead { + NSNumber *secondsValue = [NSNumber numberWithFloat:seekToSeconds]; + NSString *allowSeekAheadValue = [self stringForJSBoolean:allowSeekAhead]; + NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, %@);", secondsValue, allowSeekAheadValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +#pragma mark - Cueing methods + +- (void)cueVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoById('%@', %@, '%@');", + videoId, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)cueVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});", videoId, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)loadVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoById('%@', %@, '%@');", + videoId, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)loadVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});",videoId, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)cueVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, '%@');", + videoURL, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)cueVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, %@, '%@');", + videoURL, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)loadVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, '%@');", + videoURL, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)loadVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, %@, '%@');", + videoURL, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +#pragma mark - Cueing methods for lists + +- (void)cuePlaylistByPlaylistId:(NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; + [self cuePlaylist:playlistIdString + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)cuePlaylistByVideos:(NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + [self cuePlaylist:[self stringFromVideoIdArray:videoIds] + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)loadPlaylistByPlaylistId:(NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; + [self loadPlaylist:playlistIdString + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)loadPlaylistByVideos:(NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + [self loadPlaylist:[self stringFromVideoIdArray:videoIds] + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +#pragma mark - Setting the playback rate + +- (float)playbackRate { + NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackRate();"]; + return [returnValue floatValue]; +} + +- (void)setPlaybackRate:(float)suggestedRate { + NSString *command = [NSString stringWithFormat:@"player.setPlaybackRate(%f);", suggestedRate]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (NSArray *)availablePlaybackRates { + NSString *returnValue = + [self stringFromEvaluatingJavaScript:@"player.getAvailablePlaybackRates();"]; + + NSData *playbackRateData = [returnValue dataUsingEncoding:NSUTF8StringEncoding]; + NSError *jsonDeserializationError; + NSArray *playbackRates = [NSJSONSerialization JSONObjectWithData:playbackRateData + options:kNilOptions + error:&jsonDeserializationError]; + if (jsonDeserializationError) { + return nil; + } + + return playbackRates; +} + +#pragma mark - Setting playback behavior for playlists + +- (void)setLoop:(BOOL)loop { + NSString *loopPlayListValue = [self stringForJSBoolean:loop]; + NSString *command = [NSString stringWithFormat:@"player.setLoop(%@);", loopPlayListValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +- (void)setShuffle:(BOOL)shuffle { + NSString *shufflePlayListValue = [self stringForJSBoolean:shuffle]; + NSString *command = [NSString stringWithFormat:@"player.setShuffle(%@);", shufflePlayListValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +#pragma mark - Playback status + +- (float)videoLoadedFraction { + return [[self stringFromEvaluatingJavaScript:@"player.getVideoLoadedFraction();"] floatValue]; +} + +- (YTPlayerState)playerState { + NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlayerState();"]; + return [YTPlayerView playerStateForString:returnValue]; +} + +- (float)currentTime { + return [[self stringFromEvaluatingJavaScript:@"player.getCurrentTime();"] floatValue]; +} + +// Playback quality +- (YTPlaybackQuality)playbackQuality { + NSString *qualityValue = [self stringFromEvaluatingJavaScript:@"player.getPlaybackQuality();"]; + return [YTPlayerView playbackQualityForString:qualityValue]; +} + +- (void)setPlaybackQuality:(YTPlaybackQuality)suggestedQuality { + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.setPlaybackQuality('%@');", qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +#pragma mark - Video information methods + +- (NSTimeInterval)duration { + return [[self stringFromEvaluatingJavaScript:@"player.getDuration();"] doubleValue]; +} + +- (NSURL *)videoUrl { + return [NSURL URLWithString:[self stringFromEvaluatingJavaScript:@"player.getVideoUrl();"]]; +} + +- (NSString *)videoEmbedCode { + return [self stringFromEvaluatingJavaScript:@"player.getVideoEmbedCode();"]; +} + +#pragma mark - Playlist methods + +- (NSArray *)playlist { + NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylist();"]; + + NSData *playlistData = [returnValue dataUsingEncoding:NSUTF8StringEncoding]; + NSError *jsonDeserializationError; + NSArray *videoIds = [NSJSONSerialization JSONObjectWithData:playlistData + options:kNilOptions + error:&jsonDeserializationError]; + if (jsonDeserializationError) { + return nil; + } + + return videoIds; +} + +- (int)playlistIndex { + NSString *returnValue = [self stringFromEvaluatingJavaScript:@"player.getPlaylistIndex();"]; + return [returnValue intValue]; +} + +#pragma mark - Playing a video in a playlist + +- (void)nextVideo { + [self stringFromEvaluatingJavaScript:@"player.nextVideo();"]; +} + +- (void)previousVideo { + [self stringFromEvaluatingJavaScript:@"player.previousVideo();"]; +} + +- (void)playVideoAt:(int)index { + NSString *command = + [NSString stringWithFormat:@"player.playVideoAt(%@);", [NSNumber numberWithInt:index]]; + [self stringFromEvaluatingJavaScript:command]; +} + +#pragma mark - Helper methods + +- (NSArray *)availableQualityLevels { + NSString *returnValue = + [self stringFromEvaluatingJavaScript:@"player.getAvailableQualityLevels().toString();"]; + if(!returnValue) return nil; + + NSArray *rawQualityValues = [returnValue componentsSeparatedByString:@","]; + NSMutableArray *levels = [[NSMutableArray alloc] init]; + for (NSString *rawQualityValue in rawQualityValues) { + YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:rawQualityValue]; + [levels addObject:[NSNumber numberWithInt:quality]]; + } + return levels; +} + +- (BOOL)webView:(UIWebView *)webView + shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType { + if ([request.URL.host isEqual: self.originURL.host]) { + return YES; + } else if ([request.URL.scheme isEqual:@"ytplayer"]) { + [self notifyDelegateOfYouTubeCallbackUrl:request.URL]; + return NO; + } else if ([request.URL.scheme isEqual: @"http"] || [request.URL.scheme isEqual:@"https"]) { + return [self handleHttpNavigationToUrl:request.URL]; + } + return YES; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } +} + +/** + * Convert a quality value from NSString to the typed enum value. + * + * @param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080". + * @return An enum value representing the playback quality. + */ ++ (YTPlaybackQuality)playbackQualityForString:(NSString *)qualityString { + YTPlaybackQuality quality = kYTPlaybackQualityUnknown; + + if ([qualityString isEqualToString:kYTPlaybackQualitySmallQuality]) { + quality = kYTPlaybackQualitySmall; + } else if ([qualityString isEqualToString:kYTPlaybackQualityMediumQuality]) { + quality = kYTPlaybackQualityMedium; + } else if ([qualityString isEqualToString:kYTPlaybackQualityLargeQuality]) { + quality = kYTPlaybackQualityLarge; + } else if ([qualityString isEqualToString:kYTPlaybackQualityHD720Quality]) { + quality = kYTPlaybackQualityHD720; + } else if ([qualityString isEqualToString:kYTPlaybackQualityHD1080Quality]) { + quality = kYTPlaybackQualityHD1080; + } else if ([qualityString isEqualToString:kYTPlaybackQualityHighResQuality]) { + quality = kYTPlaybackQualityHighRes; + } else if ([qualityString isEqualToString:kYTPlaybackQualityAutoQuality]) { + quality = kYTPlaybackQualityAuto; + } + + return quality; +} + +/** + * Convert a |YTPlaybackQuality| value from the typed value to NSString. + * + * @param quality A |YTPlaybackQuality| parameter. + * @return An |NSString| value to be used in the JavaScript bridge. + */ ++ (NSString *)stringForPlaybackQuality:(YTPlaybackQuality)quality { + switch (quality) { + case kYTPlaybackQualitySmall: + return kYTPlaybackQualitySmallQuality; + case kYTPlaybackQualityMedium: + return kYTPlaybackQualityMediumQuality; + case kYTPlaybackQualityLarge: + return kYTPlaybackQualityLargeQuality; + case kYTPlaybackQualityHD720: + return kYTPlaybackQualityHD720Quality; + case kYTPlaybackQualityHD1080: + return kYTPlaybackQualityHD1080Quality; + case kYTPlaybackQualityHighRes: + return kYTPlaybackQualityHighResQuality; + case kYTPlaybackQualityAuto: + return kYTPlaybackQualityAutoQuality; + default: + return kYTPlaybackQualityUnknownQuality; + } +} + +/** + * Convert a state value from NSString to the typed enum value. + * + * @param stateString A string representing player state. Ex: "-1", "0", "1". + * @return An enum value representing the player state. + */ ++ (YTPlayerState)playerStateForString:(NSString *)stateString { + YTPlayerState state = kYTPlayerStateUnknown; + if ([stateString isEqualToString:kYTPlayerStateUnstartedCode]) { + state = kYTPlayerStateUnstarted; + } else if ([stateString isEqualToString:kYTPlayerStateEndedCode]) { + state = kYTPlayerStateEnded; + } else if ([stateString isEqualToString:kYTPlayerStatePlayingCode]) { + state = kYTPlayerStatePlaying; + } else if ([stateString isEqualToString:kYTPlayerStatePausedCode]) { + state = kYTPlayerStatePaused; + } else if ([stateString isEqualToString:kYTPlayerStateBufferingCode]) { + state = kYTPlayerStateBuffering; + } else if ([stateString isEqualToString:kYTPlayerStateCuedCode]) { + state = kYTPlayerStateQueued; + } + return state; +} + +/** + * Convert a state value from the typed value to NSString. + * + * @param quality A |YTPlayerState| parameter. + * @return A string value to be used in the JavaScript bridge. + */ ++ (NSString *)stringForPlayerState:(YTPlayerState)state { + switch (state) { + case kYTPlayerStateUnstarted: + return kYTPlayerStateUnstartedCode; + case kYTPlayerStateEnded: + return kYTPlayerStateEndedCode; + case kYTPlayerStatePlaying: + return kYTPlayerStatePlayingCode; + case kYTPlayerStatePaused: + return kYTPlayerStatePausedCode; + case kYTPlayerStateBuffering: + return kYTPlayerStateBufferingCode; + case kYTPlayerStateQueued: + return kYTPlayerStateCuedCode; + default: + return kYTPlayerStateUnknownCode; + } +} + +#pragma mark - Private methods + +/** + * Private method to handle "navigation" to a callback URL of the format + * ytplayer://action?data=someData + * This is how the UIWebView communicates with the containing Objective-C code. + * Side effects of this method are that it calls methods on this class's delegate. + * + * @param url A URL of the format ytplayer://action?data=value. + */ +- (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url { + NSString *action = url.host; + + // We know the query can only be of the format ytplayer://action?data=SOMEVALUE, + // so we parse out the value. + NSString *query = url.query; + NSString *data; + if (query) { + data = [query componentsSeparatedByString:@"="][1]; + } + + if ([action isEqual:kYTPlayerCallbackOnReady]) { + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } + if ([self.delegate respondsToSelector:@selector(playerViewDidBecomeReady:)]) { + [self.delegate playerViewDidBecomeReady:self]; + } + } else if ([action isEqual:kYTPlayerCallbackOnStateChange]) { + if ([self.delegate respondsToSelector:@selector(playerView:didChangeToState:)]) { + YTPlayerState state = kYTPlayerStateUnknown; + + if ([data isEqual:kYTPlayerStateEndedCode]) { + state = kYTPlayerStateEnded; + } else if ([data isEqual:kYTPlayerStatePlayingCode]) { + state = kYTPlayerStatePlaying; + } else if ([data isEqual:kYTPlayerStatePausedCode]) { + state = kYTPlayerStatePaused; + } else if ([data isEqual:kYTPlayerStateBufferingCode]) { + state = kYTPlayerStateBuffering; + } else if ([data isEqual:kYTPlayerStateCuedCode]) { + state = kYTPlayerStateQueued; + } else if ([data isEqual:kYTPlayerStateUnstartedCode]) { + state = kYTPlayerStateUnstarted; + } + + [self.delegate playerView:self didChangeToState:state]; + } + } else if ([action isEqual:kYTPlayerCallbackOnPlaybackQualityChange]) { + if ([self.delegate respondsToSelector:@selector(playerView:didChangeToQuality:)]) { + YTPlaybackQuality quality = [YTPlayerView playbackQualityForString:data]; + [self.delegate playerView:self didChangeToQuality:quality]; + } + } else if ([action isEqual:kYTPlayerCallbackOnError]) { + if ([self.delegate respondsToSelector:@selector(playerView:receivedError:)]) { + YTPlayerError error = kYTPlayerErrorUnknown; + + if ([data isEqual:kYTPlayerErrorInvalidParamErrorCode]) { + error = kYTPlayerErrorInvalidParam; + } else if ([data isEqual:kYTPlayerErrorHTML5ErrorCode]) { + error = kYTPlayerErrorHTML5Error; + } else if ([data isEqual:kYTPlayerErrorNotEmbeddableErrorCode] || + [data isEqual:kYTPlayerErrorSameAsNotEmbeddableErrorCode]) { + error = kYTPlayerErrorNotEmbeddable; + } else if ([data isEqual:kYTPlayerErrorVideoNotFoundErrorCode] || + [data isEqual:kYTPlayerErrorCannotFindVideoErrorCode]) { + error = kYTPlayerErrorVideoNotFound; + } + + [self.delegate playerView:self receivedError:error]; + } + } else if ([action isEqualToString:kYTPlayerCallbackOnPlayTime]) { + if ([self.delegate respondsToSelector:@selector(playerView:didPlayTime:)]) { + float time = [data floatValue]; + [self.delegate playerView:self didPlayTime:time]; + } + } else if ([action isEqualToString:kYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad]) { + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } + } +} + +- (BOOL)handleHttpNavigationToUrl:(NSURL *) url { + // Usually this means the user has clicked on the YouTube logo or an error message in the + // player. Most URLs should open in the browser. The only http(s) URL that should open in this + // UIWebView is the URL for the embed, which is of the format: + // http(s)://www.youtube.com/embed/[VIDEO ID]?[PARAMETERS] + NSError *error = NULL; + NSRegularExpression *ytRegex = + [NSRegularExpression regularExpressionWithPattern:kYTPlayerEmbedUrlRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *ytMatch = + [ytRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *adRegex = + [NSRegularExpression regularExpressionWithPattern:kYTPlayerAdUrlRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *adMatch = + [adRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *syndicationRegex = + [NSRegularExpression regularExpressionWithPattern:kYTPlayerSyndicationRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + + NSTextCheckingResult *syndicationMatch = + [syndicationRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *oauthRegex = + [NSRegularExpression regularExpressionWithPattern:kYTPlayerOAuthRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *oauthMatch = + [oauthRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *staticProxyRegex = + [NSRegularExpression regularExpressionWithPattern:kYTPlayerStaticProxyRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *staticProxyMatch = + [staticProxyRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + if (ytMatch || adMatch || oauthMatch || staticProxyMatch || syndicationMatch) { + return YES; + } else { + [[UIApplication sharedApplication] openURL:url]; + return NO; + } +} + + +/** + * Private helper method to load an iframe player with the given player parameters. + * + * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters + * to instantiate the HTML5 player with. This differs depending on + * whether a single video or playlist is being loaded. + * @return YES if successful, NO if not. + */ +- (BOOL)loadWithPlayerParams:(NSDictionary *)additionalPlayerParams { + NSDictionary *playerCallbacks = @{ + @"onReady" : @"onReady", + @"onStateChange" : @"onStateChange", + @"onPlaybackQualityChange" : @"onPlaybackQualityChange", + @"onError" : @"onPlayerError" + }; + NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init]; + if (additionalPlayerParams) { + [playerParams addEntriesFromDictionary:additionalPlayerParams]; + } + if (![playerParams objectForKey:@"height"]) { + [playerParams setValue:@"100%" forKey:@"height"]; + } + if (![playerParams objectForKey:@"width"]) { + [playerParams setValue:@"100%" forKey:@"width"]; + } + + [playerParams setValue:playerCallbacks forKey:@"events"]; + + if ([playerParams objectForKey:@"playerVars"]) { + NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init]; + [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]]; + + if (![playerVars objectForKey:@"origin"]) { + self.originURL = [NSURL URLWithString:@"about:blank"]; + } else { + self.originURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]]; + } + } else { + // This must not be empty so we can render a '{}' in the output JSON + [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"]; + } + + // Remove the existing webView to reset any state + [self.webView removeFromSuperview]; + _webView = [self createNewWebView]; + [self addSubview:self.webView]; + + NSError *error = nil; + NSString *path = [[NSBundle bundleForClass:[YTPlayerView class]] pathForResource:@"YTPlayerView-iframe-player" + ofType:@"html" + inDirectory:@"Assets"]; + + // in case of using Swift and embedded frameworks, resources included not in main bundle, + // but in framework bundle + if (!path) { + path = [[[self class] frameworkBundle] pathForResource:@"YTPlayerView-iframe-player" + ofType:@"html" + inDirectory:@"Assets"]; + } + + NSString *embedHTMLTemplate = + [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; + + if (error) { + NSLog(@"Received error rendering template: %@", error); + return NO; + } + + // Render the playerVars as a JSON dictionary. + NSError *jsonRenderingError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams + options:NSJSONWritingPrettyPrinted + error:&jsonRenderingError]; + if (jsonRenderingError) { + NSLog(@"Attempted configuration of player with invalid playerVars: %@ \tError: %@", + playerParams, + jsonRenderingError); + return NO; + } + + NSString *playerVarsJsonString = + [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString]; + [self.webView loadHTMLString:embedHTML baseURL: self.originURL]; + [self.webView setDelegate:self]; + self.webView.allowsInlineMediaPlayback = YES; + self.webView.mediaPlaybackRequiresUserAction = NO; + + if ([self.delegate respondsToSelector:@selector(playerViewPreferredInitialLoadingView:)]) { + UIView *initialLoadingView = [self.delegate playerViewPreferredInitialLoadingView:self]; + if (initialLoadingView) { + initialLoadingView.frame = self.bounds; + initialLoadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:initialLoadingView]; + self.initialLoadingView = initialLoadingView; + } + } + + return YES; +} + +/** + * Private method for cueing both cases of playlist ID and array of video IDs. Cueing + * a playlist does not start playback. + * + * @param cueingString A JavaScript string representing an array, playlist ID or list of + * video IDs to play with the playlist player. + * @param index 0-index position of video to start playback on. + * @param startSeconds Seconds after start of video to begin playback. + * @param suggestedQuality Suggested YTPlaybackQuality to play the videos. + * @return The result of cueing the playlist. + */ +- (void)cuePlaylist:(NSString *)cueingString + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *indexValue = [NSNumber numberWithInt:index]; + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cuePlaylist(%@, %@, %@, '%@');", + cueingString, indexValue, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +/** + * Private method for loading both cases of playlist ID and array of video IDs. Loading + * a playlist automatically starts playback. + * + * @param cueingString A JavaScript string representing an array, playlist ID or list of + * video IDs to play with the playlist player. + * @param index 0-index position of video to start playback on. + * @param startSeconds Seconds after start of video to begin playback. + * @param suggestedQuality Suggested YTPlaybackQuality to play the videos. + * @return The result of cueing the playlist. + */ +- (void)loadPlaylist:(NSString *)cueingString + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(YTPlaybackQuality)suggestedQuality { + NSNumber *indexValue = [NSNumber numberWithInt:index]; + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [YTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadPlaylist(%@, %@, %@, '%@');", + cueingString, indexValue, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command]; +} + +/** + * Private helper method for converting an NSArray of video IDs into its JavaScript equivalent. + * + * @param videoIds An array of video ID strings to convert into JavaScript format. + * @return A JavaScript array in String format containing video IDs. + */ +- (NSString *)stringFromVideoIdArray:(NSArray *)videoIds { + NSMutableArray *formattedVideoIds = [[NSMutableArray alloc] init]; + + for (id unformattedId in videoIds) { + [formattedVideoIds addObject:[NSString stringWithFormat:@"'%@'", unformattedId]]; + } + + return [NSString stringWithFormat:@"[%@]", [formattedVideoIds componentsJoinedByString:@", "]]; +} + +/** + * Private method for evaluating JavaScript in the WebView. + * + * @param jsToExecute The JavaScript code in string format that we want to execute. + * @return JavaScript response from evaluating code. + */ +- (NSString *)stringFromEvaluatingJavaScript:(NSString *)jsToExecute { + return [self.webView stringByEvaluatingJavaScriptFromString:jsToExecute]; +} + +/** + * Private method to convert a Objective-C BOOL value to JS boolean value. + * + * @param boolValue Objective-C BOOL value. + * @return JavaScript Boolean value, i.e. "true" or "false". + */ +- (NSString *)stringForJSBoolean:(BOOL)boolValue { + return boolValue ? @"true" : @"false"; +} + +#pragma mark - Exposed for Testing + +- (void)setWebView:(UIWebView *)webView { + _webView = webView; +} + +- (UIWebView *)createNewWebView { + UIWebView *webView = [[UIWebView alloc] initWithFrame:self.bounds]; + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + webView.scrollView.scrollEnabled = NO; + webView.scrollView.bounces = NO; + + if ([self.delegate respondsToSelector:@selector(playerViewPreferredWebViewBackgroundColor:)]) { + webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self]; + if (webView.backgroundColor == [UIColor clearColor]) { + webView.opaque = NO; + } + } + + return webView; +} + +- (void)removeWebView { + [self.webView removeFromSuperview]; + self.webView = nil; +} + ++ (NSBundle *)frameworkBundle { + static NSBundle* frameworkBundle = nil; + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + NSString* mainBundlePath = [[NSBundle bundleForClass:[self class]] resourcePath]; + NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"Assets.bundle"]; + frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath]; + }); + return frameworkBundle; +} + +@end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/LICENSE b/Carthage/Checkouts/youtube-ios-player-helper/LICENSE new file mode 100644 index 0000000..bfdf05f --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/.gitignore b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/.gitignore new file mode 100644 index 0000000..16703a6 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/.gitignore @@ -0,0 +1,23 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa + +# CocoaPods +Pods \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/Podfile b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/Podfile new file mode 100644 index 0000000..de92228 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/Podfile @@ -0,0 +1,19 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +pod "youtube-ios-player-helper", :path => "../../youtube-ios-player-helper.podspec" + +target "youtube-player-ios-exampleTests", :exclusive => true do + pod "OCMock", "3.1.2" +end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example.xcodeproj/project.pbxproj b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..28a361e --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example.xcodeproj/project.pbxproj @@ -0,0 +1,569 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 4D6993E218E22E0C0073680F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D6993E118E22E0C0073680F /* Foundation.framework */; }; + 4D6993E418E22E0C0073680F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D6993E318E22E0C0073680F /* CoreGraphics.framework */; }; + 4D6993E618E22E0C0073680F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D6993E518E22E0C0073680F /* UIKit.framework */; }; + 4D6993EC18E22E0C0073680F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4D6993EA18E22E0C0073680F /* InfoPlist.strings */; }; + 4D6993FA18E22E0C0073680F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4D6993F918E22E0C0073680F /* Images.xcassets */; }; + 4D69940118E22E0C0073680F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D69940018E22E0C0073680F /* XCTest.framework */; }; + 4D69940218E22E0C0073680F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D6993E118E22E0C0073680F /* Foundation.framework */; }; + 4D69940318E22E0C0073680F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D6993E518E22E0C0073680F /* UIKit.framework */; }; + 4D69940B18E22E0C0073680F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4D69940918E22E0C0073680F /* InfoPlist.strings */; }; + 4D69940D18E22E0C0073680F /* youtube_player_ios_exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D69940C18E22E0C0073680F /* youtube_player_ios_exampleTests.m */; }; + 4D69941D18E22EE10073680F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D69941718E22EE10073680F /* AppDelegate.m */; }; + 4D69941E18E22EE10073680F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4D69941818E22EE10073680F /* Main.storyboard */; }; + 4D69941F18E22EE10073680F /* SingleVideoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D69941A18E22EE10073680F /* SingleVideoViewController.m */; }; + 4D69942018E22EE10073680F /* PlaylistViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D69941C18E22EE10073680F /* PlaylistViewController.m */; }; + 4D69942218E22F000073680F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D69942118E22F000073680F /* main.m */; }; + A1667DB27CF64FC392012064 /* libPods-youtube-player-ios-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 97FE8C7A7A914485BBD9A3D4 /* libPods-youtube-player-ios-exampleTests.a */; }; + A732EFB889AD44979951554A /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 614CB957CD094425884C83E1 /* libPods.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4D69940418E22E0C0073680F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4D6993D618E22E0C0073680F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4D6993DD18E22E0C0073680F; + remoteInfo = "youtube-player-ios-example"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 41DE6A7D49F386BF08C18468 /* Pods-youtube-player-ios-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-youtube-player-ios-exampleTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-youtube-player-ios-exampleTests/Pods-youtube-player-ios-exampleTests.release.xcconfig"; sourceTree = ""; }; + 4D6993DE18E22E0C0073680F /* youtube-player-ios-example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "youtube-player-ios-example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4D6993E118E22E0C0073680F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 4D6993E318E22E0C0073680F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 4D6993E518E22E0C0073680F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 4D6993E918E22E0C0073680F /* youtube-player-ios-example-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "youtube-player-ios-example-Info.plist"; sourceTree = ""; }; + 4D6993EB18E22E0C0073680F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 4D6993EF18E22E0C0073680F /* youtube-player-ios-example-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "youtube-player-ios-example-Prefix.pch"; sourceTree = ""; }; + 4D6993F918E22E0C0073680F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 4D6993FF18E22E0C0073680F /* youtube-player-ios-exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "youtube-player-ios-exampleTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4D69940018E22E0C0073680F /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + 4D69940818E22E0C0073680F /* youtube-player-ios-exampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "youtube-player-ios-exampleTests-Info.plist"; sourceTree = ""; }; + 4D69940A18E22E0C0073680F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 4D69940C18E22E0C0073680F /* youtube_player_ios_exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = youtube_player_ios_exampleTests.m; sourceTree = ""; }; + 4D69941618E22EE10073680F /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 4D69941718E22EE10073680F /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 4D69941818E22EE10073680F /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 4D69941918E22EE10073680F /* SingleVideoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SingleVideoViewController.h; sourceTree = ""; }; + 4D69941A18E22EE10073680F /* SingleVideoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SingleVideoViewController.m; sourceTree = ""; }; + 4D69941B18E22EE10073680F /* PlaylistViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaylistViewController.h; sourceTree = ""; }; + 4D69941C18E22EE10073680F /* PlaylistViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaylistViewController.m; sourceTree = ""; }; + 4D69942118E22F000073680F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5EDBAF28CE586F953081FFD5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 614CB957CD094425884C83E1 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 97FE8C7A7A914485BBD9A3D4 /* libPods-youtube-player-ios-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-youtube-player-ios-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9F1A73947F21B4AC48269A4E /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + A33CC11F5E5B159D09ADCB5D /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + C9EAC06935E4E004160720A4 /* Pods-youtube-player-ios-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-youtube-player-ios-exampleTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-youtube-player-ios-exampleTests/Pods-youtube-player-ios-exampleTests.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4D6993DB18E22E0C0073680F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D6993E418E22E0C0073680F /* CoreGraphics.framework in Frameworks */, + 4D6993E618E22E0C0073680F /* UIKit.framework in Frameworks */, + 4D6993E218E22E0C0073680F /* Foundation.framework in Frameworks */, + A732EFB889AD44979951554A /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4D6993FC18E22E0C0073680F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D69940118E22E0C0073680F /* XCTest.framework in Frameworks */, + 4D69940318E22E0C0073680F /* UIKit.framework in Frameworks */, + 4D69940218E22E0C0073680F /* Foundation.framework in Frameworks */, + A1667DB27CF64FC392012064 /* libPods-youtube-player-ios-exampleTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4D6993D518E22E0C0073680F = { + isa = PBXGroup; + children = ( + 4D6993E718E22E0C0073680F /* youtube-player-ios-example */, + 4D69940618E22E0C0073680F /* youtube-player-ios-exampleTests */, + 4D6993E018E22E0C0073680F /* Frameworks */, + 4D6993DF18E22E0C0073680F /* Products */, + 686E9A838DE037A32431FD61 /* Pods */, + ); + sourceTree = ""; + }; + 4D6993DF18E22E0C0073680F /* Products */ = { + isa = PBXGroup; + children = ( + 4D6993DE18E22E0C0073680F /* youtube-player-ios-example.app */, + 4D6993FF18E22E0C0073680F /* youtube-player-ios-exampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4D6993E018E22E0C0073680F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4D6993E118E22E0C0073680F /* Foundation.framework */, + 4D6993E318E22E0C0073680F /* CoreGraphics.framework */, + 4D6993E518E22E0C0073680F /* UIKit.framework */, + 4D69940018E22E0C0073680F /* XCTest.framework */, + 614CB957CD094425884C83E1 /* libPods.a */, + 97FE8C7A7A914485BBD9A3D4 /* libPods-youtube-player-ios-exampleTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 4D6993E718E22E0C0073680F /* youtube-player-ios-example */ = { + isa = PBXGroup; + children = ( + 4D69941618E22EE10073680F /* AppDelegate.h */, + 4D69941718E22EE10073680F /* AppDelegate.m */, + 4D69941818E22EE10073680F /* Main.storyboard */, + 4D69941918E22EE10073680F /* SingleVideoViewController.h */, + 4D69941A18E22EE10073680F /* SingleVideoViewController.m */, + 4D69941B18E22EE10073680F /* PlaylistViewController.h */, + 4D69941C18E22EE10073680F /* PlaylistViewController.m */, + 4D6993F918E22E0C0073680F /* Images.xcassets */, + 4D6993E818E22E0C0073680F /* Supporting Files */, + ); + path = "youtube-player-ios-example"; + sourceTree = ""; + }; + 4D6993E818E22E0C0073680F /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 4D6993E918E22E0C0073680F /* youtube-player-ios-example-Info.plist */, + 4D6993EA18E22E0C0073680F /* InfoPlist.strings */, + 4D69942118E22F000073680F /* main.m */, + 4D6993EF18E22E0C0073680F /* youtube-player-ios-example-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 4D69940618E22E0C0073680F /* youtube-player-ios-exampleTests */ = { + isa = PBXGroup; + children = ( + 4D69940C18E22E0C0073680F /* youtube_player_ios_exampleTests.m */, + 4D69940718E22E0C0073680F /* Supporting Files */, + ); + path = "youtube-player-ios-exampleTests"; + sourceTree = ""; + }; + 4D69940718E22E0C0073680F /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 4D69940818E22E0C0073680F /* youtube-player-ios-exampleTests-Info.plist */, + 4D69940918E22E0C0073680F /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 686E9A838DE037A32431FD61 /* Pods */ = { + isa = PBXGroup; + children = ( + 9F1A73947F21B4AC48269A4E /* Pods.debug.xcconfig */, + A33CC11F5E5B159D09ADCB5D /* Pods.release.xcconfig */, + C9EAC06935E4E004160720A4 /* Pods-youtube-player-ios-exampleTests.debug.xcconfig */, + 41DE6A7D49F386BF08C18468 /* Pods-youtube-player-ios-exampleTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4D6993DD18E22E0C0073680F /* youtube-player-ios-example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4D69941018E22E0C0073680F /* Build configuration list for PBXNativeTarget "youtube-player-ios-example" */; + buildPhases = ( + FE80856E09B8459AAE18655D /* Check Pods Manifest.lock */, + 4D6993DA18E22E0C0073680F /* Sources */, + 4D6993DB18E22E0C0073680F /* Frameworks */, + 4D6993DC18E22E0C0073680F /* Resources */, + 8D2FF9AA090848A181BEBD44 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "youtube-player-ios-example"; + productName = "youtube-player-ios-example"; + productReference = 4D6993DE18E22E0C0073680F /* youtube-player-ios-example.app */; + productType = "com.apple.product-type.application"; + }; + 4D6993FE18E22E0C0073680F /* youtube-player-ios-exampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4D69941318E22E0C0073680F /* Build configuration list for PBXNativeTarget "youtube-player-ios-exampleTests" */; + buildPhases = ( + 40A67310F9C54613B3075C8C /* Check Pods Manifest.lock */, + 4D6993FB18E22E0C0073680F /* Sources */, + 4D6993FC18E22E0C0073680F /* Frameworks */, + 4D6993FD18E22E0C0073680F /* Resources */, + B16AF54440614B7BBEA8906E /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4D69940518E22E0C0073680F /* PBXTargetDependency */, + ); + name = "youtube-player-ios-exampleTests"; + productName = "youtube-player-ios-exampleTests"; + productReference = 4D6993FF18E22E0C0073680F /* youtube-player-ios-exampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4D6993D618E22E0C0073680F /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + ORGANIZATIONNAME = "YouTube Developer Relations"; + TargetAttributes = { + 4D6993FE18E22E0C0073680F = { + TestTargetID = 4D6993DD18E22E0C0073680F; + }; + }; + }; + buildConfigurationList = 4D6993D918E22E0C0073680F /* Build configuration list for PBXProject "youtube-player-ios-example" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 4D6993D518E22E0C0073680F; + productRefGroup = 4D6993DF18E22E0C0073680F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4D6993DD18E22E0C0073680F /* youtube-player-ios-example */, + 4D6993FE18E22E0C0073680F /* youtube-player-ios-exampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4D6993DC18E22E0C0073680F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D69941E18E22EE10073680F /* Main.storyboard in Resources */, + 4D6993FA18E22E0C0073680F /* Images.xcassets in Resources */, + 4D6993EC18E22E0C0073680F /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4D6993FD18E22E0C0073680F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D69940B18E22E0C0073680F /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 40A67310F9C54613B3075C8C /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 8D2FF9AA090848A181BEBD44 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + B16AF54440614B7BBEA8906E /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-youtube-player-ios-exampleTests/Pods-youtube-player-ios-exampleTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + FE80856E09B8459AAE18655D /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4D6993DA18E22E0C0073680F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D69942218E22F000073680F /* main.m in Sources */, + 4D69942018E22EE10073680F /* PlaylistViewController.m in Sources */, + 4D69941D18E22EE10073680F /* AppDelegate.m in Sources */, + 4D69941F18E22EE10073680F /* SingleVideoViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4D6993FB18E22E0C0073680F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4D69940D18E22E0C0073680F /* youtube_player_ios_exampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4D69940518E22E0C0073680F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4D6993DD18E22E0C0073680F /* youtube-player-ios-example */; + targetProxy = 4D69940418E22E0C0073680F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 4D6993EA18E22E0C0073680F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 4D6993EB18E22E0C0073680F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 4D69940918E22E0C0073680F /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 4D69940A18E22E0C0073680F /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4D69940E18E22E0C0073680F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 4D69940F18E22E0C0073680F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4D69941118E22E0C0073680F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9F1A73947F21B4AC48269A4E /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "youtube-player-ios-example/youtube-player-ios-example-Prefix.pch"; + INFOPLIST_FILE = "youtube-player-ios-example/youtube-player-ios-example-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 4D69941218E22E0C0073680F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A33CC11F5E5B159D09ADCB5D /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "youtube-player-ios-example/youtube-player-ios-example-Prefix.pch"; + INFOPLIST_FILE = "youtube-player-ios-example/youtube-player-ios-example-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + 4D69941418E22E0C0073680F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C9EAC06935E4E004160720A4 /* Pods-youtube-player-ios-exampleTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/youtube-player-ios-example.app/youtube-player-ios-example"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "youtube-player-ios-example/youtube-player-ios-example-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + 4D69941518E22E0C0073680F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 41DE6A7D49F386BF08C18468 /* Pods-youtube-player-ios-exampleTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/youtube-player-ios-example.app/youtube-player-ios-example"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "youtube-player-ios-example/youtube-player-ios-example-Prefix.pch"; + INFOPLIST_FILE = "youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4D6993D918E22E0C0073680F /* Build configuration list for PBXProject "youtube-player-ios-example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4D69940E18E22E0C0073680F /* Debug */, + 4D69940F18E22E0C0073680F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4D69941018E22E0C0073680F /* Build configuration list for PBXNativeTarget "youtube-player-ios-example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4D69941118E22E0C0073680F /* Debug */, + 4D69941218E22E0C0073680F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4D69941318E22E0C0073680F /* Build configuration list for PBXNativeTarget "youtube-player-ios-exampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4D69941418E22E0C0073680F /* Debug */, + 4D69941518E22E0C0073680F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4D6993D618E22E0C0073680F /* Project object */; +} diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.h b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.h new file mode 100644 index 0000000..032bd05 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.h @@ -0,0 +1,19 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@interface AppDelegate : UIResponder + +@end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.m b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.m new file mode 100644 index 0000000..efb6061 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/AppDelegate.m @@ -0,0 +1,26 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "AppDelegate.h" + +@implementation AppDelegate + +@synthesize window; + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + return YES; +} + +@end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/AppIcon.appiconset/Contents.json b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..33ec0bc --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,28 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/LaunchImage.launchimage/Contents.json b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..c79ebd3 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Main.storyboard b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Main.storyboard new file mode 100644 index 0000000..3d14ab2 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/Main.storyboard @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.h b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.h new file mode 100644 index 0000000..a99a2be --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.h @@ -0,0 +1,29 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "YTPlayerView.h" + +@interface PlaylistViewController : UIViewController + +@property(nonatomic, strong) IBOutlet YTPlayerView *playerView; +@property(nonatomic, weak) IBOutlet UIButton *playButton; +@property(nonatomic, weak) IBOutlet UIButton *pauseButton; +@property(nonatomic, weak) IBOutlet UIButton *stopButton; +@property(nonatomic, weak) IBOutlet UIButton *nextVideoButton; +@property(nonatomic, weak) IBOutlet UIButton *previousVideoButton; +@property(nonatomic, weak) IBOutlet UITextView *statusTextView; + +- (IBAction)buttonPressed:(id)sender; + +@end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.m b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.m new file mode 100644 index 0000000..ef35a98 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/PlaylistViewController.m @@ -0,0 +1,80 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "PlaylistViewController.h" + +@implementation PlaylistViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + NSString* playlistId = @"PLhBgTdAWkxeCMHYCQ0uuLyhydRJGDRNo5"; + + // For a full list of player parameters, see the documentation for the HTML5 player + // at: https://developers.google.com/youtube/player_parameters?playerVersion=HTML5 + NSDictionary *playerVars = @{ + @"controls" : @0, + @"playsinline" : @1, + @"autohide" : @1, + @"showinfo" : @0, + @"modestbranding" : @1 + }; + self.playerView.delegate = self; + + [self.playerView loadWithPlaylistId:playlistId playerVars:playerVars]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(receivedPlaybackStartedNotification:) + name:@"Playback started" + object:nil]; +} + +- (IBAction)buttonPressed:(id)sender { + if (sender == self.playButton) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"Playback started" object:self]; + [self.playerView playVideo]; + } else if (sender == self.pauseButton) { + [self.playerView pauseVideo]; + } else if (sender == self.stopButton) { + [self.playerView stopVideo]; + } else if (sender == self.nextVideoButton) { + [self appendStatusText:@"Loading next video in playlist\n"]; + [self.playerView nextVideo]; + } else if (sender == self.previousVideoButton) { + [self appendStatusText:@"Loading previous video in playlist\n"]; + [self.playerView previousVideo]; + } +} + +- (void)receivedPlaybackStartedNotification:(NSNotification *) notification { + if([notification.name isEqual:@"Playback started"] && notification.object != self) { + [self.playerView pauseVideo]; + } +} + +/** + * Private helper method to add player status in statusTextView and scroll view automatically. + * + * @param status a string describing current player state + */ +- (void)appendStatusText:(NSString*)status { + [self.statusTextView setText:[self.statusTextView.text stringByAppendingString:status]]; + NSRange range = NSMakeRange(self.statusTextView.text.length - 1, 1); + + // To avoid dizzying scrolling on appending latest status. + self.statusTextView.scrollEnabled = NO; + [self.statusTextView scrollRangeToVisible:range]; + self.statusTextView.scrollEnabled = YES; +} + +@end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.h b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.h new file mode 100644 index 0000000..a97023b --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.h @@ -0,0 +1,36 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "YTPlayerView.h" + +@interface SingleVideoViewController : UIViewController + +@property(nonatomic, strong) IBOutlet YTPlayerView *playerView; +@property(nonatomic, weak) IBOutlet UIButton *playButton; +@property(nonatomic, weak) IBOutlet UIButton *pauseButton; +@property(nonatomic, weak) IBOutlet UIButton *stopButton; +@property(nonatomic, weak) IBOutlet UIButton *startButton; +@property(nonatomic, weak) IBOutlet UIButton *reverseButton; +@property(nonatomic, weak) IBOutlet UIButton *forwardButton; +@property(nonatomic, weak) IBOutlet UITextView *statusTextView; + +@property(nonatomic, weak) IBOutlet UISlider *slider; + +- (IBAction)onSliderChange:(id)sender; + +- (IBAction)buttonPressed:(id)sender; + +@end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.m b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.m new file mode 100644 index 0000000..1ad5aef --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/SingleVideoViewController.m @@ -0,0 +1,102 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "SingleVideoViewController.h" + +@implementation SingleVideoViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + NSString *videoId = @"M7lc1UVf-VE"; + + // For a full list of player parameters, see the documentation for the HTML5 player + // at: https://developers.google.com/youtube/player_parameters?playerVersion=HTML5 + NSDictionary *playerVars = @{ + @"controls" : @0, + @"playsinline" : @1, + @"autohide" : @1, + @"showinfo" : @0, + @"modestbranding" : @1 + }; + self.playerView.delegate = self; + [self.playerView loadWithVideoId:videoId playerVars:playerVars]; + + + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(receivedPlaybackStartedNotification:) + name:@"Playback started" + object:nil]; +} + +- (void)playerView:(YTPlayerView *)ytPlayerView didChangeToState:(YTPlayerState)state { + NSString *message = [NSString stringWithFormat:@"Player state changed: %ld\n", (long)state]; + [self appendStatusText:message]; +} + +- (void)playerView:(YTPlayerView *)playerView didPlayTime:(float)playTime { + float progress = playTime/self.playerView.duration; + [self.slider setValue:progress]; +} + +- (IBAction)onSliderChange:(id)sender { + float seekToTime = self.playerView.duration * self.slider.value; + [self.playerView seekToSeconds:seekToTime allowSeekAhead:YES]; + [self appendStatusText:[NSString stringWithFormat:@"Seeking to time: %.0f seconds\n", seekToTime]]; +} + +- (IBAction)buttonPressed:(id)sender { + if (sender == self.playButton) { + [[NSNotificationCenter defaultCenter] postNotificationName:@"Playback started" object:self]; + [self.playerView playVideo]; + } else if (sender == self.stopButton) { + [self.playerView stopVideo]; + } else if (sender == self.pauseButton) { + [self.playerView pauseVideo]; + } else if (sender == self.reverseButton) { + float seekToTime = self.playerView.currentTime - 30.0; + [self.playerView seekToSeconds:seekToTime allowSeekAhead:YES]; + [self appendStatusText:[NSString stringWithFormat:@"Seeking to time: %.0f seconds\n", seekToTime]]; + } else if (sender == self.forwardButton) { + float seekToTime = self.playerView.currentTime + 30.0; + [self.playerView seekToSeconds:seekToTime allowSeekAhead:YES]; + [self appendStatusText:[NSString stringWithFormat:@"Seeking to time: %.0f seconds\n", seekToTime]]; + } else if (sender == self.startButton) { + [self.playerView seekToSeconds:0 allowSeekAhead:YES]; + [self appendStatusText:@"Seeking to beginning\n"]; + } +} + +- (void)receivedPlaybackStartedNotification:(NSNotification *) notification { + if([notification.name isEqual:@"Playback started"] && notification.object != self) { + [self.playerView pauseVideo]; + } +} + +/** + * Private helper method to add player status in statusTextView and scroll view automatically. + * + * @param status a string describing current player state + */ +- (void)appendStatusText:(NSString *)status { + [self.statusTextView setText:[self.statusTextView.text stringByAppendingString:status]]; + NSRange range = NSMakeRange(self.statusTextView.text.length - 1, 1); + + // To avoid dizzying scrolling on appending latest status. + self.statusTextView.scrollEnabled = NO; + [self.statusTextView scrollRangeToVisible:range]; + self.statusTextView.scrollEnabled = YES; +} + +@end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/en.lproj/InfoPlist.strings b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/main.m b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/main.m new file mode 100644 index 0000000..925926a --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/main.m @@ -0,0 +1,23 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Info.plist b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Info.plist new file mode 100644 index 0000000..a9fc5fd --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.youtube.demo.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Prefix.pch b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Prefix.pch new file mode 100644 index 0000000..9f4c510 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-example/youtube-player-ios-example-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import + +#ifndef __IPHONE_5_0 +#warning "This project uses features only available in iOS SDK 5.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/en.lproj/InfoPlist.strings b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist new file mode 100644 index 0000000..676215e --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube-player-ios-exampleTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.youtube.demo.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube_player_ios_exampleTests.m b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube_player_ios_exampleTests.m new file mode 100644 index 0000000..c96b6f4 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Project/youtube-player-ios-example/youtube-player-ios-exampleTests/youtube_player_ios_exampleTests.m @@ -0,0 +1,415 @@ +// Copyright 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +#import "YTPlayerView.h" + +@interface youtube_player_ios_exampleTests : XCTestCase + +@end + +@interface YTPlayerView (ExposedForTesting) +-(void)setWebView:(UIWebView *)webView; +-(UIWebView *) createNewWebView; +@end + +// This version makes a small change to createNewWebView to not create a new UIWebView. Instead, +// it just reuses the existing UIWebView. This makes it easier for us to inject a mock UIWebView. +@interface YTPlayerViewTestingVersion : YTPlayerView +@end + +@implementation YTPlayerViewTestingVersion + +/** + * An overridden method to make injecting UIWebView easier. This version is only used in the + * YTPlayerView::loadWithVideoId: and YTPlayerView::loadWithPlaylistId: methods. + * + * @return The same UIWebView that the YTPlayerView already has. + */ +- (UIWebView *) createNewWebView { + return self.webView; +} + +@end + +@implementation youtube_player_ios_exampleTests { + YTPlayerView *playerView; + id mockWebView; + id mockDelegate; +} + +- (void)setUp { + [super setUp]; + playerView = [[YTPlayerView alloc] init]; + mockWebView = [OCMockObject mockForClass:[UIWebView class]]; + mockDelegate = [OCMockObject mockForProtocol:@protocol(YTPlayerViewDelegate)]; + playerView.delegate = mockDelegate; + playerView.webView = mockWebView; +} + +- (void)tearDown { + [super tearDown]; +} + +#pragma mark - Player loading tests + +- (void)testLoadPlayerVideoId { + YTPlayerViewTestingVersion *testingVersion = [[YTPlayerViewTestingVersion alloc] init]; + UIWebView *webView = [[UIWebView alloc] init]; + id partialWebViewMock = [OCMockObject partialMockForObject:webView]; + testingVersion.webView = partialWebViewMock; + + [[partialWebViewMock expect] loadHTMLString:[OCMArg checkWithBlock:^BOOL(NSString *html) { + if ([html rangeOfString:@"VIDEO_ID_HERE"].location == NSNotFound) { + return NO; + } else { + return YES; + } + }] + baseURL:[OCMArg any]]; + [testingVersion loadWithVideoId:@"VIDEO_ID_HERE"]; + [partialWebViewMock verify]; +} + +- (void)testLoadPlayerPlaylistId { + YTPlayerViewTestingVersion *testingVersion = [[YTPlayerViewTestingVersion alloc] init]; + UIWebView *webView = [[UIWebView alloc] init]; + id partialWebViewMock = [OCMockObject partialMockForObject:webView]; + testingVersion.webView = partialWebViewMock; + + [[partialWebViewMock expect] loadHTMLString:[OCMArg checkWithBlock:^BOOL(NSString *html) { + // There are two strings to check for: + // "list" : "PLAYLIST_ID_HERE" + // and + // "listType" : "playlist" + NSString *expectedPlaylistId = @"\"list\" : \"PLAYLIST_ID_HERE\""; + NSString *expectedListType = @"\"listType\" : \"playlist\""; + if ([html rangeOfString:expectedPlaylistId].location == NSNotFound) { + return NO; + } + if ([html rangeOfString:expectedListType].location == NSNotFound) { + return NO; + } + return YES; + }] + baseURL:[OCMArg any]]; + [testingVersion loadWithPlaylistId:@"PLAYLIST_ID_HERE"]; + [partialWebViewMock verify]; +} + +- (void)testLoadPlayerPlayerParameters { + YTPlayerViewTestingVersion *testingVersion = [[YTPlayerViewTestingVersion alloc] init]; + UIWebView *webView = [[UIWebView alloc] init]; + id partialWebViewMock = [OCMockObject partialMockForObject:webView]; + testingVersion.webView = partialWebViewMock; + + [[partialWebViewMock expect] loadHTMLString:[OCMArg checkWithBlock:^BOOL(NSString *html) { + if ([html rangeOfString:@"\"RANDOM_PARAMETER\" : 1"].location == NSNotFound) { + return NO; + } else { + return YES; + } + }] + baseURL:[OCMArg any]]; + [testingVersion loadWithVideoId:@"VIDEO_ID_HERE" playerVars:@{ @"RANDOM_PARAMETER" : @1 }]; + [partialWebViewMock verify]; +} + +#pragma mark - Player Controls + +- (void)testPlayVideo { + [[mockWebView expect] stringByEvaluatingJavaScriptFromString:@"player.playVideo();"]; + [playerView playVideo]; + [mockWebView verify]; +} + +- (void)testPauseVideo { + [[mockDelegate expect] playerView:playerView didChangeToState:kYTPlayerStatePaused]; + [[mockWebView expect] stringByEvaluatingJavaScriptFromString:@"player.pauseVideo();"]; + [playerView pauseVideo]; + [mockWebView verify]; +} + +- (void)testStopVideo { + [[mockWebView expect] stringByEvaluatingJavaScriptFromString:@"player.stopVideo();"]; + [playerView stopVideo]; + [mockWebView verify]; +} + +- (void)testSeekTo { + [[mockWebView expect] stringByEvaluatingJavaScriptFromString:@"player.seekTo(5.5, false);"]; + [playerView seekToSeconds:5.5 allowSeekAhead:NO]; + [mockWebView verify]; +} + +#pragma mark - Tests for cueing and loading videos + +- (void)testCueVideoByIdstartSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cueVideoById('abc', 5.5, 'hd1080');"]; + [playerView cueVideoById:@"abc" startSeconds:5.5 suggestedQuality:kYTPlaybackQualityHD1080]; + [mockWebView verify]; +} + +- (void)testCueVideoByIdstartSecondsendSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cueVideoById({'videoId': 'abc', 'startSeconds': 5.5, 'endSeconds': 10.5, 'suggestedQuality': 'hd1080'});"]; + [playerView cueVideoById:@"abc" + startSeconds:5.5 + endSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHD1080]; + [mockWebView verify]; +} + +- (void)testLoadVideoByIdstartSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.loadVideoById('abc', 5.5, 'highres');"]; + [playerView loadVideoById:@"abc" startSeconds:5.5 suggestedQuality:kYTPlaybackQualityHighRes]; + [mockWebView verify]; +} + +- (void)testLoadVideoByIdstartSecondsendSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cueVideoById({'videoId': 'abc', 'startSeconds': 5.5, 'endSeconds': 10.5, 'suggestedQuality': 'highres'});"]; + [playerView cueVideoById:@"abc" + startSeconds:5.5 + endSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHighRes]; + [mockWebView verify]; +} + +- (void)testCueVideoByUrlstartSecondsWithQuality { + [[mockWebView expect] stringByEvaluatingJavaScriptFromString: + @"player.cueVideoByUrl('http://www.youtube.com/watch?v=J0tafinyviA', 5.5, 'hd1080');"]; + [playerView cueVideoByURL:@"http://www.youtube.com/watch?v=J0tafinyviA" + startSeconds:5.5 + suggestedQuality:kYTPlaybackQualityHD1080]; + [mockWebView verify]; +} + +- (void)testCueVideoByUrlstartSecondsendSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cueVideoByUrl('http://www.youtube.com/" + "watch?v=J0tafinyviA', 5.5, 10.5, 'hd1080');"]; + [playerView cueVideoByURL:@"http://www.youtube.com/watch?v=J0tafinyviA" + startSeconds:5.5 + endSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHD1080]; + [mockWebView verify]; +} + +- (void)testLoadVideoByUrlstartSecondsWithQuality { + [[mockWebView expect] stringByEvaluatingJavaScriptFromString: + @"player.loadVideoByUrl('http://www.youtube.com/watch?v=J0tafinyviA', 5.5, 'highres');"]; + [playerView loadVideoByURL:@"http://www.youtube.com/watch?v=J0tafinyviA" + startSeconds:5.5 + suggestedQuality:kYTPlaybackQualityHighRes]; + [mockWebView verify]; +} + +- (void)testLoadVideoByUrlstartSecondsendSecondsWithQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cueVideoByUrl('http://www.youtube.com/" + "watch?v=J0tafinyviA', 5.5, 10.5, 'highres');"]; + [playerView cueVideoByURL:@"http://www.youtube.com/watch?v=J0tafinyviA" + startSeconds:5.5 + endSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHighRes]; + [mockWebView verify]; +} + +#pragma mark - Tests for cueing and loading playlists + +- (void)testCuePlaylistIdIndexStartSecondsWithSuggestedQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.cuePlaylist('abc', 2, 10.5, 'hd1080');"]; + [playerView cuePlaylistByPlaylistId:@"abc" + index:2 + startSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHD1080]; + [mockWebView verify]; +} + +- (void)testCuePlaylistWithListOfVideoIds { + + [[mockWebView expect] stringByEvaluatingJavaScriptFromString: + @"player.cuePlaylist(['abc', 'def'], 2, 10.5, 'hd1080');"]; + + NSArray *videoIds = @[ @"abc", @"def" ]; + [playerView cuePlaylistByVideos:videoIds + index:2 + startSeconds:10.5 + suggestedQuality:kYTPlaybackQualityHD1080]; + + [mockWebView verify]; +} + +#pragma mark - Retrieving playlist information + +- (void)testGetPlaylist { + // The key test here is seeing if we can correctly convert a JavaScript + // array into an NSArray of NSString instances + [[[mockWebView stub] andReturn:@"[\"abc\", \"def\", \"xyz\"]"] + stringByEvaluatingJavaScriptFromString:@"player.getPlaylist();"]; + + NSArray *expectedArray = @[ @"abc", @"def", @"xyz" ]; + NSArray *videoIds = playerView.playlist; + XCTAssertEqualObjects(expectedArray, videoIds, @"Arrays are not equal."); +} + +#pragma mark - Callback tests + +- (void)testOnPlayerReadyCallback { + NSURL *url = [[NSURL alloc] initWithString:@"ytplayer://onReady"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; + + [[mockDelegate expect] playerViewDidBecomeReady:[OCMArg any]]; + [[mockDelegate stub] playerViewDidBecomeReady:[OCMArg any]]; + + [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + [mockDelegate verify]; +} + +- (void)testOnPlayerStateChangeCallback { + NSURL *url = [[NSURL alloc] initWithString:@"ytplayer://onStateChange?data=1"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; + + [[mockDelegate expect] playerView:[OCMArg any] didChangeToState:kYTPlayerStatePlaying]; + + [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + [mockDelegate verify]; +} + +- (void)testOnPlaybackQualityChangeCallback { + NSURL *url = [[NSURL alloc] initWithString:@"ytplayer://onPlaybackQualityChange?data=hd1080"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; + + [[mockDelegate expect] playerView:[OCMArg any] didChangeToQuality:kYTPlaybackQualityHD1080]; + + [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + [mockDelegate verify]; +} + +- (void)testOnErrorCallback { + NSURL *url = [[NSURL alloc] initWithString:@"ytplayer://onError?data=101"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; + + [[mockDelegate expect] playerView:[OCMArg any] receivedError:kYTPlayerErrorNotEmbeddable]; + + [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + [mockDelegate verify]; +} + +- (void)testGetAvailablePlaybackRates { + [[[mockWebView stub] andReturn:@"[0.25, 0.5, 1, 1.5, 2]"] + stringByEvaluatingJavaScriptFromString:@"player.getAvailablePlaybackRates();"]; + + NSArray *expectedArray = @[ @0.25, @0.5, @1, @1.5, @2 ]; + NSArray *videoIds = playerView.availablePlaybackRates; + XCTAssertEqualObjects(videoIds, expectedArray, @"Arrays are not equal."); +} + +- (void)testGetAvailableQualityLevels { + [[[mockWebView stub] andReturn:@"highres,hd1080"] + stringByEvaluatingJavaScriptFromString:@"player.getAvailableQualityLevels().toString();"]; + + NSArray *expectedArray = + [NSArray arrayWithObjects:[NSNumber numberWithInt:kYTPlaybackQualityHighRes], + [NSNumber numberWithInt:kYTPlaybackQualityHD1080], nil]; + NSArray *availableQualityLevels = playerView.availableQualityLevels; + XCTAssertEqualObjects(availableQualityLevels, expectedArray, + @"Available quality levels is not what was expected"); +} + +- (void)testSetPlaybackQuality { + [[mockWebView expect] + stringByEvaluatingJavaScriptFromString:@"player.setPlaybackQuality('highres');"]; + playerView.playbackQuality = kYTPlaybackQualityHighRes; + [mockWebView verify]; +} + +- (void)testGetPlaybackQuality { + [[[mockWebView stub] andReturn:@"highres"] + stringByEvaluatingJavaScriptFromString:@"player.getPlaybackQuality();"]; + XCTAssertEqual(kYTPlaybackQualityHighRes, playerView.playbackQuality); +} + +- (void)testGetVideoUrl { + NSURL *expectedURL = [NSURL URLWithString:@"http://www.youtube.com/watch?v=9moAdEslwkg"]; + [[[mockWebView stub] andReturn:@"http://www.youtube.com/watch?v=9moAdEslwkg"] + stringByEvaluatingJavaScriptFromString:@"player.getVideoUrl();"]; + XCTAssert([expectedURL isEqual:playerView.videoUrl]); +} + +- (void)testGetVideoEmbedCode { + NSString *expectedEmbedCode = + @""; + [[[mockWebView stub] + andReturn:@""] + stringByEvaluatingJavaScriptFromString:@"player.getVideoEmbedCode();"]; + XCTAssertEqual(expectedEmbedCode, playerView.videoEmbedCode); +} + +#pragma mark - Testing catching non-embed URLs + +- (void)testCatchingEmbedUrls { + NSURL *youTubeEmbed = + [NSURL URLWithString:@"https://www.youtube.com/embed/M7lc1UVf-VE?showinfo=0"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:youTubeEmbed]; + + // Application should NOT open the browser to the embed URL + id mockApplication = [OCMockObject partialMockForObject:[UIApplication sharedApplication]]; + [[mockApplication reject] openURL:youTubeEmbed]; + + BOOL success = [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + + XCTAssertEqual(YES, success, @"UIWebView should navigate to embed URL without opening browser"); + + [mockApplication verify]; + [mockApplication stopMocking]; +} + +- (void)testCatchingNonEmbedUrls { + NSURL *supportUrl = + [NSURL URLWithString:@"https://support.google.com/youtube/answer/3037019?p=player_error1&rd=1"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:supportUrl]; + + id mockApplication = [OCMockObject partialMockForObject:[UIApplication sharedApplication]]; + [[mockApplication expect] openURL:supportUrl]; + + BOOL success = [playerView webView:mockWebView + shouldStartLoadWithRequest:request + navigationType:UIWebViewNavigationTypeOther]; + + XCTAssertEqual(NO, success, @"A non embed URL should open the browser"); + + [mockApplication verify]; + [mockApplication stopMocking]; +} + +@end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/README.md b/Carthage/Checkouts/youtube-ios-player-helper/README.md new file mode 100644 index 0000000..453f66f --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/README.md @@ -0,0 +1,45 @@ +# YouTube-Player-iOS-Helper + +[![Version](https://cocoapod-badges.herokuapp.com/v/youtube-ios-player-helper/badge.png)](https://cocoapods.org/pods/youtube-ios-player-helper) +[![Platform](https://cocoapod-badges.herokuapp.com/p/youtube-ios-player-helper/badge.png)](https://cocoapods.org/pods/youtube-ios-player-helper) + +## Usage + +To run the example project; clone the repo, and run `pod install` from the Project directory first. For a simple tutorial see this Google Developers article - [Using the YouTube Helper Library to embed YouTube videos in your iOS application](https://developers.google.com/youtube/v3/guides/ios_youtube_helper). + +## Requirements + +## Installation + +YouTube-Player-iOS-Helper is available through [CocoaPods](http://cocoapods.org), to install +it simply add the following line to your Podfile: + + pod "youtube-ios-player-helper", "~> 0.1.4" + +After installing in your project and opening the workspace, to use the library: + + 1. Drag a UIView the desired size of your player onto your Storyboard. + 2. Change the UIView's class in the Identity Inspector tab to YTPlayerView + 3. Import "YTPlayerView.h" in your ViewController. + 4. Add the following property to your ViewController's header file: +```objc + @property(nonatomic, strong) IBOutlet YTPlayerView *playerView; +``` + 5. Load the video into the player in your controller's code with the following code: +```objc + [self.playerView loadWithVideoId:@"M7lc1UVf-VE"]; +``` + 6. Run your code! + +See the sample project for more advanced uses, including passing additional player parameters and +working with callbacks via YTPlayerViewDelegate. + +## Author + +Ikai Lan +Ibrahim Ulukaya, ulukaya@google.com +Yoshifumi Yamaguchi, yoshifumi@google.com + +## License + +YouTube-Player-iOS-Helper is available under the Apache 2.0 license. See the LICENSE file for more info. diff --git a/Carthage/Checkouts/youtube-ios-player-helper/Rakefile b/Carthage/Checkouts/youtube-ios-player-helper/Rakefile new file mode 100644 index 0000000..b3d5988 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/Rakefile @@ -0,0 +1,162 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +desc "Runs the specs [EMPTY]" +task :spec do + # Provide your own implementation +end + +task :version do + git_remotes = `git remote`.strip.split("\n") + + if git_remotes.count > 0 + puts "-- fetching version number from github" + sh 'git fetch' + + remote_version = remote_spec_version + end + + if remote_version.nil? + puts "There is no current released version. You're about to release a new Pod." + version = "0.0.1" + else + puts "The current released version of your pod is " + remote_spec_version.to_s() + version = suggested_version_number + end + + puts "Enter the version you want to release (" + version + ") " + new_version_number = $stdin.gets.strip + if new_version_number == "" + new_version_number = version + end + + replace_version_number(new_version_number) +end + +desc "Release a new version of the Pod" +task :release do + + puts "* Running version" + sh "rake version" + + unless ENV['SKIP_CHECKS'] + if `git symbolic-ref HEAD 2>/dev/null`.strip.split('/').last != 'master' + $stderr.puts "[!] You need to be on the `master' branch in order to be able to do a release." + exit 1 + end + + if `git tag`.strip.split("\n").include?(spec_version) + $stderr.puts "[!] A tag for version `#{spec_version}' already exists. Change the version in the podspec" + exit 1 + end + + puts "You are about to release `#{spec_version}`, is that correct? [y/n]" + exit if $stdin.gets.strip.downcase != 'y' + end + + puts "* Running specs" + sh "rake spec" + + puts "* Linting the podspec" + sh "pod lib lint" + + # Then release + sh "git commit #{podspec_path} CHANGELOG.md -m 'Release #{spec_version}'" + sh "git tag -a #{spec_version} -m 'Release #{spec_version}'" + sh "git push origin master" + sh "git push origin --tags" + sh "pod push master #{podspec_path}" +end + +# @return [Pod::Version] The version as reported by the Podspec. +# +def spec_version + require 'cocoapods' + spec = Pod::Specification.from_file(podspec_path) + spec.version +end + +# @return [Pod::Version] The version as reported by the Podspec from remote. +# +def remote_spec_version + require 'cocoapods-core' + + if spec_file_exist_on_remote? + remote_spec = eval(`git show origin/master:#{podspec_path}`) + remote_spec.version + else + nil + end +end + +# @return [Bool] If the remote repository has a copy of the podpesc file or not. +# +def spec_file_exist_on_remote? + test_condition = `if git rev-parse --verify --quiet origin/master:#{podspec_path} >/dev/null; + then + echo 'true' + else + echo 'false' + fi` + + 'true' == test_condition.strip +end + +# @return [String] The relative path of the Podspec. +# +def podspec_path + podspecs = Dir.glob('*.podspec') + if podspecs.count == 1 + podspecs.first + else + raise "Could not select a podspec" + end +end + +# @return [String] The suggested version number based on the local and remote version numbers. +# +def suggested_version_number + if spec_version != remote_spec_version + spec_version.to_s() + else + next_version(spec_version).to_s() + end +end + +# @param [Pod::Version] version +# the version for which you need the next version +# +# @note It is computed by bumping the last component of the versino string by 1. +# +# @return [Pod::Version] The version that comes next after the version supplied. +# +def next_version(version) + version_components = version.to_s().split("."); + last = (version_components.last.to_i() + 1).to_s + version_components[-1] = last + Pod::Version.new(version_components.join(".")) +end + +# @param [String] new_version_number +# the new version number +# +# @note This methods replaces the version number in the podspec file with a new version number. +# +# @return void +# +def replace_version_number(new_version_number) + text = File.read(podspec_path) + text.gsub!(/(s.version( )*= ")#{spec_version}(")/, "\\1#{new_version_number}\\3") + File.open(podspec_path, "w") { |file| file.puts text } +end \ No newline at end of file diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.podspec b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.podspec new file mode 100644 index 0000000..8eee90f --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.podspec @@ -0,0 +1,54 @@ +Pod::Spec.new do |s| + s.name = "youtube-ios-player-helper" + s.version = "0.1.6" + s.summary = "Helper library for iOS developers that want to embed YouTube videos in + their iOS apps with the iframe player API." + + s.description = <<-DESC + Helper library for iOS developers that want to play YouTube videos in + their iOS apps with the iframe player API. + + This library allows iOS developers to quickly embed YouTube videos within + their applications via a custom UIView subclass, YTPlayerView. + This library provides: + + * A managed UIWebView instance that loads the HTML code for the iframe player + * Objective-C wrapper functions for the JavaScript Player API + * YTPlayerViewDelegate for handling YouTube player state changes natively in + your Objective-C code + DESC + + s.homepage = "https://developers.google.com/youtube/v3/guides/ios_youtube_helper" + s.license = { + :type => 'Apache', + :text => <<-LICENSE + Copyright 2014 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + LICENSE + } + s.author = { "Ikai Lan" => "", + "Ibrahim Ulukaya" => "ulukaya@google.com", + "Yoshifumi Yamaguchi" => "yoshifumi@google.com" } + s.social_media_url = "https://twitter.com/YouTubeDev" + s.source = { :git => "https://github.com/youtube/youtube-ios-player-helper.git", :tag => "0.1.6" } + + s.platform = :ios, '6.0' + s.requires_arc = true + + s.source_files = 'Classes' + s.resources = "youtube-ios-player-helper/Assets.bundle" + + s.ios.exclude_files = 'Classes/osx' + s.osx.exclude_files = 'Classes/ios' +end diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/project.pbxproj b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/project.pbxproj new file mode 100644 index 0000000..dd3f576 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/project.pbxproj @@ -0,0 +1,295 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + B3C76A271B975ADB00F375B4 /* YTPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = B3C76A251B975ADB00F375B4 /* YTPlayerView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3C76A281B975ADB00F375B4 /* YTPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = B3C76A261B975ADB00F375B4 /* YTPlayerView.m */; }; + B3C76A2F1B975C0100F375B4 /* YouTubeiOSPlayerHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = B3C76A2D1B975BD500F375B4 /* YouTubeiOSPlayerHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B3C76A331B9765D700F375B4 /* Assets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B3C76A321B9765D700F375B4 /* Assets.bundle */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B3C76A1A1B975AA700F375B4 /* YouTubeiOSPlayerHelper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YouTubeiOSPlayerHelper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B3C76A1F1B975AA700F375B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B3C76A251B975ADB00F375B4 /* YTPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YTPlayerView.h; path = Classes/YTPlayerView.h; sourceTree = SOURCE_ROOT; }; + B3C76A261B975ADB00F375B4 /* YTPlayerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YTPlayerView.m; path = Classes/YTPlayerView.m; sourceTree = SOURCE_ROOT; }; + B3C76A2D1B975BD500F375B4 /* YouTubeiOSPlayerHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YouTubeiOSPlayerHelper.h; sourceTree = ""; }; + B3C76A321B9765D700F375B4 /* Assets.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Assets.bundle; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B3C76A161B975AA700F375B4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B3C76A101B975AA700F375B4 = { + isa = PBXGroup; + children = ( + B3C76A1C1B975AA700F375B4 /* youtube-ios-player-helper */, + B3C76A1B1B975AA700F375B4 /* Products */, + ); + sourceTree = ""; + }; + B3C76A1B1B975AA700F375B4 /* Products */ = { + isa = PBXGroup; + children = ( + B3C76A1A1B975AA700F375B4 /* YouTubeiOSPlayerHelper.framework */, + ); + name = Products; + sourceTree = ""; + }; + B3C76A1C1B975AA700F375B4 /* youtube-ios-player-helper */ = { + isa = PBXGroup; + children = ( + B3C76A321B9765D700F375B4 /* Assets.bundle */, + B3C76A251B975ADB00F375B4 /* YTPlayerView.h */, + B3C76A261B975ADB00F375B4 /* YTPlayerView.m */, + B3C76A1F1B975AA700F375B4 /* Info.plist */, + B3C76A2D1B975BD500F375B4 /* YouTubeiOSPlayerHelper.h */, + ); + path = "youtube-ios-player-helper"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + B3C76A171B975AA700F375B4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B3C76A271B975ADB00F375B4 /* YTPlayerView.h in Headers */, + B3C76A2F1B975C0100F375B4 /* YouTubeiOSPlayerHelper.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + B3C76A191B975AA700F375B4 /* YouTubeiOSPlayerHelper */ = { + isa = PBXNativeTarget; + buildConfigurationList = B3C76A221B975AA700F375B4 /* Build configuration list for PBXNativeTarget "YouTubeiOSPlayerHelper" */; + buildPhases = ( + B3C76A151B975AA700F375B4 /* Sources */, + B3C76A161B975AA700F375B4 /* Frameworks */, + B3C76A171B975AA700F375B4 /* Headers */, + B3C76A181B975AA700F375B4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YouTubeiOSPlayerHelper; + productName = "youtube-ios-player-helper"; + productReference = B3C76A1A1B975AA700F375B4 /* YouTubeiOSPlayerHelper.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B3C76A111B975AA700F375B4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = "YouTube Developer Relations"; + TargetAttributes = { + B3C76A191B975AA700F375B4 = { + CreatedOnToolsVersion = 7.0; + }; + }; + }; + buildConfigurationList = B3C76A141B975AA700F375B4 /* Build configuration list for PBXProject "youtube-ios-player-helper" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = B3C76A101B975AA700F375B4; + productRefGroup = B3C76A1B1B975AA700F375B4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B3C76A191B975AA700F375B4 /* YouTubeiOSPlayerHelper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B3C76A181B975AA700F375B4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B3C76A331B9765D700F375B4 /* Assets.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B3C76A151B975AA700F375B4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B3C76A281B975ADB00F375B4 /* YTPlayerView.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B3C76A201B975AA700F375B4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + B3C76A211B975AA700F375B4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + B3C76A231B975AA700F375B4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "youtube-ios-player-helper/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.youtube.youtube-ios-player-helper"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + B3C76A241B975AA700F375B4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "youtube-ios-player-helper/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.youtube.youtube-ios-player-helper"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B3C76A141B975AA700F375B4 /* Build configuration list for PBXProject "youtube-ios-player-helper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B3C76A201B975AA700F375B4 /* Debug */, + B3C76A211B975AA700F375B4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B3C76A221B975AA700F375B4 /* Build configuration list for PBXNativeTarget "YouTubeiOSPlayerHelper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B3C76A231B975AA700F375B4 /* Debug */, + B3C76A241B975AA700F375B4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B3C76A111B975AA700F375B4 /* Project object */; +} diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/xcshareddata/xcschemes/youtube-ios-player-helper.xcscheme b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/xcshareddata/xcschemes/youtube-ios-player-helper.xcscheme new file mode 100644 index 0000000..502a9f5 --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper.xcodeproj/xcshareddata/xcschemes/youtube-ios-player-helper.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Assets.bundle/Assets/YTPlayerView-iframe-player.html b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Assets.bundle/Assets/YTPlayerView-iframe-player.html new file mode 100644 index 0000000..975510e --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Assets.bundle/Assets/YTPlayerView-iframe-player.html @@ -0,0 +1,90 @@ + + + + + + + +
+
+
+ + + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Info.plist b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/YouTubeiOSPlayerHelper.h b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/YouTubeiOSPlayerHelper.h new file mode 100644 index 0000000..9a886df --- /dev/null +++ b/Carthage/Checkouts/youtube-ios-player-helper/youtube-ios-player-helper/YouTubeiOSPlayerHelper.h @@ -0,0 +1,19 @@ +// +// youtube-ios-player-helper.h +// youtube-ios-player-helper +// +// Created by Niels van Hoorn on 02/09/15. +// Copyright © 2015 YouTube Developer Relations. All rights reserved. +// + +#import + +//! Project version number for youtube-ios-player-helper. +FOUNDATION_EXPORT double youtube_ios_player_helperVersionNumber; + +//! Project version string for youtube-ios-player-helper. +FOUNDATION_EXPORT const unsigned char youtube_ios_player_helperVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +#import "YTPlayerView.h" \ No newline at end of file From c01e955e4c9d808c2696f86c2dbd10c50432ba21 Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Wed, 23 May 2018 16:34:08 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[add]=20=E3=83=A9=E3=82=A4=E3=83=96?= =?UTF-8?q?=E3=83=A9=E3=83=AA=E3=82=92import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SyncPod.xcodeproj/project.pbxproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SyncPod.xcodeproj/project.pbxproj b/SyncPod.xcodeproj/project.pbxproj index 8c5b1ba..0011911 100644 --- a/SyncPod.xcodeproj/project.pbxproj +++ b/SyncPod.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ 0D8BB2B420109DEE00C7F6A9 /* RoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D8BB2B320109DEE00C7F6A9 /* RoomViewController.swift */; }; 0D8BB2B62010A25700C7F6A9 /* ActionCableClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8BB2B52010A25700C7F6A9 /* ActionCableClient.framework */; }; 0D8BB2B82010A37900C7F6A9 /* Starscream.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8BB2B72010A37900C7F6A9 /* Starscream.framework */; }; + 0D92819420B55074008B0F32 /* YouTubeiOSPlayerHelper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D92819320B55074008B0F32 /* YouTubeiOSPlayerHelper.framework */; }; 0D939F41201B6A0200014633 /* CreateRoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D939F40201B6A0200014633 /* CreateRoomViewController.swift */; }; 0DA2FB49201C700A00292BFE /* RoomTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA2FB48201C700A00292BFE /* RoomTabViewController.swift */; }; 0DCA418D200C9B3500ED9B50 /* DeviceConst.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DCA418C200C9B3500ED9B50 /* DeviceConst.swift */; }; @@ -117,6 +118,7 @@ 0D8BB2B320109DEE00C7F6A9 /* RoomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomViewController.swift; sourceTree = ""; }; 0D8BB2B52010A25700C7F6A9 /* ActionCableClient.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ActionCableClient.framework; path = Carthage/Build/iOS/ActionCableClient.framework; sourceTree = ""; }; 0D8BB2B72010A37900C7F6A9 /* Starscream.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Starscream.framework; path = Carthage/Build/iOS/Starscream.framework; sourceTree = ""; }; + 0D92819320B55074008B0F32 /* YouTubeiOSPlayerHelper.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = YouTubeiOSPlayerHelper.framework; path = Carthage/Build/iOS/YouTubeiOSPlayerHelper.framework; sourceTree = ""; }; 0D939F40201B6A0200014633 /* CreateRoomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewController.swift; sourceTree = ""; }; 0DA2FB48201C700A00292BFE /* RoomTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTabViewController.swift; sourceTree = ""; }; 0DCA418C200C9B3500ED9B50 /* DeviceConst.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceConst.swift; sourceTree = ""; }; @@ -135,6 +137,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0D92819420B55074008B0F32 /* YouTubeiOSPlayerHelper.framework in Frameworks */, 0D0507E4202A22C400791EA9 /* AlamofireImage.framework in Frameworks */, 0DF8BF762024D7BD00F250E1 /* ScrollingStackContainer.framework in Frameworks */, 0D67B738201C3B6D00CCB84C /* XLPagerTabStrip.framework in Frameworks */, @@ -183,6 +186,7 @@ 0D4B438220099E540035DF1D /* Frameworks */ = { isa = PBXGroup; children = ( + 0D92819320B55074008B0F32 /* YouTubeiOSPlayerHelper.framework */, 0D0507E3202A22C300791EA9 /* AlamofireImage.framework */, 0DF8BF752024D7BD00F250E1 /* ScrollingStackContainer.framework */, 0D67B737201C3B6D00CCB84C /* XLPagerTabStrip.framework */, @@ -474,6 +478,7 @@ "$(SRCROOT)/Carthage/Build/iOS/XLPagerTabStrip.framework", "$(SRCROOT)/Carthage/Build/iOS/ScrollingStackContainer.framework", "$(SRCROOT)/Carthage/Build/iOS/AlamofireImage.framework", + "$(SRCROOT)/Carthage/Build/iOS/YouTubeiOSPlayerHelper.framework", ); outputPaths = ( );