Skip to content

Commit b51c492

Browse files
committed
remake search, add songs to playlist
1 parent 5139584 commit b51c492

File tree

9 files changed

+242
-23
lines changed

9 files changed

+242
-23
lines changed

apps/playlist/urls.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from apps.playlist import views
44

55
urlpatterns = patterns('apps.playlist.views',
6-
6+
77
url(r'^$', 'index_view',name="home"),
88
url(r'^playlist/create$', 'createPlaylist',name="createPlaylistView"),
99
url(r'^playlist/read$', 'readPlaylist',name="readPlaylistView"),
10+
url(r'^playlist/addSong$', 'addSongToPlaylist',name="readPlaylistView"),
1011

11-
)
12+
)

apps/playlist/views.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
from django.shortcuts import render, render_to_response
22
from apps.playlist.models import Playlist
33
from apps.auth.models import CustomUser
4+
from apps.songs.models import Song
45
from apps.playlist.forms import createPlaylistForm
56
from django.template import RequestContext
67
from django.contrib.auth.decorators import login_required
8+
from django.http import HttpResponse
9+
from django.views.decorators.csrf import csrf_exempt
10+
711

812
# Create your views here.
913
@login_required()
@@ -19,7 +23,7 @@ def createPlaylist(request):
1923
playlist.save()
2024
ctx = {'form':createPlaylistForm(), 'msg':'Lista creada exitosamente'}
2125
return render_to_response('playlist_create.html', ctx, context_instance=RequestContext(request))
22-
26+
2327
else:
2428
form = createPlaylistForm()
2529
ctx = {'form':form}
@@ -38,3 +42,20 @@ def readPlaylist(request):
3842
playlist = Playlist.objects.all()
3943
ctx = {"playlists":playlist}
4044
return render_to_response('playlist.html',ctx,context_instance = RequestContext(request))
45+
46+
@csrf_exempt
47+
def addSongToPlaylist(request):
48+
listName = request.POST.get('listName')
49+
50+
songId = request.POST.get('songId')
51+
songThumbnail = request.POST.get('songThumbnail')
52+
songName = request.POST.get('songName')
53+
54+
song = Song(youtubeId=songId,name=songName,thumbnail=songThumbnail)
55+
song.save()
56+
57+
playlist = Playlist.objects.get(name=listName, user=CustomUser.objects.get(user=request.user))
58+
59+
playlist.songs.add(song)
60+
61+
return HttpResponse('ok')

apps/search/views.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from django.http import HttpResponse
66
from apiclient.discovery import build
77
from optparse import OptionParser
8-
import json
8+
9+
from apps.playlist.models import Playlist
10+
from apps.auth.models import CustomUser
911
# Create your views here.
1012

1113
DEVELOPER_KEY = "AIzaSyCchxKFBGhOmC-y7847rVbNjVep14nb2kk" # TODO move this to an env variable
@@ -25,6 +27,10 @@ def search(request):
2527
maxResults=request.GET.get('max') or 25
2628
).execute()
2729

28-
print type(search_response)
30+
ctx = {}
31+
32+
ctx['results'] = search_response.get('items')
33+
if request.user.is_authenticated():
34+
ctx['lists'] = Playlist.objects.filter(user=CustomUser.objects.get(user= request.user))
2935

30-
return render_to_response('search.html', {'results':search_response.get('items')}, RequestContext(request))
36+
return render_to_response('search.html', ctx, RequestContext(request))

client_secrets.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"client_id":"544131258425-9jsatusf92dnp9ld9k8t10oqp93ljspt.apps.googleusercontent.com",
55
"client_secret":"pXGMsuvZ5R-S7vLAqeXJn706",
6-
"redirect_uris": ["https://orfeo.herokuapp.com/oauth2callback"],
6+
"redirect_uris": ["http://localhost:8000/complete/google-oauth2/"],
77
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
88
"token_uri":"https://accounts.google.com/o/oauth2/token"
99
}

orfeo/settings.py

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
'apps.playlist',
4343
'apps.songs',
4444
'apps.auth',
45+
'apps.search',
4546
)
4647

4748
MIDDLEWARE_CLASSES = (
@@ -113,6 +114,8 @@
113114
GOOGLE_OAUTH2_CLIENT_ID = '544131258425-46tb1igfofj4ijhha6eg1lsd6ikq9dk7.apps.googleusercontent.com'
114115
GOOGLE_OAUTH2_CLIENT_SECRET = 'yled4NNElrceS0tWAGZPMG7p'
115116

117+
GOOGLE_OAUTH_EXTRA_SCOPE = ['https://www.googleapis.com/auth/youtube']
118+
116119
SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer'
117120

118121
LOGIN_URL = '/login/'
+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
!function ($) {
2+
3+
/* MODAL POPOVER PUBLIC CLASS DEFINITION
4+
* =============================== */
5+
6+
var ModalPopover = function (element, options) {
7+
this.options = options;
8+
this.$element = $(element)
9+
.delegate('[data-dismiss="modal-popup"]', 'click.dismiss.modal-popup', $.proxy(this.hide, this));
10+
this.options.remote && this.$element.find('.popover-content').load(this.options.remote);
11+
this.$parent = options.$parent; // todo make sure parent is specified
12+
}
13+
14+
15+
/* NOTE: MODAL POPOVER EXTENDS BOOTSTRAP-MODAL.js
16+
========================================== */
17+
18+
19+
ModalPopover.prototype = $.extend({}, $.fn.modal.Constructor.prototype, {
20+
21+
constructor:ModalPopover,
22+
23+
24+
getPosition:function () {
25+
var $element = this.$parent;
26+
return $.extend({}, ($element.offset()), {
27+
width:$element[0].offsetWidth, height:$element[0].offsetHeight
28+
});
29+
},
30+
31+
show:function () {
32+
var $dialog = this.$element;
33+
$dialog.css({ top:0, left:0, display:'block', 'z-index':1050 });
34+
35+
var placement = typeof this.options.placement == 'function' ?
36+
this.options.placement.call(this, $tip[0], this.$element[0]) :
37+
this.options.placement;
38+
39+
var pos = this.getPosition();
40+
41+
var actualWidth = $dialog[0].offsetWidth;
42+
var actualHeight = $dialog[0].offsetHeight;
43+
44+
var tp;
45+
switch (placement) {
46+
case 'bottom':
47+
tp = {top:pos.top + pos.height, left:pos.left + pos.width / 2 - actualWidth / 2}
48+
break;
49+
case 'top':
50+
tp = {top:pos.top - actualHeight, left:pos.left + pos.width / 2 - actualWidth / 2}
51+
break;
52+
case 'left':
53+
tp = {top:pos.top + pos.height / 2 - actualHeight / 2, left:pos.left - actualWidth}
54+
break;
55+
case 'right':
56+
tp = {top:pos.top + pos.height / 2 - actualHeight / 2, left:pos.left + pos.width}
57+
break;
58+
}
59+
60+
$dialog
61+
.css(tp)
62+
.addClass(placement)
63+
.addClass('in');
64+
65+
66+
$.fn.modal.Constructor.prototype.show.call(this, arguments); // super
67+
},
68+
69+
/** todo entire function was copied just to set the background to 'none'. need a better way */
70+
backdrop:function (callback) {
71+
var that = this
72+
, animate = this.$element.hasClass('fade') ? 'fade' : ''
73+
74+
if (this.isShown && this.options.backdrop) {
75+
var doAnimate = $.support.transition && animate
76+
77+
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" style="background:none" />')
78+
.appendTo(document.body)
79+
80+
if (this.options.backdrop != 'static') {
81+
this.$backdrop.click($.proxy(this.hide, this))
82+
}
83+
84+
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
85+
86+
this.$backdrop.addClass('in')
87+
88+
doAnimate ?
89+
this.$backdrop.one($.support.transition.end, callback) :
90+
callback()
91+
92+
} else if (!this.isShown && this.$backdrop) {
93+
this.$backdrop.removeClass('in')
94+
95+
$.support.transition && this.$element.hasClass('fade') ?
96+
this.$backdrop.one($.support.transition.end, $.proxy(this.removeBackdrop, this)) :
97+
this.removeBackdrop()
98+
99+
} else if (callback) {
100+
callback()
101+
}
102+
}
103+
104+
});
105+
106+
107+
/* MODAL POPOVER PLUGIN DEFINITION
108+
* ======================= */
109+
110+
$.fn.modalPopover = function (option) {
111+
return this.each(function () {
112+
var $this = $(this);
113+
var data = $this.data('modal-popover');
114+
var options = $.extend({}, $.fn.modalPopover.defaults, $this.data(), typeof option == 'object' && option);
115+
// todo need to replace 'parent' with 'target'
116+
options['$parent'] = (data && data.$parent) || option.$parent || $(options.target);
117+
118+
if (!data) $this.data('modal-popover', (data = new ModalPopover(this, options)))
119+
120+
if (typeof option == 'string') data[option]()
121+
})
122+
}
123+
124+
$.fn.modalPopover.Constructor = ModalPopover
125+
126+
$.fn.modalPopover.defaults = $.extend({}, $.fn.modal.defaults, {
127+
placement:'right',
128+
keyboard: true
129+
});
130+
131+
132+
$(function () {
133+
$('body').on('click.modal-popover.data-api', '[data-toggle="modal-popover"]', function (e) {
134+
var $this = $(this);
135+
var href = $this.attr('href');
136+
var $dialog = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))); //strip for ie7
137+
var option = $dialog.data('modal-popover') ? 'toggle' : $.extend({ remote:!/#/.test(href) && href }, $dialog.data(), $this.data());
138+
option['$parent'] = $this;
139+
140+
e.preventDefault();
141+
142+
$dialog
143+
.modalPopover(option)
144+
.modalPopover('show')
145+
.one('hide', function () {
146+
$this.focus()
147+
})
148+
})
149+
})
150+
151+
}(window.jQuery);
152+
//

orfeo/static/js/playlists.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
$('.list-add').on('click', function(event){
2+
event.preventDefault();
3+
var that = $(this);
4+
$.ajax({type:'POST', url:'playlist/addSong', data:$(this).data()}).done(function(response, result){
5+
if(result==='success'){
6+
that.addClass('list-group-item-success');
7+
}
8+
});
9+
});

orfeo/templates/base.html

+7-5
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ <h4 class="text-center">Listas de reproducción</h4>
101101
<a href=""><button type="button" class="col-xs-push-1 col-xs-4 col-md-push-3 col-md-2 btn btn-default">Eliminar lista</button></a>
102102
</section>
103103

104-
105-
<!-- Espacio disponible para el contenido general de la pagina -->
104+
105+
<!-- Espacio disponible para el contenido general de la pagina -->
106106
{% else %}
107107

108108
<section class="container" id="listas">
@@ -134,14 +134,14 @@ <h4>clasica</h4>
134134

135135
</div>
136136
</section>
137-
137+
138138
{% endif %}
139139

140140

141141
{% endblock %}
142-
143142

144-
143+
144+
145145

146146

147147
<!-- ............................................ ................................................................-->
@@ -221,10 +221,12 @@ <h3>En la Web</h3>
221221

222222
<script src="/static/js/jquery-1.10.2.js"></script>
223223
<script src="/static/js/bootstrap.min.js"></script>
224+
<script src="/static/js/bootstrap-modal-popover.js"></script>
224225
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
225226
<script src="/static/js/classie.js"></script>
226227
<script src="/static/js/cbpAnimatedHeader.js"></script>
227228
<script src="/static/js/freelancer.js"></script>
229+
<script src="/static/js/playlists.js"></script>
228230

229231
</body>
230232

orfeo/templates/search.html

+36-11
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,50 @@
11
{% extends "base.html" %}
22

33
{% block content %}
4-
4+
55
<section class="container">
66
<div class="col-lg-12">
77
<h4 class="text-center">Resultados busqueda</h4>
88

9-
<ul class="thumbnails">
109
{% for result in results %}
11-
<li class="col-lg-12">
12-
<div class="thumbnail right-caption col-lg-12">
13-
<img class="col-lg-2" src="{{ result.snippet.thumbnails.default.url }}" alt="">
14-
<div class="caption">
15-
<h5>{{ result.snippet.title }}</h5>
16-
<p>{{ result.snippet.description }}</p>
17-
<p><a href="https://www.youtube.com/watch?v={{ result.id.videoId }}" class="btn">Play</a></p>
10+
<div class="media">
11+
<a class="pull-left" href="#">
12+
<img class="media-object" src="{{ result.snippet.thumbnails.default.url }}" alt="...">
13+
</a>
14+
<div class="media-body">
15+
<h4 class="media-heading">{{ result.snippet.title }}</h4>
16+
{{ result.snippet.description }}
17+
<div class="btn-group">
18+
{% if user.is_authenticated %}
19+
<a href="https://www.youtube.com/watch?v={{ result.id.videoId }}?autoplay=1" class="glyphicon glyphicon-play" target="_blank"></a>
20+
<a class="glyphicon glyphicon-plus-sign" data-toggle="modal" data-target="#popupBottom-{{ result.id.videoId }}"></a>
21+
<div id="popupBottom-{{ result.id.videoId }}" class="modal" tabindex="-1" role="dialog">
22+
<div class="modal-dialog modal-sm">
23+
<div class="modal-content">
24+
<div class="modal-header">
25+
<h5>Agrega {{ result.snippet.title }} a tus Listas</h5>
26+
</div>
27+
<div class="modal-body">
28+
<div class="list-group">
29+
{% for list in lists %}
30+
<a class="list-group-item list-add" data-list-name="{{ list.name }}" data-song-id="{{ result.id.videoId }}" data-song-name="{{ result.snippet.title }}" data-song-thumbnail="{{ result.snippet.thumbnails.default.url }}" href="#">
31+
<h4 class="list-group-item-heading">{{ list.name }}</h4>
32+
</a>
33+
{% endfor %}
34+
</div>
35+
</div>
36+
</div>
37+
</div>
1838
</div>
39+
{% else %}
40+
<!-- TODO change this link to a popup explainin why loging :P -->
41+
<a href="/login/google-oauth2/" class="btn glyphicon glyphicon-play" role="button" target="_blank"></a>
42+
<a href="/login/google-oauth2/" class="btn glyphicon glyphicon-plus-sign" role="button"></a>
43+
{% endif %}
1944
</div>
20-
</li>
45+
</div>
46+
</div>
2147
{% endfor %}
22-
</ul>
2348

2449
</div>
2550
</section>

0 commit comments

Comments
 (0)