Example #1
0
class ChatMessage(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    sent_on = db.Column(db.DateTime, default=db.func.now())
    streamer = db.relationship('Streamer', backref=db.backref('chat_messages', lazy='dynamic'))
    streamer_id = db.Column('streamer_id', db.Integer(), db.ForeignKey('streamer.id'))
    sender = db.Column(db.String())
    text = db.Column(db.String())
Example #2
0
class YoutubeChannel(db.Model):
    channel_id = db.Column(db.String(24), primary_key=True)
    title = db.Column(db.String(30))
    streams = db.relationship("YoutubeStream", backref="youtube_channel")
    streamer_id = db.Column('streamer_id', db.Integer(), db.ForeignKey('streamer.id'))
    streamer = db.relationship('Streamer', uselist=False, backref=db.backref('youtube_channel_class', uselist=False))

    def __init__(self, channel_id, title=None):
        self.channel_id = channel_id
        if title:
            self.title = title
        else:
            r = requests_get_with_retries(
                "https://www.googleapis.com/youtube/v3/channels?id={}&part=snippet&key={}".format(
                    channel_id, app.config['YOUTUBE_KEY']), retries_num=15)

            r.raise_for_status()

            for item in r.json()['items']:
                self.title = item['snippet']['title']

    def __eq__(self, other):
        return type(self) == type(other) and self.channel_id == other.channel_id

    def __hash__(self):
        return hash(self.channel_id)

    def __repr__(self):
        return u'<YoutubeChannel {} with title {}>'.format(self.channel_id, self.title)
Example #3
0
class Stream(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String(50))
    scheduled_start_time = db.Column(db.DateTime())
    actual_start_time = db.Column(db.DateTime())
    status = db.Column(db.Enum('upcoming', 'live', 'completed', name='stream_status'))
    title = db.Column(db.String(200))
    submissions = db.relationship('Submission', secondary=stream_submission, backref=db.backref('streams', lazy='dynamic'))
    streamer_id = db.Column('streamer_id', db.Integer(), db.ForeignKey('streamer.id'))
    streamer = db.relationship('Streamer', backref=db.backref('streams', lazy='dynamic'))
    current_viewers = db.Column(db.Integer)
    confstream = db.Column(db.Boolean(), default=False)
    __mapper_args__ = {
        'polymorphic_on': type,
        'polymorphic_identity': 'stream'
    }

    def format_start_time(self, countdown=True, start_time=True):
        if not self.scheduled_start_time or (not countdown and not start_time):
            return None

        if countdown:
            return humanize.naturaltime(datetime.utcnow() - self.scheduled_start_time) +\
                ((", " + datetime.strftime(self.scheduled_start_time, "%Y-%m-%d %H:%M UTC")) if start_time else "")
        else:
            return datetime.strftime(self.scheduled_start_time, "%Y-%m-%d %H:%M UTC")

    def add_submission(self, submission):
        if submission not in self.submissions:
            self.submissions.append(submission)

    def _go_live(self):
        if self.status != 'live' and self.streamer and\
            self.streamer.checked and\
            (self.streamer.last_time_notified is None or
                (datetime.utcnow() - self.streamer.last_time_notified) > timedelta(hours=1)):
            self.streamer.need_to_notify_subscribers = True
        self.status = 'live'
Example #4
0
class Streamer(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    reddit_username = db.column_property(db.Column(db.String(20), unique=True), comparator_factory=CaseInsensitiveComparator)
    twitch_channel = db.column_property(db.Column(db.String(25), unique=True), comparator_factory=CaseInsensitiveComparator)
    info = db.Column(db.Text())
    checked = db.Column(db.Boolean(), default=False)
    rtmp_secret = db.Column(db.String(50))
    test = db.Column(db.Boolean(), default=False)
    as_subscriber_id = db.Column('as_subscriber_id', db.Integer(), db.ForeignKey('subscriber.id'))
    as_subscriber = db.relationship('Subscriber', backref=db.backref('as_streamer'))
    subscribers = db.relationship('Subscriber', secondary=streamer_subscriptions, lazy='dynamic', backref=db.backref('subscribed_to', lazy='dynamic'))
    need_to_notify_subscribers = db.Column(db.Boolean, default=False)
    last_time_notified = db.Column(db.DateTime())
    is_banned = db.Column(db.Boolean(), default=False)

    @property
    def youtube_channel(self):
        if self.youtube_channel_class is None:
            return None
        return self.youtube_channel_class.channel_id

    @youtube_channel.setter
    def youtube_channel(self, channel_id):
        self.youtube_channel_class = get_or_create(YoutubeChannel, channel_id=channel_id)

    @youtube_channel.deleter
    def youtube_channel(self):
        self.youtube_channel_class = None

    # XXX: this is kinda ugly, but simple
    # nginx-rtmp supports only fixed number of redirects
    # TODO: This should be fixed later
    rtmp_redirect_1 = db.Column(db.String())
    rtmp_redirect_2 = db.Column(db.String())
    rtmp_redirect_3 = db.Column(db.String())

    def __init__(self, reddit_username, checked=False):
        self.reddit_username = reddit_username
        self.checked = checked

    def __repr__(self):
        return '<Streamer {} {}>'.format(self.id, self.reddit_username)

    def get_id(self):
        return self.reddit_username

    def already_subscribed(self, another_streamer):
        return another_streamer and (another_streamer == self or (self.as_subscriber and self.as_subscriber.already_subscribed(another_streamer)))

    def streaming_key(self):
        return self.reddit_username + '?pass='******'completed' if not wpcs.status else wpcs.status
        db.session.commit()

    def populate_email(self, email):
        if not self.as_subscriber or len(self.as_subscriber.as_streamer) > 1:
            self.as_subscriber = get_or_create(Subscriber, email=email)
        else:
            alredy_existing_subscriber = Subscriber.query.filter_by(email=email).first()
            if alredy_existing_subscriber:
                db.session.delete(self.as_subscriber)
                self.as_subscriber = alredy_existing_subscriber
            else:
                self.as_subscriber.email = email

    def populate(self, form):
        self.info = form.info.data
        tc = form.twitch_channel_extract()

        # delete inapropriate tstream
        if tc != self.twitch_channel:
            ts = self.streams.filter_by(type='twitch_stream').first()
            if ts:
                ts.streamer = None

        # rebind tstream
        streamer = Streamer.query.filter_by(twitch_channel=tc).first()
        if streamer and streamer != current_user:
            streamer.twitch_channel = None
            for ts in streamer.streams.filter_by(type='twitch_stream'):
                ts.streamer = self

        self.twitch_channel = tc if tc else None

        yc = get_or_create(YoutubeChannel, channel_id=form.youtube_channel_extract())

        # delete inapropriate ystreams
        if yc != self.youtube_channel_class:
            for ys in self.streams.filter_by(type='youtube_stream'):
                ys.streamer = None

        # rebind ystreams
        streamer = yc.streamer
        if streamer and streamer != current_user:
            del streamer.youtube_channel
            for ys in yc.streams:
                ys.streamer = self

        self.youtube_channel_class = yc if yc else None
Example #5
0
class YoutubeStream(Stream):
    ytid = db.Column(db.String(11), unique=True)
    vod_views = db.Column(db.Integer)
    youtube_channel_id = db.Column(db.String(24), db.ForeignKey('youtube_channel.channel_id'))

    def __init__(self, ytid):
        self.ytid = ytid
        self.submissions = []

    def __eq__(self, other):
        return type(self) == type(other) and self.ytid == other.ytid

    def __hash__(self):
        return hash(self.ytid)

    def __repr__(self):
        return '<YoutubeStream {} {}>'.format(self.id, self.ytid)

    def _update_vod_views(self):
        app.logger.info("Updating view count for {}".format(self))
        r = requests_get_with_retries(
            "https://www.googleapis.com/youtube/v3/videos?id={}&part=statistics&key={}".format(
                self.ytid, app.config['YOUTUBE_KEY']), retries_num=15)

        r.raise_for_status()
        for item in r.json()['items']:
            self.vod_views = item['statistics']['viewCount']

    def _update_status(self):
        app.logger.info("Updating status for {}".format(self))

        r = requests_get_with_retries(
            "https://www.googleapis.com/youtube/v3/videos?id={}&part=snippet,liveStreamingDetails&key={}".format(
                self.ytid, app.config['YOUTUBE_KEY']), retries_num=15)

        r.raise_for_status()
        if not r.json()['items']:
            self.status = 'completed'
            self.current_viewers = None
            return

        for item in r.json()['items']:

            self.youtube_channel = get_or_create(YoutubeChannel,
                                                 channel_id=item['snippet']['channelId'])
            self.youtube_channel.title = item['snippet']['channelTitle']

            # if there is streamer with this channel
            if self.youtube_channel.streamer is not None:
                self.streamer = self.youtube_channel.streamer
            elif self.streamer is not None:
                # if streamer has no yc and didn't ever checked profile
                if not self.streamer.checked:
                    self.streamer.youtube_channel_class = self.youtube_channel
                # otherwise
                else:
                    self.streamer = None

            self.title = item['snippet']['title']
            if 'liveStreamingDetails' in item:
                self.scheduled_start_time = item['liveStreamingDetails'].get('scheduledStartTime', None)
                if 'concurrentViewers' in item['liveStreamingDetails']:
                    self.current_viewers = item['liveStreamingDetails']['concurrentViewers']

            if item['snippet']['liveBroadcastContent'] == "none" and not self.actual_start_time:
                # TODO: it is probably better to have a separate column for vids
                self.actual_start_time = item['snippet'].get('publishedAt')

            if item['snippet']['liveBroadcastContent'] == 'live':
                self._go_live()
                if 'actualStartTime' in item['liveStreamingDetails']:
                    self.actual_start_time = item['liveStreamingDetails'].get('actualStartTime', None)
                else:  # Youtube is weird, and sometimes this happens. If there is no actual start time, then we fall back to scheduledStartTime
                    self.actual_start_time = item['liveStreamingDetails'].get('scheduledStartTime', None)
            elif item['snippet']['liveBroadcastContent'] == 'upcoming':
                self.status = 'upcoming'
            else:
                self.status = 'completed'
                self.current_viewers = None

    def _get_flair(self):
        fst = self.format_start_time(start_time=False)
        status_to_flair = {"live": (u"Live", u"one"),
                           "completed": (u"Recording Available", u"four"),
                           "upcoming": (fst if fst else u"Upcoming", u"two"),
                           None: (None, None)}

        return status_to_flair[self.status]

    def normal_url(self):
        return "http://www.youtube.com/watch?v={}".format(self.ytid)

    def html_code(self, autoplay=False):
        return """
                <iframe width="640" height="390" frameborder="0" allowfullscreen
                src="http://www.youtube.com/embed/{}?rel=0&autoplay={}">
                </iframe>
              """.format(self.ytid, int(autoplay))

    __mapper_args__ = {
        'polymorphic_identity': 'youtube_stream'
    }
Example #6
0

class Anon(AnonymousUserMixin):
    def already_subscribed(self, streamer):
        email = request.cookies.get("email")
        return email and streamer and get_or_create(Subscriber, email=email) in streamer.subscribers


class CaseInsensitiveComparator(ColumnProperty.Comparator):
    def __eq__(self, other):
        return db.func.lower(self.__clause_element__()) == db.func.lower(other)

login_manager.anonymous_user = Anon

stream_submission = db.Table('stream_submission',
                             db.Column('stream_id', db.Integer(), db.ForeignKey('stream.id')),
                             db.Column('submission_id', db.String(6), db.ForeignKey('submission.submission_id')))

streamer_subscriptions = db.Table('streamer_subscriptions',
                                  db.Column('streamer_id', db.Integer(), db.ForeignKey('streamer.id')),
                                  db.Column('subscriber_id', db.Integer(), db.ForeignKey('subscriber.id')))


class Submission(db.Model):
    submission_id = db.Column(db.String(6), primary_key=True)
    recording_available = db.Column(db.Boolean())

    def __repr__(self):
        return '<Submission {}>'.format(self.submission_id)