Skip to content

Loading…

Extend playlist #24

Merged
merged 6 commits into from

2 participants

@kracekumar

Added Extend playlist future.

@jace jace commented on an outdated diff
hgtv/forms/playlist.py
@@ -40,5 +41,12 @@ class PlaylistAddForm(Form):
playlist = wtf.SelectField('Add to playlist', coerce=int)
+def playlist_validate_url(self, field):
+ youtube_regex = re.compile(r'(http|https)://www.youtube.com/playlist?([0-9A-Za-z]+.)', re.UNICODE)
@jace HasGeek member
jace added a note

Move this to module level so that you don't recompile each time the validator is called -- that's no different from using a raw regex string. We should also establish a convention for naming regex strings. I've been using the _re suffix in other places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jace jace commented on an outdated diff
hgtv/views/playlist.py
@@ -49,7 +49,7 @@ def inner(start_index=1, max_result=50, total=0):
# If the video is private still youtube provides the title but doesn't
# provide thumbnail & urls, check for private video
is_private = item.get('app$control')
- if is_private is not None and is_private['yt$state']['reasonCode']:
+ if is_private is not None and is_private.get('yt$state') and is_private.get('yt$state').get('reasonCode'):
@jace HasGeek member
jace added a note

This bit doesn't make sense: is_private.get('yt$state').get('reasonCode')

The .get() call is used when the key may not exist, but if it does not exist, the following .get() will fail. Your line should be:

if is_private is not None and is_private.get('yt$state') and is_private['yt$state'].get('reasonCode'):

@jace HasGeek member
jace added a note

That is, you are testing for None already, so the .get() is redundant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jace jace commented on an outdated diff
hgtv/views/playlist.py
((6 lines not shown))
video.thumbnail_path = thumbnails.save(filestorage)
video.video_sourceid = item['media$group']['yt$videoid']['$t']
video.video_source = u"youtube"
video.make_name()
- playlist.videos.append(video)
+ if not Video.query.filter_by(video_url=video.video_url, playlist=playlist).first():
@jace HasGeek member
jace added a note

Don't limit the query by current playlist. If the video is already in another playlist, just add it from there.

Also, video_url can vary for the same URL since Youtube sometimes adds extra characters like ?plcp=1. Look for unique video_source and video_sourceid instead.

I wrote this query to avoid duplicate videos for extend playlist(currently youtube allows duplicate videos to exist in playlist). Yes check should be for video_sourceid, now I feel check should be made before creating video object, and check for entire video list and ignore if it is in current playlist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jace jace commented on an outdated diff
hgtv/views/playlist.py
@@ -79,8 +81,7 @@ def inner(start_index=1, max_result=50, total=0):
raise DataProcessingError("Unable to establish connection")
except gaierror:
raise DataProcessingError("Unable to resolve the hostname")
- except KeyError:
- raise
+ except KeyError, key:
@jace HasGeek member
jace added a note

Shouldn't that be Keyerror, e since the second item is the exception object, not the key? Also, is it needed? Your exception handler isn't doing anything with the exception object.

Yes, it should be e. Actually I wanted to capture particular KeyError like yt$state later figured out you can't do it. It should be e.key == 'key', will remove it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jace jace commented on an outdated diff
hgtv/views/playlist.py
((7 lines not shown))
if 'media$description' in r.json['feed']['media$group']:
playlist.description = escape(r.json['feed']['media$group']['media$description']['$t'])
for item in r.json['feed'].get('entry', []):
# If the video is private still youtube provides the title but doesn't
# provide thumbnail & urls, check for private video
is_private = item.get('app$control')
- if is_private is not None and is_private['yt$state']['reasonCode']:
+ if is_private is not None and is_private.get('yt$state') and is_private['yt$state'].get('reasonCode'):
@jace HasGeek member
jace added a note

Just realized you can replace the two lines above with this one:

if item.get('app$control', {}).get('yt$state', {}).get('reasonCode'):  # Is it private?
    continue

The comment is to explain what this is checking for since the variable names aren't intuitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jace jace merged commit e589bed into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 6, 2012
  1. @kracekumar

    Added extend playlist video

    kracekumar committed
  2. @kracekumar

    Small tweaks

    kracekumar committed
  3. @kracekumar

    Removed Ajax chosen work

    kracekumar committed
Commits on Dec 7, 2012
  1. @kracekumar
Commits on Dec 10, 2012
  1. @kracekumar
  2. @kracekumar
View
10 hgtv/forms/playlist.py
@@ -1,3 +1,4 @@
+
# -*- coding: utf-8 -*-
import re
@@ -11,6 +12,7 @@
invalid_name = re.compile(r'[^\w._-]', re.UNICODE)
+youtube_playlist_regex = re.compile(r'(http|https)://www.youtube.com/playlist?([0-9A-Za-z]+.)', re.UNICODE)
class PlaylistForm(Form):
@@ -40,5 +42,11 @@ class PlaylistAddForm(Form):
playlist = wtf.SelectField('Add to playlist', coerce=int)
+def playlist_validate_url(self, field):
+ if not youtube_playlist_regex.search(field.data):
+ raise wtf.ValidationError("InCorrect Youtube Playlist URL")
+
+
class PlaylistImportForm(Form):
- playlist_url = wtf.html5.URLField(u"Playlist URL", validators=[wtf.Required()])
+ playlist_url = wtf.html5.URLField(u"Playlist URL", validators=[wtf.Required(), playlist_validate_url])
+
View
2 hgtv/models/channel.py
@@ -210,6 +210,8 @@ def url_for(self, action='view'):
return url_for('playlist_view', channel=self.channel.name, playlist=self.name)
elif action == 'edit':
return url_for('playlist_edit', channel=self.channel.name, playlist=self.name)
+ elif action == 'extend':
+ return url_for('playlist_extend', channel=self.channel.name, playlist=self.name)
elif action == 'delete':
return url_for('playlist_delete', channel=self.channel.name, playlist=self.name)
elif action == 'new-video':
View
4 hgtv/models/video.py
@@ -96,6 +96,10 @@ def url_for(self, action='view', channel=None, playlist=None, _external=False):
return url_for('video_add_speaker',
channel=channel.name, playlist=playlist.name,
video=self.url_name, _external=_external)
+ elif action == 'autocomplete-speaker':
+ return url_for('video_autocomplete_speaker',
+ channel=channel.name, playlist=playlist.name,
+ video=self.url_name, _external=_external)
elif action == 'remove-speaker':
return url_for('video_remove_speaker',
channel=channel.name, playlist=playlist.name,
View
63 hgtv/templates/playlist.html
@@ -26,6 +26,7 @@
{%- endif %}
{% if 'edit' in g.permissions -%}
<a href="{{ playlist.url_for('edit') }}"><span class="icon-pencil">Edit playlist</span></a>
+ <a href="{{ playlist.url_for('extend') }}" class='extend-playlist' data-method='GET'><span class="icon-pencil">Extend playlist</span></a>
{%- endif %}
</div>
</div>
@@ -75,4 +76,66 @@
</div>
{% endblock %}
{% endblock %}
+{% endblock %}
+{% block footerscripts %}
+<script type="text/javascript">
+ $(function() {
+ var playlist_action = function(url, type) {
+ $.ajax({
+ type: type,
+ url: url,
+ success: function(msg) {
+ console.log(msg);
+ if (msg['message_type'] === 'success') {
+ if (msg['action'] === 'modal-window') {
+ $(msg['html']).modal({show: true, keyboard: true}).bind('click keydown', function(event) {
+ //code for on enter keypress & submit button
+ var modalWindow = $(this);
+ //e.which === 1 says button is clicked
+ if (event.keyCode === 13 || event.which === 1) {
+ if ($(event.target).hasClass('btn-primary') || event.keyCode === 13) {
+ event.preventDefault();
+ $("#modal-form").ajaxSubmit({
+ beforeSubmit: function() {
+ $(".loading").removeClass('hidden');
+ },
+ success: function(msg) {
+ if (msg['message_type'] === 'success') {
+ if (msg['action'] === 'append') {
+ $(msg['html']).insertBefore($('.divider'));
+ toastr.success(msg['message']);
+ } else if (msg['action'] === 'redirect'){
+ window.location = msg['url'];
+ } else if (msg['action'] === 'noop') {
+ toastr.success(msg['message']);
+ }
+ $(modalWindow).modal('hide');
+ $(modalWindow).empty(); //remove modal window once playlist is created.
+ } else if (msg['message_type'] === 'error') {
+ $(modalWindow).html(msg['html']);
+ } else if (msg['message_type'] === 'server-error') {
+ toastr.error(msg['message']);
+ }
+ },
+ error: function(msg) {
+ toastr.error("Please try after sometime");
+ $(modalWindow).modal('hide');
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+ },
+ error: function(msg) {}
+ });
+ }
+ $("a.extend-playlist").on('click', function(event) {
+ event.preventDefault();
+ playlist_action($(this).attr('href'), $(this).attr('data-method'));
+
+ });
+ });
+</script>
{%- endblock %}
View
1 hgtv/templates/video.html
@@ -233,7 +233,6 @@
{%- endblock %} <!-- /block main -->
{% block footerscripts %}
- <script type="text/javascript" src="http://connect2campus.in/slides/swfobject.js"></script>
{% if g.user %}
<script type="text/javascript">
$(function() {
View
3 hgtv/templates/videoedit.html
@@ -136,9 +136,8 @@
},
});
};
-
//Enable Add button on user input, disable if empty
- $('#speaker_name').on('keydown', function(event){
+ $('#speaker_name').on('keydown', function(event){
if (event.keyCode === 13) {
event.preventDefault();
if ($("#speaker_name").val() !== '') {
View
89 hgtv/views/playlist.py
@@ -5,7 +5,7 @@
import requests
from werkzeug import secure_filename
-from flask import render_template, flash, escape
+from flask import render_template, flash, escape, request, jsonify
from coaster.views import load_model, load_models
from baseframe.forms import render_redirect, render_form, render_delete_sqla
from hgtv import app
@@ -42,30 +42,43 @@ def inner(start_index=1, max_result=50, total=0):
raise DataProcessingError("Unable to fetch data, please check the youtube url")
else:
# fetch playlist info
- playlist.title = r.json['feed']['title']['$t']
+ # prevent overwriting title during Extend playlist
+ playlist.title = playlist.title or r.json['feed']['title']['$t']
if 'media$description' in r.json['feed']['media$group']:
playlist.description = escape(r.json['feed']['media$group']['media$description']['$t'])
for item in r.json['feed'].get('entry', []):
- # If the video is private still youtube provides the title but doesn't
- # provide thumbnail & urls, check for private video
- is_private = item.get('app$control')
- if is_private is not None and is_private['yt$state']['reasonCode']:
+ if item.get('app$control', {}).get('yt$state', {}).get('reasonCode'): # Is it private?
continue
- video = Video(playlist=playlist)
- video.title = item['title']['$t']
- video.video_url = item['media$group']['media$player']['url']
- if 'media$description' in item['media$group']:
- video.description = escape(item['media$group']['media$description']['$t'])
- for video_content in item['media$group']['media$thumbnail']:
- if video_content['yt$name'] == 'mqdefault':
- thumbnail_url_request = requests.get(video_content['url'])
- filestorage = return_werkzeug_filestorage(thumbnail_url_request,
- filename=secure_filename(item['title']['$t']))
- video.thumbnail_path = thumbnails.save(filestorage)
- video.video_sourceid = item['media$group']['yt$videoid']['$t']
- video.video_source = u"youtube"
- video.make_name()
- playlist.videos.append(video)
+ videos = Video.query.filter_by(video_source=u"youtube", video_sourceid=item['media$group']['yt$videoid']['$t']).all()
+ if videos:
+ # If video isn't present in current playlist, copy the video parameters
+ if not filter(lambda video: video.playlist == playlist, videos):
+ new_video = Video(playlist=playlist)
+ video = videos[0]
+ new_video.name = video.name
+ new_video.title = video.title
+ new_video.video_url = video.video_url
+ new_video.description = video.description
+ new_video.thumbnail_path = video.thumbnail_path
+ new_video.video_source = u"youtube"
+ new_video.video_sourceid = video.video_sourceid
+ playlist.videos.append(new_video)
+ else:
+ video = Video(playlist=playlist)
+ video.title = item['title']['$t']
+ video.video_url = item['media$group']['media$player']['url']
+ if 'media$description' in item['media$group']:
+ video.description = escape(item['media$group']['media$description']['$t'])
+ for video_content in item['media$group']['media$thumbnail']:
+ if video_content['yt$name'] == 'mqdefault':
+ thumbnail_url_request = requests.get(video_content['url'])
+ filestorage = return_werkzeug_filestorage(thumbnail_url_request,
+ filename=secure_filename(item['title']['$t']) or 'name-missing')
+ video.thumbnail_path = thumbnails.save(filestorage)
+ video.video_sourceid = item['media$group']['yt$videoid']['$t']
+ video.video_source = u"youtube"
+ video.make_name()
+ playlist.videos.append(video)
#When no more data is present to retrieve in playlist 'feed' is absent in json
if 'entry' in r.json['feed']:
total += len(r.json['feed']['entry'])
@@ -80,7 +93,6 @@ def inner(start_index=1, max_result=50, total=0):
except gaierror:
raise DataProcessingError("Unable to resolve the hostname")
except KeyError:
- raise
raise DataProcessingError("Supplied youtube URL doesn't contain video information")
else:
raise ValueError("Unsupported video site")
@@ -173,3 +185,36 @@ def playlist_import(channel):
return render_redirect(playlist.url_for(), code=303)
return render_form(form=form, title="Import Playlist", submit=u"Import",
cancel_url=channel.url_for(), ajax=False)
+
+
+@app.route('/<channel>/<playlist>/extend', methods=['GET', 'POST'])
+@lastuser.requires_login
+@load_models(
+ (Channel, {'name': 'channel'}, 'channel'),
+ (Playlist, {'name': 'playlist', 'channel': 'channel'}, 'playlist'),
+ permission='edit')
+def playlist_extend(channel, playlist):
+ form = PlaylistImportForm()
+ form.channel = channel
+ html = render_template('playlist-extend.html', form=form, channel=channel, playlist=playlist)
+ if request.is_xhr:
+ if form.validate_on_submit():
+ playlist_url = escape(form.playlist_url.data)
+ initial_count = len(playlist.videos)
+ try:
+ process_playlist(playlist_url=playlist_url, playlist=playlist)
+ except:
+ return jsonify({'message_type': "server-error",
+ 'message': 'Oops, something went wrong, please try later'})
+ additions = (len(playlist.videos) - initial_count)
+ if additions:
+ db.session.commit()
+ flash(u"Added '%d' videos" % (len(playlist.videos) - initial_count), 'success')
+ return jsonify({'message_type': "success", 'action': 'redirect', 'url': playlist.url_for()})
+ return jsonify({'message_type': "success", 'action': 'noop', 'message': 'Already upto date'})
+ if form.errors:
+ html = render_template('playlist-extend.html', form=form, channel=channel, playlist=playlist)
+ return jsonify({'message_type': "error", 'action': 'append',
+ 'html': html})
+ return jsonify ({'action': 'modal-window', 'message_type': 'success', 'html': html})
+ return html
Something went wrong with that request. Please try again.