class PlaylistVideo(TimestampMixin, db.Model): __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) description = db.Column(db.UnicodeText, nullable=False, default=True)
class PlaylistRedirect(BaseMixin, db.Model): __tablename__ = "playlist_redirect" channel_id = db.Column(None, db.ForeignKey('channel.id'), nullable=False) channel = db.relationship(Channel, backref=db.backref('playlist_redirects', cascade='all, delete-orphan')) name = db.Column(db.Unicode(250), nullable=False) playlist_id = db.Column(None, db.ForeignKey('playlist.id'), nullable=False) playlist = db.relationship(Playlist, backref=db.backref( 'redirects', cascade='all, delete-orphan')) __table_args__ = (db.UniqueConstraint(channel_id, name), ) def redirect_view_args(self): return {'playlist': self.playlist.name} @classmethod def migrate_profile(cls, oldchannel, newchannel): """ There's no point trying to migrate playlists when merging channels, so discard them. """ oldchannel.playlist_redirects = [] return [cls.__table__.name]
class ChannelVideo(TimestampMixin, db.Model): __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) relation = db.Column( db.Integer, nullable=False) # Describes why the channel is linked to the video
class Playlist(BaseScopedNameMixin, db.Model): __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')) parent = db.synonym('channel') description = db.Column(db.UnicodeText, default=u'', nullable=False) public = db.Column(db.Boolean, nullable=False, default=True) recorded_date = db.Column(db.Date, nullable=True) published_date = db.Column(db.Date, nullable=False, default=date.today) featured = db.Column(db.Boolean, default=False, nullable=False) type = db.Column(db.Integer, default=PLAYLIST_TYPE.REGULAR, nullable=False) auto_type = db.Column(db.Integer, nullable=True) __table_args__ = (db.UniqueConstraint('channel_id', 'auto_type'), db.UniqueConstraint('channel_id', 'name')) _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)) def __repr__(self): if self.auto_type: return '<AutoPlaylist %s of %s>' % (self.type_label(), self.channel.title) else: return '<Playlist %s of %s>' % (self.title, self.channel.title) @classmethod def get_featured(cls, count): return cls.query.filter_by( public=True, auto_type=None, featured=True).order_by( 'featured').order_by('updated_at').limit(count).all() def type_label(self): if self.auto_type is not None: return playlist_auto_types.get(self.auto_type) else: return playlist_types.get(self.type, playlist_types[0]) def permissions(self, user, inherited=None): perms = super(Playlist, self).permissions(user, inherited) if self.public: perms.add('view') if user and self.channel.userid in user.user_organizations_owned_ids(): perms.add('view') # In case playlist is not public perms.add('edit') perms.add('delete') perms.add('new-video') perms.add('add-video') perms.add('remove-video') return perms def url_for(self, action='view'): if 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 == 'delete': return url_for('playlist_delete', channel=self.channel.name, playlist=self.name) elif action == 'new-video': return url_for('video_new', channel=self.channel.name, playlist=self.name) # The remove-video view URL is in Video, not here. Only the permission comes from here. def next(self, video): return Video.query.filter_by(id=video.id + 1, playlist=self).first() def prev(self, video): return Video.query.filter_by(id=video.id - 1, playlist=self).first()
class Video(BaseIdNameMixin, CommentingMixin, db.Model): __tablename__ = 'video' 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'') video_url = db.Column(db.Unicode(250), nullable=False) slides_url = db.Column(db.Unicode(250), nullable=False, default=u'') thumbnail_path = db.Column(db.Unicode(250), nullable=True, default=u'') video_source = db.Column(db.Unicode(80), nullable=False, default=u'') video_sourceid = db.Column(db.Unicode(80), nullable=False, default=u'') slides_source = db.Column(db.Unicode(80), nullable=False, default=u'') slides_sourceid = db.Column(db.Unicode(80), nullable=False, default=u'') video_slides_mapping = db.Column(db.UnicodeText, nullable=True, default=u'') video_slides_mapping_json = db.Column(db.UnicodeText, nullable=True, default=u'') playlists = association_proxy('_playlists', 'playlist', creator=lambda x: PlaylistVideo(playlist=x)) tags = db.relationship('Tag', secondary=tags_videos, backref=db.backref('videos')) def __repr__(self): return u'<Video %s>' % self.url_name def permissions(self, user, inherited=None): perms = super(Video, self).permissions(user, inherited) perms.add('view') if user and self.channel.userid in user.user_organizations_owned_ids(): perms.add('edit') perms.add('delete') else: perms.discard('edit') perms.discard('delete') # Allow speakers to edit if user: pl = user.channel.playlist_for_speaking_in() if pl and self in pl.videos: perms.add('edit') return perms def url_for(self, action='view', channel=None, playlist=None, _external=False): channel = channel or self.channel playlist = playlist or self.playlist if playlist.channel != channel or playlist not in self.playlists: return if action == 'view': return url_for('video_view', videopath='%s/%s/%s' % (channel.name, playlist.name, self.url_name), _external=_external) elif action == 'remove-video': return url_for('video_remove', channel=channel.name, playlist=playlist.name, video=self.url_name, _external=_external) # Edit and Delete can only be from the source playlist elif action == 'edit': return url_for('video_edit', channel=self.channel.name, playlist=self.playlist.name, video=self.url_name, _external=_external) elif action == 'delete': return url_for('video_delete', channel=self.channel.name, playlist=self.playlist.name, video=self.url_name, _external=_external) elif action == 'add-speaker': 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, video=self.url_name, _external=_external) elif action == 'action': return url_for('video_action', channel=channel.name, playlist=playlist.name, video=self.url_name, _external=_external) def embed_video_for(self, action='view'): if self.video_source == u'youtube': if action == 'edit': return Markup('<iframe id="youtube_player" src="//www.youtube.com/embed/%s?wmode=transparent&showinfo=0&rel=0&autohide=0&autoplay=0&enablejsapi=1&version=3" frameborder="0" allowfullscreen></iframe>' % self.video_sourceid) elif action == 'view': return Markup('<iframe id="youtube_player" src="//www.youtube.com/embed/%s?wmode=transparent&showinfo=0&rel=0&autohide=0&autoplay=1&enablejsapi=1&version=3" frameborder="0" allowfullscreen></iframe>' % self.video_sourceid) elif self.video_source == u"vimeo": if action == 'edit': return Markup('<iframe id="vimeo_player" src="//player.vimeo.com/video/%s?api=1&player_id=vimeoplayer" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>' % self.video_sourceid) elif action == 'view': return Markup('<iframe id="vimeo_player" src="//player.vimeo.com/video/%s?api=1&autoplay=1" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>' % self.video_sourceid) elif self.video_source == u"ustream": if action == 'edit': return Markup('<iframe id="ustream_player" src="//www.ustream.tv/embed/%s?v=3&wmode=direct" scrolling="no" frameborder="0" style="border: 0px none transparent;"> </iframe>' % self.video_sourceid) elif action == 'view': return Markup('<iframe id="ustream_player" src="//www.ustream.tv/embed/%s?v=3&wmode=direct" scrolling="no" frameborder="0" style="border: 0px none transparent;"> </iframe>' % self.video_sourceid) return u'' def embed_slides_for(self, action=None): if self.slides_source == u'speakerdeck': html = '<iframe src="//www.speakerdeck.com/embed/%s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>' % urllib.quote(self.slides_sourceid) return Markup(html) elif self.slides_source == u'slideshare': html = '<iframe id="slideshare" src="//www.slideshare.net/slideshow/embed_code/%s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>' % urllib.quote(self.slides_sourceid) return Markup(html) return u'' @cached_property def speakers(self): return [plv.playlist.channel for plv in PlaylistVideo.query.filter_by(video=self) if plv.playlist.auto_type == PLAYLIST_AUTO_TYPE.SPEAKING_IN]
class Playlist(BaseScopedNameMixin, db.Model): __tablename__ = 'playlist' channel_id = db.Column(db.Integer, db.ForeignKey('channel.id'), nullable=False) description = db.Column(db.UnicodeText, default=u'', nullable=False) public = db.Column(db.Boolean, nullable=False, default=True) recorded_date = db.Column(db.Date, nullable=True) published_date = db.Column(db.Date, nullable=False, default=date.today) featured = db.Column(db.Boolean, default=False, nullable=False) type = db.Column(db.Integer, default=PLAYLIST_TYPE.REGULAR, nullable=False) auto_type = db.Column(db.Integer, nullable=True) banner_ad_filename = db.Column(db.Unicode(250), nullable=True, default=u'') banner_ad_url = db.Column(db.Unicode(250), nullable=False, default=u'') channel = db.relationship(Channel, primaryjoin=channel_id == Channel.id, backref=db.backref( 'playlists', order_by=(recorded_date.desc(), published_date.desc()), cascade='all, delete-orphan')) parent = db.synonym('channel') __table_args__ = (db.UniqueConstraint('channel_id', 'auto_type'), db.UniqueConstraint('channel_id', 'name')) _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)) def __repr__(self): if self.auto_type: return '<AutoPlaylist %s of %s>' % (self.type_label(), self.channel.title) else: return '<Playlist %s of %s>' % (self.title, self.channel.title) @classmethod def get_featured(cls, count): return cls.query.filter_by( public=True, auto_type=None, featured=True).order_by( 'featured').order_by('updated_at').limit(count).all() @classmethod def migrate_profile(cls, oldchannel, newchannel): """ Move all playlists from the old channel to the new channel. """ def move_playlist(playlist, channel): """ Move playlist to a new channel """ conflict = bool( playlist.query.filter_by(name=playlist.name, channel=channel).count()) playlist.channel = channel if conflict: playlist.make_name() for playlist in oldchannel.playlists: if playlist.auto_type: # Check for matching playlist in newchannel newplaylist = newchannel.get_auto_playlist( auto_type=playlist.auto_type, create=False) if not newplaylist: move_playlist(playlist, newchannel) else: while playlist._videos: plv = playlist._videos.pop(0) if plv.video not in newplaylist.videos: newplaylist._videos.append(plv) for video in playlist.primary_videos: video.playlist = newplaylist db.session.delete(playlist) else: move_playlist(playlist, newchannel) return [cls.__table__.name, PlaylistVideo.__table__.name] def type_label(self): if self.auto_type is not None: return PLAYLIST_AUTO_TYPE[self.auto_type].title else: return PLAYLIST_TYPE.get(self.type, PLAYLIST_TYPE[0]) def permissions(self, user, inherited=None): perms = super(Playlist, self).permissions(user, inherited) if self.public: perms.add('view') if user and self.channel.userid in user.user_organizations_owned_ids(): perms.add('view') # In case playlist is not public perms.add('edit') perms.add('delete') if not self.auto_type or self.auto_type == PLAYLIST_AUTO_TYPE.STREAM: perms.add('new-video') perms.add('extend') perms.add('add-video') perms.add('remove-video') return perms def url_for(self, action='view', _external=False): if action == 'view': return url_for('playlist_view', channel=self.channel.name, playlist=self.name, _external=_external) elif action == 'feed': return url_for('playlist_feed', channel=self.channel.name, playlist=self.name, _external=_external) elif action == 'edit': return url_for('playlist_edit', channel=self.channel.name, playlist=self.name, _external=_external) elif action == 'extend': return url_for('playlist_extend', channel=self.channel.name, playlist=self.name, _external=_external) elif action == 'delete': return url_for('playlist_delete', channel=self.channel.name, playlist=self.name, _external=_external) elif action == 'new-video': return url_for('video_new', channel=self.channel.name, playlist=self.name, _external=_external) def next(self, video): for index, _video in enumerate(self.videos): if video is _video: try: return self.videos[index + 1] except IndexError: return None else: return None def prev(self, video): for index, _video in enumerate(self.videos): if video is _video: if index == 0: return None try: return self.videos[index - 1] except IndexError: return None else: return None
return self.name @classmethod def get(cls, title): tag = cls.query.filter_by(title=title).first() if tag: return tag else: name = make_name(title) # Is this name already in use? If yes, return it tag = cls.query.filter_by(name=name).first() if tag: return tag else: tag = cls(name=name, title=title) db.session.add(tag) return tag def rename(self, title): name = make_name(title) if self.query.filter_by(name=name).first() is not None: raise ValueError(u"Name already in use") else: 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')))
class Video(BaseIdNameMixin, CommentingMixin, db.Model): __tablename__ = 'video' 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'') video_url = db.Column(db.Unicode(250), nullable=False) slides_url = db.Column(db.Unicode(250), nullable=False, default=u'') thumbnail_path = db.Column(db.Unicode(250), nullable=True, default=u'') video_source = db.Column(db.Unicode(80), nullable=False, default=u'') video_sourceid = db.Column(db.Unicode(80), nullable=False, default=u'') slides_source = db.Column(db.Unicode(80), nullable=False, default=u'') slides_sourceid = db.Column(db.Unicode(80), 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')) def __repr__(self): return u'<Video %s>' % self.url_name def permissions(self, user, inherited=None): perms = super(Video, self).permissions(user, inherited) perms.add('view') if user and self.channel.userid in user.user_organizations_owned_ids(): perms.add('edit') perms.add('delete') else: if 'edit' in perms: perms.remove('edit') if 'delete' in perms: perms.remove('delete') # Allow speakers to edit if user: pl = user.channel.playlist_for_speaking_in() if pl and self in pl.videos: perms.add('edit') return perms def url_for(self, action='view', channel=None, playlist=None, _external=False): channel = channel or self.channel playlist = playlist or self.playlist if playlist.channel != channel or playlist not in self.playlists: return if action == 'view': return url_for('video_view', channel=channel.name, playlist=playlist.name, video=self.url_name, _external=_external) elif action == 'remove-video': return url_for('video_remove', channel=channel.name, playlist=playlist.name, video=self.url_name, _external=_external) # Edit and Delete can only be from the source playlist elif action == 'edit': return url_for('video_edit', channel=self.channel.name, playlist=self.playlist.name, video=self.url_name, _external=_external) elif action == 'delete': return url_for('video_delete', channel=self.channel.name, playlist=self.playlist.name, video=self.url_name, _external=_external) elif action == 'add-speaker': return url_for('video_add_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, video=self.url_name, _external=_external) elif action == 'action': return url_for('video_action', channel=channel.name, playlist=playlist.name, video=self.url_name, _external=_external) def embed_video_for(self, action='view'): if self.video_source == u'youtube': if action == 'edit': return Markup( '<iframe src="http://www.youtube.com/embed/%s?wmode=transparent&showinfo=0&rel=0&autohide=1&autoplay=0" frameborder="0" allowfullscreen></iframe>' % self.video_sourceid) elif action == 'view': return Markup( '<iframe src="http://www.youtube.com/embed/%s?wmode=transparent&showinfo=0&rel=0&autohide=1&autoplay=1" frameborder="0" allowfullscreen></iframe>' % self.video_sourceid) return u'' def embed_slides_for(self, action=None): if self.slides_source == u'speakerdeck': html = '<iframe src="http://www.speakerdeck.com/embed/%s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>' % self.slides_sourceid return Markup(html) elif self.slides_source == u'slideshare': html = '<iframe src="http://www.slideshare.net/slideshow/embed_code/%s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>' % self.slides_sourceid return Markup(html) return u''