Permalink
Browse files

New new work. Videos are anchored to their primary playlist again.

  • Loading branch information...
1 parent f430364 commit b97f1b832b73f784a60c3717ce96e36256e3035d @jace jace committed Apr 29, 2012
View
@@ -1,2 +1,3 @@
+from hgtv.forms.channel import *
from hgtv.forms.playlist import *
from hgtv.forms.video import *
View
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+import flask.ext.wtf as wtf
+from baseframe.forms import Form, RichTextField
+
+__all__ = ['ChannelForm']
+
+
+class ChannelForm(Form):
+ type = wtf.SelectField(u"Channel type", coerce=int, validators=[wtf.Required()])
+ description = RichTextField(u"Description")
View
@@ -1,13 +1,37 @@
# -*- coding: utf-8 -*-
-from baseframe.forms import Form
+import re
+from baseframe.forms import Form, RichTextField
import flask.ext.wtf as wtf
+from hgtv.models import Playlist
+
__all__ = ['PlaylistForm', 'PlaylistAddForm']
+invalid_name = re.compile(r'\s', re.UNICODE)
+
+
class PlaylistForm(Form):
- title = wtf.TextField('Title', validators=[wtf.Required()])
+ title = wtf.TextField(u"Title", validators=[wtf.Required()],
+ description=u"The name of your playlist")
+ short_title = wtf.TextField(u"Short title", validators=[wtf.Optional()],
+ description=u"Shorter title, displayed next to the channel's name when viewing a video")
+ name = wtf.TextField(u"URL Name", validators=[wtf.Optional()],
+ description=u"Optional. Will be automatically generated if left blank")
+ description = RichTextField(u"Description")
+
+ def validate_name(self, field):
+ if invalid_name.search(field.data):
+ raise wtf.ValidationError("The name cannot have spaces")
+ if self.edit_obj:
+ edit_id = self.edit_obj.id
+ else:
+ edit_id = None
+ existing = Playlist.query.filter_by(channel=self.channel, name=field.data).first()
+ if existing and existing.id != edit_id:
+ raise wtf.ValidationError("That name is already in use")
+
class PlaylistAddForm(Form):
playlist = wtf.SelectField('Add to playlist', coerce=int)
View
@@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
-from baseframe.forms import Form
+from baseframe.forms import Form, RichTextField
import flask.ext.wtf as wtf
__all__ = ['VideoAddForm', 'VideoEditForm']
+
class VideoAddForm(Form):
- url = wtf.TextField('Video URL', validators=[wtf.Required()])
+ video_url = wtf.html5.URLField('Video URL', validators=[wtf.Required()])
+ slides_url = wtf.html5.URLField('Slides URL', validators=[wtf.Optional()])
class VideoEditForm(Form):
- description = wtf.TextAreaField('Description')
- url = wtf.TextField('Video URL', validators=[wtf.Required()])
- slides = wtf.TextField('Slides URL')
+ title = wtf.TextField('Title', validators=[wtf.Required()])
+ description = RichTextField('Description')
+ slides_url = wtf.html5.URLField('Slides URL', validators=[wtf.Optional()])
View
@@ -2,11 +2,11 @@
from flask.ext.sqlalchemy import SQLAlchemy
from hgtv import app
-from coaster.sqlalchemy import BaseMixin, BaseNameMixin
+from coaster.sqlalchemy import TimestampMixin, BaseMixin, BaseNameMixin, BaseIdNameMixin
db = SQLAlchemy(app)
+from hgtv.models.video import *
from hgtv.models.channel import *
-from hgtv.models.tag import *
from hgtv.models.user import *
-from hgtv.models.video import *
+from hgtv.models.tag import *
View
@@ -1,29 +1,77 @@
# -*- coding: utf-8 -*-
+from sqlalchemy.ext.orderinglist import ordering_list
+from sqlalchemy.ext.associationproxy import association_proxy
+
from hgtv.models import db, BaseNameMixin
+from hgtv.models.video import ChannelVideo, PlaylistVideo
+
+
+__all__ = ['CHANNEL_TYPE', 'PLAYLIST_TYPE', 'Channel', 'Playlist']
+
+
+class CHANNEL_TYPE:
+ UNDEFINED = 0
+ PERSON = 1
+ ORGANIZATION = 2
+ EVENTSERIES = 3
+
+
+class PLAYLIST_TYPE:
+ REGULAR = 0
+ EVENT = 1
+ WATCHED = 2
+ ATTENDED = 3
+
+
+channel_types = {
+ 0: u"Channel",
+ 1: u"Person",
+ 2: u"Organization",
+ 3: u"Event Series",
+ }
+
+
+playlist_types = {
+ 0: u"Playlist",
+ 1: u"Event",
+ }
class Channel(db.Model, BaseNameMixin):
__tablename__ = 'channel'
userid = db.Column(db.Unicode(22), nullable=False, unique=True)
+ description = db.Column(db.UnicodeText, default=u'', nullable=False)
featured = db.Column(db.Boolean, default=False, nullable=False)
+ type = db.Column(db.Integer, default=CHANNEL_TYPE.UNDEFINED, nullable=False)
+
+ _videos = db.relationship(ChannelVideo,
+ order_by=[ChannelVideo.seq],
+ collection_class=ordering_list('seq'),
+ backref='channel',
+ cascade='all, delete-orphan')
+ videos = association_proxy('_videos', 'video', creator=lambda x: ChannelVideo(video=x))
+
+ def type_label(self):
+ return channel_types.get(self.type, channel_types[0])
class Playlist(db.Model, BaseNameMixin):
__tablename__ = 'playlist'
+ short_title = db.Column(db.Unicode(80), nullable=False, default=u'')
channel_id = db.Column(db.Integer, db.ForeignKey('channel.id'), nullable=False)
channel = db.relationship(Channel, primaryjoin=channel_id == Channel.id,
backref=db.backref('playlists', cascade='all, delete-orphan'))
+ description = db.Column(db.UnicodeText, default=u'', nullable=False)
featured = db.Column(db.Boolean, default=False, nullable=False)
+ type = db.Column(db.Integer, default=PLAYLIST_TYPE.REGULAR, nullable=False)
+ _videos = db.relationship(PlaylistVideo,
+ order_by=[PlaylistVideo.seq],
+ collection_class=ordering_list('seq'),
+ backref='playlist',
+ cascade='all, delete-orphan')
+ videos = association_proxy('_videos', 'video', creator=lambda x: PlaylistVideo(video=x))
-channels_videos = db.Table('channels_videos',
- db.Column('channel_id', db.Integer, db.ForeignKey('channel.id'), nullable=False),
- db.Column('video_id', db.Integer, db.ForeignKey('video.id'), nullable=False)
- )
-
-
-playlists_videos = db.Table('playlists_videos',
- db.Column('playlist_id', db.Integer, db.ForeignKey('playlist.id'), nullable=False),
- db.Column('video_id', db.Integer, db.ForeignKey('video.id'), nullable=False)
- )
+ def type_label(self):
+ return playlist_types.get(self.type, playlist_types[0])
View
@@ -38,6 +38,7 @@ def rename(self, title):
self.name = name
self.title = title
+
tags_videos = db.Table('tags_videos',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
db.Column('video_id', db.Integer, db.ForeignKey('video.id'))
View
@@ -1,13 +1,28 @@
# -*- coding: utf-8 -*-
-from flask import g
+from flask import g, url_for
from flask.ext.lastuser.sqlalchemy import UserBase
from hgtv.models import db
+from hgtv.models.channel import Channel
__all__ = ['User']
+
class User(db.Model, UserBase):
__tablename__ = 'user'
+ @property
+ def profile_url(self):
+ return url_for('channel_view', channel=self.username or self.userid)
+
+ @property
+ def channel(self):
+ return Channel.query.filter_by(userid=self.userid).first()
+
+ @property
+ def channels(self):
+ return Channel.query.filter(Channel.userid.in_(self.user_organization_ids())).all()
+
+
def default_user(context):
return g.user.id if g.user else None
View
@@ -1,60 +1,91 @@
-#!/usr/bin/env python
-# -*- coding: iso-8859-15 -*-
+# -*- coding: utf-8 -*-
-from hgtv.models import db, BaseNameMixin
-from hgtv.models.tag import tags_videos, Tag
-from hgtv.models.channel import Channel, channels_videos, playlists_videos
+from sqlalchemy.ext.associationproxy import association_proxy
import requests
-import re
from urlparse import urlparse, parse_qs
-import json
+from flask import json, escape
+from hgtv.models import db, TimestampMixin, BaseIdNameMixin
-__all__ = ['Video']
+from hgtv.models.tag import tags_videos
+__all__ = ['ChannelVideo', 'PlaylistVideo', 'Video']
-class Video(db.Model, BaseNameMixin):
+
+class ChannelVideo(db.Model, TimestampMixin):
+ __tablename__ = 'channel_video'
+ channel_id = db.Column(db.Integer, db.ForeignKey('channel.id'), primary_key=True)
+ video_id = db.Column(db.Integer, db.ForeignKey('video.id'), primary_key=True)
+ video = db.relationship('Video', backref=db.backref('_channels', cascade='all, delete-orphan'))
+ seq = db.Column(db.Integer, nullable=False)
+
+
+class PlaylistVideo(db.Model, TimestampMixin):
+ __tablename__ = 'playlist_video'
+ playlist_id = db.Column(db.Integer, db.ForeignKey('playlist.id'), primary_key=True)
+ video_id = db.Column(db.Integer, db.ForeignKey('video.id'), primary_key=True)
+ video = db.relationship('Video', backref=db.backref('_playlists', cascade='all, delete-orphan'))
+ seq = db.Column(db.Integer, nullable=False)
+
+
+class Video(db.Model, BaseIdNameMixin):
__tablename__ = 'video'
- channel_id = db.Column(db.Integer, db.ForeignKey('channel.id'), nullable=False)
- channel = db.relationship(Channel, primaryjoin=channel_id == Channel.id,
- backref=db.backref('videos', cascade='all, delete-orphan'))
+ playlist_id = db.Column(db.Integer, db.ForeignKey('playlist.id'), nullable=False)
+ playlist = db.relationship('Playlist',
+ backref=db.backref('primary_videos', cascade='all, delete-orphan'))
+ channel = association_proxy('playlist', 'channel')
description = db.Column(db.UnicodeText, nullable=False, default=u'')
- url = db.Column(db.Unicode(250), nullable=False)
- slides = db.Column(db.Unicode(250), nullable=False, default=u'')
+ video_url = db.Column(db.Unicode(250), nullable=False)
+ slides_url = db.Column(db.Unicode(250), nullable=False, default=u'')
thumbnail_url = db.Column(db.Unicode(250), nullable=True, default=u'')
+ video_html = db.Column(db.Unicode(250), nullable=False, default=u'')
+ slides_html = db.Column(db.Unicode(250), nullable=False, default=u'')
+
+ channels = association_proxy('_channels', 'channel', creator=lambda x: ChannelVideo(channel=x))
+ playlists = association_proxy('_playlists', 'playlist', creator=lambda x: PlaylistVideo(playlist=x))
+
tags = db.relationship('Tag', secondary=tags_videos, backref=db.backref('videos'))
- channels = db.relationship('Channel', secondary=channels_videos, backref=db.backref('tagged_videos'))
- playlists = db.relationship('Playlist', secondary=playlists_videos, backref=db.backref('videos'))
def __repr__(self):
- return u'<Video %s>' % self.name
+ return u'<Video %s>' % self.url_name
- def get_metadata(self):
+ # FIXME: Move these into the view, out of the model
+ def process_video(self):
"""
- Get Metadata for the video from the corresponding site
+ Get metadata for the video from the corresponding site
"""
# Parse the video url
- if self.url:
- parsed = urlparse(self.url)
- else:
- return None
- # Check video source and get corresponding data
- if parsed.netloc == 'youtube.com' or 'www.youtube.com':
- self.get_youtube_data(parsed)
-
- def get_youtube_data(self, parsed):
+ if self.video_url:
+ parsed = urlparse(self.video_url)
+ # Check video source and get corresponding data
+ if parsed.netloc in ['youtube.com', 'www.youtube.com']:
+ video_id = parse_qs(parsed.query)['v'][0]
+ r = requests.get('https://gdata.youtube.com/feeds/api/videos/%s?v=2&alt=json' % video_id)
+ data = json.loads(r.text)
+ self.title = data['entry']['title']['$t']
+ self.description = escape(data['entry']['media$group']['media$description']['$t'])
+ for item in data['entry']['media$group']['media$thumbnail']:
+ if item['yt$name'] == 'mqdefault':
+ self.thumbnail_url = item['url'] # .replace('hqdefault', 'mqdefault')
+ self.video_html = '<iframe src="http://www.youtube.com/embed/%s?wmode=transparent&autoplay=1" frameborder="0" allowfullscreen></iframe>' % video_id
+ else:
+ raise ValueError("Unsupported video site")
+
+ def process_slides(self):
"""
- Get Metadata from youtube
+ Get metadata for slides from the corresponding site
"""
- video = parse_qs(parsed.query)['v'][0]
- r = requests.get('https://gdata.youtube.com/feeds/api/videos/%s?v=2&alt=json' % video)
- data = json.loads(r.text)
- self.title = data['entry']['title']['$t']
- self.description = data['entry']['media$group']['media$description']['$t']
- for item in data['entry']['media$group']['media$thumbnail']:
- if item['yt$name'] == 'hqdefault':
- self.thumbnail_url = item['url']
- for item in data['entry']['category']:
- if item['scheme'] == 'http://gdata.youtube.com/schemas/2007/keywords.cat':
- Tag.get(item['term'])
+ if self.slides_url:
+ parsed = urlparse(self.slides_url)
+ if parsed.netloc in ['slideshare.net', 'www.slideshare.net']:
+ r = requests.get('http://www.slideshare.net/api/oembed/2?url=%s&format=json' % self.slides_url)
+ data = json.loads(r.text)
+ slides_id = data['slideshow_id']
+ self.slides_html = '<iframe src="http://www.slideshare.net/slideshow/embed_code/%s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>' % slides_id
+ elif parsed.netloc in ['speakerdeck.com', 'www.speakerdeck.com']:
+ r = requests.get('http://speakerdeck.com/oembed.json?url=%s' % self.slides_url)
+ data = json.loads(r.text)
+ self.slides_html = data['html']
+ else:
+ raise ValueError("Unsupported slides site")
Oops, something went wrong.

0 comments on commit b97f1b8

Please sign in to comment.