<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap-css@3.0.0" data-semver="3.0.0" rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
<script data-require="jquery@2.0.3" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="angular.js@1.2.0-rc3" data-semver="1.2.0-rc3" src="http://code.angularjs.org/1.2.0-rc.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script src="youtubeplayer.js"></script>
<script src="filters.js"></script>
</head>
<body ng-app="myApp">
<div class="col-lg-12">
<div ng-controller="YoutubeSearchController">
<div class="row">
<div class="col-lg-12 playlist-videos-container">
<div ng-repeat="video in playlistVideos">
<div class="playlist-video" ng-click="removeVideo($index)" ng-show="editPlaylistView">
<img class="search-result-img" ng-src="{{video.thumbnail}}"/>
<div class="playlist-video-title">{{video.title | truncate:35:"..." }}</div>
<div class="playlist-video-delete"><span class="glyphicon glyphicon-trash"></span></div>
</div>
<div class="playlist-video" ng-click="playVideo(video)" ng-hide="editPlaylistView">
<img class="search-result-img" ng-src="{{video.thumbnail}}"/>
<div class="playlist-video-title">{{video.title | truncate:35:"..." }}</div>
<div class="playlist-video-delete"><span class="glyphicon glyphicon-play"></span></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<h4 class="youtube-header">youtube & angularjs</h4>
<div id="ytplayer" youtube-player></div>
<div class="row">
<div class="col-lg-4 player-btns">
<button class="btn btn-success playlist-btn" ng-click="togglePlaylistView()" ng-show="editPlaylistView">play</button>
<button class="btn btn-primary playlist-btn" ng-click="togglePlaylistView()" ng-hide="editPlaylistView">edit</button>
<button class="btn btn-primary" ng-click="previousVideo()"><next><span class="glyphicon glyphicon-step-backward"></span></button>
<button class="btn btn-primary" ng-click="togglePlayerState()"><span class="glyphicon glyphicon-play"></span><span class="glyphicon glyphicon-pause"></span></button>
<button class="btn btn-primary" ng-click="nextVideo()"><span class="glyphicon glyphicon-step-forward"></span></button>
<button class="btn btn-primary" ng-click="toggleSearchView()">toggle search</button>
</div>
</div>
<div ng-show="searchView">
<input type="text" name="search" ng-model="search" ng-change="getData()" class="youtube-input" />
<div class="youtube-results-container">
<div ng-repeat="video in videos" class="video-search-result-container">
<div class="search-result-video" ng-click="addVideo(video)">
<img class="search-result-img" ng-src="{{video.thumbnail}}" />
<div class="search-result-title">{{video.title | truncate:35:"..." }}</div>
<div class="search-result-add"><span class="glyphicon glyphicon-plus"></span></div>
<div class="search-result-meta">{{ video.duration | durationconverter }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/iframe_api"
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
</script>
</body>
</html>
// Code goes here
var app = angular.module('myApp', ['youtube']);
app.factory('youtubeSearch', function ($q, $http) {
return {
getYoutubeData: function (search) {
var deferred = $q.defer();
var url = "http://gdata.youtube.com/feeds/api/videos?q=" + search + "&alt=json&max-results=4&start-index=1&callback=JSON_CALLBACK";
$http.jsonp(url).success(function (json) {
var videos = json.feed.entry;
deferred.resolve(videos);
}).error(function (error) {
console.log(JSON.stringify(error));
});
return deferred.promise;
}
};
});
app.controller('YoutubeSearchController', ['$scope', 'youtubeSearch', 'Player', '$timeout', '$rootScope', function ($scope, youtubeSearch, Player, $timeout, $rootScope) {
$scope.player = Player;
$scope.search = "penny and the quarters - you and me";
$scope.videos = [];
$scope.editPlaylistView = false;
$scope.searchView = true;
$scope.playlistVideos = [
{title: "You and Me - Penny & The Quarters", thumbnail: "http://i.ytimg.com/vi/zjYxNnzNhRs/0.jpg", url: "http://www.youtube.com/watch?v=zjYxNnzNhRs&feature=youtube_gdata", duration: 186, videoId: "zjYxNnzNhRs" },
{title: "Passenger - Let Her Go (Kygo Remix)", thumbnail: "http://i.ytimg.com/vi/LgL6dRQH0tg/0.jpg", url: "http://www.youtube.com/watch?v=LgL6dRQH0tg&feature=youtube_gdata", duration: 402, videoId: "LgL6dRQH0tg" },
{title: "Smokey Robinson - Tracks of my tears", thumbnail: "http://i.ytimg.com/vi/2HnVsvBO8aI/0.jpg", url: "http://www.youtube.com/watch?v=2HnVsvBO8aI&feature=youtube_gdata", duration: 182, videoId: "2HnVsvBO8aI" },
{title: "Fontella Bass - Rescue Me", thumbnail: "http://i.ytimg.com/vi/ALQ6lzS-Npc/0.jpg", url: "http://www.youtube.com/watch?v=ALQ6lzS-Npc&feature=youtube_gdata", duration: 170, videoId: "ALQ6lzS-Npc" }
];
$scope.currentVideo = $scope.playlistVideos[0];
$scope.currentVideoIndex = 0;
$scope.removeVideo = function(index) {
$scope.playlistVideos.splice(index, 1);
};
$scope.$watch(function combinedWatch() {
return {
search: $scope.search
};
}, function (value) {
if (value.search) {
$scope.videos = [];
var promise = youtubeSearch.getYoutubeData($scope.search);
promise.then(function (videosData) {
// $scope.videos = data;
angular.forEach(videosData, function (video) {
var searchVideo = {};
searchVideo.title = video.title.$t;
searchVideo.thumbnail = video.media$group.media$thumbnail[0].url;
searchVideo.url = video.link[0].href;
searchVideo.duration = video.media$group.media$content[0].duration;
searchVideo.videoId = video.link[0].href.match(/^http:\/\/www\.youtube\.com\/.*[?&]v=([^&]+)/i)[1];
$scope.videos.push(searchVideo);
});
});
}
}, true);
$scope.addVideo = function (video) {
console.log(video);
this.playlistVideos.push(video);
this.videos.splice(this.videos.indexOf(video), 1);
};
$scope.togglePlaylistView = function() {
$scope.editPlaylistView = !$scope.editPlaylistView;
};
$scope.toggleSearchView = function() {
$scope.searchView = !$scope.searchView;
};
$scope.nextVideo = function() {
// technically, if you took out all the songs, and then clicked next, it would throw an error, but this is just for the fiddle.
var nextIndex = $scope.currentVideoIndex >= $scope.playlistVideos.length - 1 ? 0 : $scope.currentVideoIndex + 1;
$scope.playVideo($scope.playlistVideos[nextIndex]);
};
$scope.previousVideo = function() {
var previousIndex = $scope.currentVideoIndex === 0 ? $scope.playlistVideos.length - 1 : $scope.currentVideoIndex - 1;
$scope.playVideo($scope.playlistVideos[previousIndex]);
};
$scope.togglePlayerState = function() {
var playerState = this.player.player.getPlayerState();
playerState == 1 ? this.player.player.pauseVideo() : this.player.player.playVideo();
};
$scope.playVideo = function(video) {
this.currentVideo = video;
this.currentVideoIndex = this.playlistVideos.indexOf(video);
this.player.player.loadVideoById(video.videoId);
};
}]);
body {
background: #ecf0f1;
}
.playlist-videos-container {
padding-top: 4px;
}
.youtube-header {
color: #3498db;
margin-left: 60px;
font-weight: 700;
font-style: italic;
}
#ytplayer {
margin-left: 20px;
}
.search-result-img {
width: 40px;
height: 40px;
border-radius: 50%;
float: left;
border: 2px solid #161f42;
}
.youtube-input {
width: 300px;
margin-left: 38px;
margin-bottom: 5px;
}
.player-btns {
margin-left: 38px;
margin-bottom: 4px;
}
.search-result-meta {
font-size: 10px;
position: absolute;
top: 28px;
left: 60px;
font-style: italic;
color: #2c3e50;
}
.search-result-video {
background: #bdc3c7;
background: #EA9E97;
border-radius: 7px;
box-shadow: 0 1px 1px 0 #161F42;
height: 44px;
padding: 2px;
margin-bottom: 3px;
margin-left: 22px;
width: 340px;
position: relative;
}
.search-result-video:hover {
cursor: pointer;
background-color: #E98378;
box-shadow: 0 1px 1px 0 #41525f;
}
.playlist-video {
background: #2c3e50;
border-radius: 7px;
box-shadow: 0 1px 1px 0 #161F42;
height: 44px;
padding: 2px;
margin-bottom: 3px;
margin-left: 22px;
width: 340px;
position: relative;
}
.playlist-video:hover {
cursor:pointer;
background: #5B6E7B;
}
.playlist-video-title {
color: #bdc3c7;
float: left;
font-size: 12px;
margin-top: 10px;
margin-left: 12px;
font-weight: 700;
}
.search-result-title {
float: left;
font-size: 12px;
margin-top: 10px;
margin-left: 12px;
font-weight: 700;
color: #2c3e50;
}
.playlist-video-delete {
font-size: 18px;
color: #bdc3c7;
float: right;
margin-right: 12px;
margin-top: 8px;
}
.search-result-add {
font-size: 18px;
float: right;
margin-right: 12px;
margin-top: 8px;
}
.playlist-btn {
margin-top: 3px;
margin-bottom: 3px;
}
var app = angular.module('myApp');
// filter to shorten youtube titles
app.filter('truncate', function () {
return function (text, length, end) {
if (isNaN(length)) {
length = 10;
}
if (end === undefined) {
end = "...";
}
if (text.length <= length || text.length - end.length <= length) {
return text;
} else {
return String(text).substring(0, length - end.length) + end;
}
};
});
// filter to convert youtube duration (seconds) into noramlly displayed timestamps
app.filter('durationconverter', function () {
return function (duration) {
minutes = Math.floor(duration / 60);
seconds = duration % 60;
if (minutes !== 0) {
return String(minutes) + " minutes, " + seconds + " seconds"
} else {
return String(seconds) + " seconds"
}
};
});
angular.module('youtube', ['ng']).run(function () {})
.service('youtubePlayerApi', ['$window', '$rootScope', '$log', function ($window, $rootScope, $log) {
var service = $rootScope.$new(true);
// Youtube callback when API is ready
$window.onYouTubeIframeAPIReady = function () {
$log.info('Youtube API is ready');
service.ready = true;
service.loadPlayer();
};
service.ready = false;
service.playerId = "ytplayer";
service.player = null;
service.videoId = "zjYxNnzNhRs";
service.playerHeight = '200';
service.playerWidth = '356';
service.bindVideoPlayer = function (elementId) {
service.playerId = elementId;
service.loadPlayer();
};
service.createPlayer = function () {
return new YT.Player(this.playerId, {
height: this.playerHeight,
width: this.playerWidth,
videoId: this.videoId,
playerVars: {
controls: 1,
modestbranding: 1,
rel: 0,
showinfo: 0
}
});
};
service.loadPlayer = function () {
// API ready?
if (this.ready && this.playerId && this.videoId) {
if (this.player) {
this.player.destroy();
}
this.player = this.createPlayer();
}
};
return service;
}])
.directive('youtubePlayer', ['youtubePlayerApi', function (youtubePlayerApi) {
return {
restrict: 'A',
link: function (scope, element) {
youtubePlayerApi.bindVideoPlayer(element[0].id);
}
};
}]);
app.factory('Player', ['youtubePlayerApi', function (youtubePlayerApi) {
return youtubePlayerApi;
}]);