├── .bowerrc ├── .gitignore ├── .travis.yml ├── README.md ├── bower.json ├── gulpfile.js ├── package.json ├── public └── assets │ └── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── src ├── assets ├── js │ ├── app.js │ ├── controllers │ │ └── YoutubeController.js │ └── services │ │ └── YoutubeService.js └── scss │ ├── _bootstrap │ ├── _config.scss │ ├── _form.scss │ ├── _init.scss │ ├── _modal.scss │ └── _videos.scss │ ├── _partials │ └── _header.scss │ └── app.scss └── jade ├── partials └── modal.jade ├── public └── index.jade └── templates └── default.jade /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/assets/scss/_vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .sass-cache/ 3 | src/assets/scss/_vendor/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: npm install -g bower 5 | script: npm install && bower install && gulp 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Travis Status](https://travis-ci.org/marcelodeveloper/angularjs-youtube.svg?branch=master) 2 | 3 | # A youtube app using angularjs 4 | 5 | > This app was created with *angularjs* using [Youtube API V3](https://developers.google.com/youtube/v3/?hl=en). 6 | 7 | # How to start? 8 | 9 | ```sh 10 | $ npm install -g gulp && npm install -g bower 11 | ``` 12 | 13 | # Install dependencies 14 | 15 | ```sh 16 | $ npm install && bower install 17 | ``` 18 | 19 | Now run ```$ gulp``` to compile css, html and js files 20 | 21 | See the result on the browser, do enter in /public directory and run a simple server in *Python* 22 | 23 | ```sh 24 | $ python -m SimpleHTTPServer 25 | ``` 26 | 27 | At moment the app show only videos by a *query*. You can choose a default query in ```src/assets/js/controllers/YoutubeController.js``` 28 | 29 | ```js 30 | // default query 31 | $scope.query = 'Red Hot Chili Peppers'; 32 | ``` 33 | 34 | If you want only use the YoutubeService to get the videos in your apps. YoutubeService can do this. You can config the basic params as maxResults and your app key in. ```src/assets/js/services/YoutubeService.js``` 35 | 36 | ```js 37 | var API = { 38 | url: 'https://www.googleapis.com/youtube/v3', // google api v3 39 | params: { 40 | key: 'yourkey', // your secret key 41 | maxResults: 20, // max results per page 42 | part: 'snippet', // groups of properties as player, status, topicDetails 43 | type: 'video' // video, channel, playlist 44 | } 45 | }; 46 | ``` 47 | 48 | # Contribute 49 | 50 | If you have something to say, add or remove. You're welcome. 51 | 52 | - Fork the repository 53 | - Commit your changes 54 | - Create a new pull request 55 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-youtube", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/marcelodeveloper/angularjs-youtube", 5 | "authors": [ 6 | "Marcelo Silva " 7 | ], 8 | "description": "A app to show your favorite videos with angularjs and youtube api v3", 9 | "keywords": [ 10 | "youtube", 11 | "videos", 12 | "js", 13 | "javascript", 14 | "angularjs", 15 | "app", 16 | "api", 17 | "mean" 18 | ], 19 | "license": "MIT", 20 | "dependencies": { 21 | "normalize-scss": "~3.0.3", 22 | "sass-bootstrap": "~3.0.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var jade = require('gulp-jade'); 3 | var uglify = require('gulp-uglify'); 4 | var concat = require('gulp-concat'); 5 | var sass = require('gulp-ruby-sass'); 6 | 7 | gulp.task('sass', function() { 8 | return sass('./src/assets/scss/app.scss', { 9 | style: 'compressed', 10 | compass: true 11 | }) 12 | .on('error', sass.logError) 13 | .pipe(gulp.dest('./public/assets/css')); 14 | }); 15 | 16 | gulp.task('template', function() { 17 | return gulp.src('./src/jade/public/**/*.jade') 18 | .pipe(jade({ 19 | pretty: false 20 | })) 21 | .pipe(gulp.dest('./public/')) 22 | }); 23 | 24 | gulp.task('scripts', function() { 25 | return gulp.src('./src/assets/js/**/*.js') 26 | .pipe(concat('app.js')) 27 | .pipe(uglify()) 28 | .pipe(gulp.dest('./public/assets/js')); 29 | }); 30 | 31 | gulp.task('watch', function() { 32 | gulp.watch('./src/assets/scss/**/*', ['sass']); 33 | gulp.watch('./src/assets/js/**/*', ['scripts']) 34 | gulp.watch('./src/jade/public/**/*', ['template']); 35 | }); 36 | 37 | gulp.task('default', ['sass', 'scripts', 'template']); 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-youtube", 3 | "version": "1.0.0", 4 | "description": "A app to show your favorite videos with angularjs and youtube api v3", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/marcelodeveloper/angularjs-youtube.git" 8 | }, 9 | "keywords": [ 10 | "youtube", 11 | "videos", 12 | "js", 13 | "javascript", 14 | "angularjs", 15 | "app", 16 | "api", 17 | "mean" 18 | ], 19 | "author": "Marcelo Silva ", 20 | "bugs": { 21 | "url": "https://github.com/marcelodeveloper/angularjs-youtube/issues" 22 | }, 23 | "homepage": "https://github.com/marcelodeveloper/angularjs-youtube#readme", 24 | "devDependencies": { 25 | "gulp": "^3.9.0", 26 | "gulp-concat": "^2.6.0", 27 | "gulp-jade": "^1.1.0", 28 | "gulp-ruby-sass": "^2.0.4", 29 | "gulp-uglify": "^1.4.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamtchelo/angularjs-youtube/19acba9d2a46a6bcfb2f86dc25abf3621fada17b/public/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/assets/fonts/glyphicons-halflings-regular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /public/assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamtchelo/angularjs-youtube/19acba9d2a46a6bcfb2f86dc25abf3621fada17b/public/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamtchelo/angularjs-youtube/19acba9d2a46a6bcfb2f86dc25abf3621fada17b/public/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/assets/js/app.js: -------------------------------------------------------------------------------- 1 | angular.module('App', ['App.Youtube']); 2 | angular.module('App.Youtube', ['Youtube.Controllers', 'Youtube.Services']); 3 | 4 | angular.module('Youtube.Controllers', []); 5 | angular.module('Youtube.Services', []); 6 | -------------------------------------------------------------------------------- /src/assets/js/controllers/YoutubeController.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var app = angular.module('Youtube.Controllers'); 5 | app.controller('YoutubeController', YoutubeController); 6 | 7 | YoutubeController.$inject = ['$scope', '$http', '$sce', 'YoutubeService']; 8 | 9 | function YoutubeController($scope, $http, $sce, YoutubeService) { 10 | // default query 11 | $scope.query = 'Red Hot Chilli Peppers'; 12 | $scope.modal = false; 13 | $scope.currentVideo = null; 14 | 15 | YoutubeService.search($scope.query) 16 | .success(function(data) { 17 | parseData(data.items); 18 | }) 19 | .error(function(error) { 20 | console.log('There was an error: ' + error); 21 | }); 22 | 23 | $scope.search = function() { 24 | YoutubeService.search($scope.query) 25 | .success(function(data) { 26 | parseData(data.items); 27 | }) 28 | .error(function(error) { 29 | console.log('There was an error: ' + error); 30 | }); 31 | }; 32 | 33 | $scope.videoModal = function(videoID) { 34 | if (!videoID) { 35 | $scope.modal = false; 36 | return; 37 | } 38 | 39 | var video = 'http://www.youtube.com/embed/' + videoID + '/?autoplay=1'; 40 | $scope.currentVideo = $sce.trustAsResourceUrl(video); 41 | $scope.modal = true; 42 | }; 43 | 44 | $scope.closeModal = function() { 45 | if ($scope.modal === false) { 46 | return; 47 | } 48 | 49 | $scope.modal = false; 50 | $scope.currentVideo = undefined; 51 | }; 52 | 53 | function parseData(data) { 54 | if (data.length > 0) { 55 | $scope.videos = []; 56 | 57 | data.forEach(function(item) { 58 | var video = { 59 | id: item.id.videoId, 60 | channelId: item.snippet.channelId, 61 | title: item.snippet.title, 62 | images: { 63 | default: { 64 | url: item.snippet.thumbnails.default.url 65 | }, 66 | medium: { 67 | url: item.snippet.thumbnails.medium.url 68 | }, 69 | high: { 70 | url: item.snippet.thumbnails.high.url 71 | } 72 | } 73 | }; 74 | 75 | $scope.videos.push(video); 76 | }); 77 | } 78 | } 79 | 80 | // Get all url params 81 | function queryString(url) { 82 | var parse_url = /^(?:([a-zA-Z]+):)?(\/{0,3})([0-9.\-a-zA-Z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/, 83 | names = ['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'], 84 | query_string = {}, 85 | result = parse_url.exec(url); 86 | 87 | var vars = result[6].split('&'); 88 | 89 | for (var i = 0; i < vars.length; i++) { 90 | var pair = vars[i].split('='); 91 | query_string[pair[0]] = pair[1]; 92 | } 93 | 94 | return query_string; 95 | } 96 | 97 | function getVideoId(url) { 98 | var query_string = queryString(url); 99 | return query_string.v; 100 | } 101 | } 102 | }()); 103 | -------------------------------------------------------------------------------- /src/assets/js/services/YoutubeService.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var app = angular.module('Youtube.Services'); 5 | app.service('YoutubeService', YoutubeService); 6 | 7 | YoutubeService.$inject = ['$http']; 8 | 9 | function YoutubeService($http) { 10 | // API config 11 | var API = { 12 | url: 'https://www.googleapis.com/youtube/v3', 13 | params: { 14 | key: 'AIzaSyBi6WSVs3D7_93pZQxXAMYmSRPZcAncX6I', 15 | maxResults: 20, 16 | part: 'snippet', 17 | type: 'video' 18 | } 19 | }; 20 | 21 | return { 22 | search: function(query) { 23 | var url = API.url + '/search/?q=' + query; 24 | url += parseParams(API.params); 25 | 26 | return $http.get(url); 27 | } 28 | }; 29 | } 30 | 31 | function parseParams(obj) { 32 | var params = ''; 33 | for (var p in obj) { 34 | if ((obj.hasOwnProperty(p)) && (obj[p] !== '')) { 35 | params += '&' + p + '=' + obj[p]; 36 | } 37 | } 38 | return params; 39 | } 40 | }()); 41 | -------------------------------------------------------------------------------- /src/assets/scss/_bootstrap/_config.scss: -------------------------------------------------------------------------------- 1 | /*===== COLORS =====*/ 2 | $primary: #E12A27; 3 | $gray: #CCC; 4 | 5 | /*===== CONFIG =====*/ 6 | $max-width: 950px; 7 | 8 | /*===== FONTS =====*/ 9 | $default-font: 16px Arial, Helvetica, sans-serif; 10 | -------------------------------------------------------------------------------- /src/assets/scss/_bootstrap/_form.scss: -------------------------------------------------------------------------------- 1 | .input-group { 2 | width: 400px; 3 | float: right; 4 | } 5 | 6 | .input-search { 7 | width: 100%; 8 | padding: 5px 10px; 9 | border: none; 10 | outline: none; 11 | color: darken($gray, 30%); 12 | font-size: 1em; 13 | border: 1px solid $gray; 14 | 15 | @include border-top-left-radius(4px); 16 | @include border-bottom-left-radius(4px); 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/scss/_bootstrap/_init.scss: -------------------------------------------------------------------------------- 1 | body,html { 2 | font: $default-font; 3 | color: darken($gray, 10%); 4 | background: lighten($gray, 10%); 5 | } 6 | 7 | a:hover, a:visited { text-decoration: none; } 8 | 9 | #app { 10 | margin: 50px auto; 11 | padding: 20px; 12 | width: $max-width; 13 | } 14 | 15 | .center { 16 | width: $max-width; 17 | margin: 0 auto; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/scss/_bootstrap/_modal.scss: -------------------------------------------------------------------------------- 1 | #modal { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | z-index: 9999; 6 | width: 100%; 7 | min-height: 100%; 8 | display: none; 9 | background: rgba(#000, 0.7); 10 | 11 | &.modal-active { display: block; } 12 | 13 | .modal-content { 14 | position: absolute; 15 | top: 0; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | padding: 10px; 20 | margin: auto; 21 | width: 700px; 22 | height: 470px; 23 | 24 | iframe { 25 | width: 100%; 26 | height: 450px; 27 | } 28 | 29 | .close-modal { 30 | position: absolute; 31 | top: -60px; 32 | right: 0; 33 | font-size: 35px; 34 | cursor: pointer; 35 | color: #FFF; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/assets/scss/_bootstrap/_videos.scss: -------------------------------------------------------------------------------- 1 | .videos { 2 | position: relative; 3 | display: inline-block; 4 | vertical-align: top; 5 | margin: 20px; 6 | width: 270px; 7 | background: #FFF; 8 | 9 | &:nth-child(3n+1) { margin-left: 0; } 10 | &:nth-child(3n+3) { margin-right: 0; } 11 | 12 | &:hover .videos-section { display: block; } 13 | 14 | .title { 15 | display: block; 16 | margin: 5px 0; 17 | padding: 10px; 18 | color: $primary; 19 | font-size: 15px; 20 | font-weight: normal; 21 | } 22 | 23 | .video-container { 24 | position: relative; 25 | float: left; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | .videos-img { 31 | width: 100%; 32 | height: auto; 33 | } 34 | 35 | .play { 36 | position: absolute; 37 | top: 0; 38 | bottom: 0; 39 | left: 0; 40 | right: 0; 41 | margin: auto; 42 | width: 30px; 43 | height: 30px; 44 | color: #FFF; 45 | font-size: 30px; 46 | } 47 | 48 | .videos-section { 49 | position: absolute; 50 | top: 0; 51 | left: 0; 52 | width: 100%; 53 | height: 100%; 54 | display: none; 55 | background: rgba($primary ,0.7); 56 | 57 | .section-bottom { 58 | position: absolute; 59 | bottom: 40px; 60 | left: 0; 61 | width: 100%; 62 | text-align: center; 63 | 64 | span { 65 | color: #FFF; 66 | font-size: 13px; 67 | } 68 | 69 | .category { padding-right: 5px; } 70 | 71 | .videos-title { 72 | display: block; 73 | width: 85%; 74 | margin: 40px auto; 75 | color: #FFF; 76 | text-align: center; 77 | font-size: 18px; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/assets/scss/_partials/_header.scss: -------------------------------------------------------------------------------- 1 | #header { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | z-index: 9999; 6 | width: 100%; 7 | padding: 10px 0; 8 | background: #FFF; 9 | border-bottom: 1px solid $gray; 10 | 11 | .logo { 12 | float: left; 13 | margin: 0; 14 | font-size: 1.5em; 15 | color: #FFF; 16 | } 17 | 18 | .subtitle { 19 | float: left; 20 | margin: 4px 20px; 21 | font-size: 20px; 22 | color: darken($gray, 40%); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/scss/app.scss: -------------------------------------------------------------------------------- 1 | @import "compass"; 2 | 3 | // Vendors 4 | @import "_vendor/normalize-scss/_normalize"; 5 | @import "_vendor/sass-bootstrap/lib/bootstrap"; 6 | 7 | // Bootstrap app 8 | @import "_bootstrap/_config"; 9 | @import "_bootstrap/_init"; 10 | @import "_bootstrap/_form"; 11 | @import "_bootstrap/_videos"; 12 | @import "_bootstrap/_modal"; 13 | 14 | // Partials app 15 | @import "_partials/_header"; 16 | -------------------------------------------------------------------------------- /src/jade/partials/modal.jade: -------------------------------------------------------------------------------- 1 | #modal(ng-click="closeModal()", ng-class="{'modal-active': modal}") 2 | .modal-content 3 | iframe(width="560", height="315", ng-if="modal", ng-src="{{ currentVideo }}", frameborder="0", allowfullscreen) 4 | a.close-modal(ng-click="closeModal()") × 5 | -------------------------------------------------------------------------------- /src/jade/public/index.jade: -------------------------------------------------------------------------------- 1 | extends ../templates/default.jade 2 | 3 | block content 4 | include ../partials/modal.jade 5 | .row 6 | .videos(ng-repeat="video in videos") 7 | a(href="#", ng-click="videoModal(video.id)") 8 | span.title {{ video.title }} 9 | .video-container 10 | img.videos-img(src="{{ video.images.medium.url }}", title="{{ video.title }}", alt="{{ video.title }}") 11 | span.glyphicon.glyphicon-play.play 12 | -------------------------------------------------------------------------------- /src/jade/templates/default.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html(lang="pt-BR", ng-app="App", ng-controller="YoutubeController") 3 | head 4 | meta(charset="UTF-8") 5 | meta(http-equiv="X-UA-Compatible", content="IE=Edge") 6 | meta(name="viewport", content="width=device-width, initial-scale=1, user-scalable=no") 7 | title {{ query }} - AngularJS Youtube 8 | link(rel="stylesheet", href="/assets/css/app.css") 9 | body 10 | header#header 11 | .center 12 | h1.logo 13 | img(src="http://www.gstatic.com/youtube/img/logo.png", width="72", height="30", alt="Youtube") 14 | h3.subtitle. 15 | A simple app to show your videos 16 | form(role="form", ng-submit="search()") 17 | .input-group 18 | input.input-search(type="text", placeholder="Search by a keyword", ng-model="query") 19 | .input-group-addon: span.glyphicon.glyphicon-search 20 | #app 21 | block content 22 | script(src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js") 23 | script(src="assets/js/app.js") 24 | --------------------------------------------------------------------------------