def handle(self, packet_msg): if not self.is_token_auth(): return # Fetch all players. Use this to init client state if self.query == 'status_change': status = packet_msg.get('status') # Get previous player state from database s = db_session() try: player = s.query(Player).filter_by(id=self.sock.uid).one() except NoResultFound: log.warn(u"Requested player %s was not found!", self.sock.uid) return finally: s.close() # Remote status == STOPPED => attempt to come up with a new source if status == 0: self.send_source(player) else: s = db_session() player.status = status s.add(player) s.commit() self.broadcast('player', {'state': player.serialize()}, query="change", client_type='user', avoid_self=False) s.close()
def handle(self, packet_msg): if not self.is_user_auth(): return # Fetch all events. Use this to init client state if self.query == 'fetchall': s = db_session() if self.is_admin(): events = [event.serialize() for event in s.query(Event).all()] else: events = [event.serialize() for event in s.query(Event).filter_by(visible=True).all()] s.close() self.send_message(events) log.info(u"[%s] Events fetched", self.sock.sid[0:6]) return if self.query == 'add' and self.is_admin(): s = db_session() event = Event(name=u'Event {}'.format(generate_session()[:4]), visible=False) s.add(event) s.commit() self.send_message(event.serialize()) s.close() log.info(u"[%s] New event added", self.sock.sid[0:6]) return if self.query == 'edit' and self.is_admin(): event_id = packet_msg.get('id') name = packet_msg.get('name') visible = packet_msg.get('visible') # Get user s = db_session() try: event = s.query(Event).filter_by(id=event_id).one() event.name = name event.visible = visible s.add(event) s.commit() self.send_message(event.serialize()) log.info(u"[%s] Event %d edited", event_id, self.sock.sid[0:6]) except NoResultFound: log.info(u"[%s] error while editing event %d: no event found", event_id, self.sock.sid[0:6]) return finally: s.close()
def handle(self, packet_msg): username = packet_msg.get('username') password = packet_msg.get('password') password2 = packet_msg.get('password2') email = packet_msg.get('email', '') nickname = packet_msg.get('nickname') if not username or not password or not nickname or not password2: self.send_error('Fill all fields', 400) return if len(username) < 5 or len(username) > 32: self.send_error('Username should be between 5 and 32 characters long', 400) return if len(nickname) < 5 or len(nickname) > 32: self.send_error('Nickname should be between 5 and 32 characters long', 400) return if email and (len(email) < 3 or len(email) > 128): self.send_error('Email should be between 3 and 128 characters long', 400) return if len(password) < 8: self.send_error('Password should be at least 8 characters long', 400) return if password != password2: self.send_error('Passwords don\'t match!', 400) return # Make sure the nickname is not yet reserved s = db_session() try: s.query(User).filter_by(nickname=nickname).one() self.send_error('Nickname is already reserved', 405) s.close() return except NoResultFound: pass # Make sure the username is not yet reserved try: s.query(User).filter_by(username=username).one() self.send_error('Username is already reserved', 405) s.close() return except NoResultFound: pass pw_hash = pbkdf2_sha256.encrypt(password) user = User(username=username, password=pw_hash, email=email, nickname=nickname, level=USERLEVELS['user']) s.add(user) s.commit() s.close() # Send simple notification log.info(u"Registered new user %s.", username) self.send_message({})
def handle_fetchall_sig(self): s = db_session() queues = [ queue.serialize() for queue in s.query(SourceQueue).filter_by( user=self.sock.uid).all() ] s.close() self.send_message(queues, query="fetchall")
def handle(self, packet_msg): if not self.is_user_auth(): return if self.query == 'fetch_most_received': # TODO: THIS IS BRUTEFORCE, MAKE A BETTER QUERY s = db_session() skips = s.query(Skip, Media, User).filter(Media.id == Skip.media, User.id == Media.user).all() s.close() counts = {} names = {} for m in skips: if m[2].id not in counts: counts[m[2].id] = 0 names[m[2].id] = m[2].nickname counts[m[2].id] += 1 out = [] k = 1 for m in sorted(counts.iteritems(), key=lambda x: x[1], reverse=True): out.append({'number': k, 'amount': m[1], 'name': names[m[0]]}) k += 1 self.send_message(out) if self.query == 'fetch_most_given': s = db_session() skips = s.query(Skip.user, func.count(Skip.id).label('skips'), User)\ .filter(User.id == Skip.user)\ .group_by(Skip.user)\ .order_by('skips desc').all() s.close() out = [] k = 1 for skip in skips: out.append({ 'number': k, 'amount': skip[1], 'name': skip[2].nickname }) k += 1 self.send_message(out)
def ensure_sourcequeue(self, player_id): s = db_session() try: r = s.query(SourceQueue).filter_by(user=self.sock.uid, target=player_id).one() return r.id except NoResultFound: sq = SourceQueue(user=self.sock.uid, target=player_id) s.add(sq) s.commit() return sq.id finally: s.close()
def handle(self, packet_msg): if not self.is_user_auth(): return if not self.is_admin(): return # Fetch all users if self.query == 'fetchall': s = db_session() users = [user.serialize() for user in s.query(User).all()] s.close() self.send_message(users) log.info(u"[%s] Users fetched", self.sock.sid[0:6]) return # Edit a single user if self.query == 'edit': user_id = packet_msg.get('id') nickname = packet_msg.get('nickname') email = packet_msg.get('email') # Get user s = db_session() try: user = s.query(User).filter_by(id=user_id).one() user.nickname = nickname user.email = email s.add(user) s.commit() self.send_message(user.serialize()) log.info(u"[%s] Player %d edited", user_id, self.sock.sid[0:6]) except NoResultFound: log.info( u"[%s] error while editing player %d: no player found", user_id, self.sock.sid[0:6]) return finally: s.close()
def handle(self, packet_msg): if not self.is_user_auth(): return # Fetch all media entries if self.query == 'fetchall': player_id = packet_msg.get('player_id') if player_id: s = db_session() medias = [media.serialize() for media in s.query(Media).filter(Media.queue == SourceQueue.id, SourceQueue.target == player_id).order_by(Media.id.asc()).all()] s.close() self.send_message(medias)
def handle(self, packet_msg): if not self.is_user_auth(): return if not self.is_admin(): return # Fetch all users if self.query == 'fetchall': s = db_session() users = [user.serialize() for user in s.query(User).all()] s.close() self.send_message(users) log.info(u"[%s] Users fetched", self.sock.sid[0:6]) return # Edit a single user if self.query == 'edit': user_id = packet_msg.get('id') nickname = packet_msg.get('nickname') email = packet_msg.get('email') # Get user s = db_session() try: user = s.query(User).filter_by(id=user_id).one() user.nickname = nickname user.email = email s.add(user) s.commit() self.send_message(user.serialize()) log.info(u"[%s] Player %d edited", user_id, self.sock.sid[0:6]) except NoResultFound: log.info(u"[%s] error while editing player %d: no player found", user_id, self.sock.sid[0:6]) return finally: s.close()
def create_admin(_username, _password): settings.config_init() db_init(settings.DATABASE_CONFIG) pw_hash = pbkdf2_sha256.encrypt(_password) s = db_session() user = User(username=_username, nickname=_username, email="*****@*****.**", password=pw_hash, level=USERLEVELS['admin']) s.add(user) try: s.commit() print("User '{}' created.".format(_username)) except IntegrityError: print("User {} already exists.".format(_username)) finally: s.close()
def handle(self, packet_msg): sid = packet_msg.get('sid') # Make sure we at least do get a sid (not empty string or stuff) if not sid: self.send_error("Invalid session", 403) log.info(u"Authentication failed") return # Attempt to find an active session and the attached user account user = None ses = None s = db_session() try: ses = s.query(Session).filter_by(key=sid).one() user = s.query(User).filter_by(id=ses.user).one() except NoResultFound: pass finally: s.close() # Session found with token. if ses and user: self.sock.sid = sid self.sock.uid = user.id self.sock.authenticated = True self.sock.level = user.level self.sock.client_type = 'user' # Send login success message self.send_message({ 'uid': self.sock.uid, 'sid': self.sock.sid, 'user': user.serialize() }) log.info(u"[%s] Authenticated", sid[0:6]) return # Error out self.send_error("Invalid session", 403) log.info(u"[%s] Authentication failed", sid[0:6])
def handle(self, packet_msg): if not self.is_user_auth(): return # Remove session s = db_session() s.query(Session).filter_by(key=self.sock.sid).delete() s.commit() s.close() # Dump out log log.info(u"[%s] Logged out", self.sock.sid[0:6]) # De-auth & clear session information self.sock.authenticated = False self.sock.sid = None self.sock.uid = None self.sock.level = 0 # Send required skip count self.send_req_skip_count()
exit(0) if 'create_test_admin' in sys.argv: create_admin('admin', 'admin') exit(0) if 'create_event' in sys.argv: settings.config_init() db_init(settings.DATABASE_CONFIG) if len(sys.argv) == 3: name = sys.argv[2] else: name = raw_input("Event name: ") s = db_session() event = Event(name=name, visible=True) s.add(event) s.commit() s.close() exit(0) if 'list_events' in sys.argv: settings.config_init() db_init(settings.DATABASE_CONFIG) s = db_session() print("ID Name") for event in s.query(Event).all(): print("{:3} {}".format(event.id, event.name)) s.close() exit(0)
def handle(self, packet_msg): if not self.is_user_auth(): return # Fetch all queues. Use this to init client state if self.query == 'fetchall': self.handle_fetchall_sig() # Fetch a single queue (for refreshing) if self.query == 'fetchone': s = db_session() try: queue = s.query(SourceQueue).filter_by( user=self.sock.uid).one().serialize() self.send_message(queue) except NoResultFound: self.send_error('No queue found', 404) finally: s.close() # Add new item to the queue. Log to DB, send signal if self.query == 'add': url = packet_msg.get('url') player_id = packet_msg.get('player_id') if not url or not player_id: self.send_error('Invalid input data', 500) return queue_id = self.ensure_sourcequeue(player_id) # Attempt to parse the url youtube_hash = self.get_youtube_code(url) other_url = None # if not youtube_hash: # other_url = self.validate_other_url(url) # Error out if necessary if not youtube_hash and not other_url: self.send_error('Invalid URL', 500) return # First, attempt to find the source from database. If it exists, simply use that. s = db_session() found_src = None try: if youtube_hash: found_src = s.query(Source).filter_by( youtube_hash=youtube_hash).one() elif other_url: found_src = s.query(Source).filter_by( other_url=other_url).one() except NoResultFound: pass finally: s.close() # If the existing source entry belongs to current user, show error. # Don't let user post the same video again and again (rudimentary protection) if found_src: s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: self.send_error('Invalid input data', 500) return finally: s.close() s = db_session() try: res = s.query(Media)\ .filter(Media.user == self.sock.uid, Media.source == found_src.id, Media.queue == queue_id)\ .all() for r in res: if r.id > player.last: self.send_error('Url is already in the queue', 500) return except NoResultFound: pass finally: s.close() # First title and description for new video first_title = "Unknown" first_desc = "" first_duration = 0 # If this is a youtube url, attempt to fetch information for it if youtube_hash: with youtube_dl.YoutubeDL({'logger': log}) as ydl: try: info = ydl.extract_info( 'http://www.youtube.com/watch?v=' + youtube_hash, download=False) except youtube_dl.DownloadError, e: self.send_error(str(e), 500) return # Check video duration if info['duration'] > settings.LIMIT_DURATION and not self.is_admin( ): current_str = format_time_delta(info['duration']) limit_str = format_time_delta(settings.LIMIT_DURATION) self.send_error( 'Video is too long ({}). Current limit is {}.'.format( current_str, limit_str), 500) return # Use video desc and title first_duration = info['duration'] first_title = info['title'] first_desc = info['description'] if found_src: # Add a new media entry s = db_session() media = Media(source=found_src.id, user=self.sock.uid, queue=queue_id) s.add(media) s.commit() s.close() # Inform playerdevices about this (in case they are waiting for news about new media) self.broadcast('playerdev', {}, query='poke', client_type="token") else: # Okay, Let's save the first draft and then poke at the downloader with MQ message s = db_session() source = Source( youtube_hash=youtube_hash if youtube_hash else '', other_url=other_url if other_url else '', title=first_title, description=first_desc, length_seconds=first_duration, ) s.add(source) s.commit() media = Media(source=source.id, user=self.sock.uid, queue=queue_id) s.add(media) s.commit() # Send message to kick the downloader self.sock.mq.send_msg(self.sock.mq.KEY_DOWNLOAD, { 'source_id': source.id, 'no_limit': self.is_admin() }) s.close() # Resend all queue data (for now) self.handle_fetchall_sig() self.send_message({}) log.info("[%s] New media added to queue", self.sock.sid[0:6])
def handle(self, packet_msg): if not self.is_user_auth(): return password = packet_msg.get('password') password2 = packet_msg.get('password2') email = packet_msg.get('email', '') nickname = packet_msg.get('nickname') if len(nickname) < 5 or len(nickname) > 32: self.send_error( 'Nickname should be between 5 and 32 characters long', 400) return if email and len(email) > 0: if len(email) < 3 or len(email) > 128: self.send_error( 'Email should be between 3 and 128 characters long', 400) return if (password and len(password) > 0) or (password2 and len(password2) > 0): if (password and len(password) < 8) or (password2 and len(password2) < 8): self.send_error( 'Password should be at least 8 characters long', 400) return if password != password2: self.send_error('Passwords don\'t match!', 400) return # Make sure the nickname is not yet reserved s = db_session() try: s.query(User).filter(User.nickname == nickname).filter( User.id != self.sock.uid).one() self.send_error('Nickname is already reserved', 405) return except NoResultFound: pass finally: s.close() # Get user try: user = s.query(User).filter_by(id=self.sock.uid).one() username = user.username user.nickname = nickname user.email = email if password: user.password = pbkdf2_sha256.encrypt(password) s.add(user) s.commit() self.send_message(message={'user': user.serialize()}) except NoResultFound: log.warn(u"User %d not found.", self.sock.uid) return finally: s.close() # Send simple notification log.info(u"Updated profile for user %s.", username)
def send_source(self, player): last_id = 0 if player.last: last_id = player.last # Attempt to fetch the next available media for this player s = db_session() media = s.query(Media)\ .filter( SourceQueue.target == self.sock.uid, Media.queue == SourceQueue.id, Media.id > last_id)\ .filter( Source.id == Media.source, Source.status == MEDIASTATUS['finished']) \ .order_by(Media.id.asc())\ .first() # No result => bail. # Result => get source ref if not media: # No result, so leave remote status as-is (stopped) and set that to DB too. player.status = 0 s.add(player) s.commit() # Announce playback status change for all users self.broadcast('player', {'state': player.serialize()}, query="change", client_type='user', avoid_self=False) # Send playback request. We will get status back later. self.send_message({'status': 0}, query='set_status', target_uid=player.id) s.close() log.info(u"No media for player, staying as STOPPED.") return else: source = s.query(Source).filter_by(id=media.source).one() s.close() real_file = source.file_name # Send playback request. We will get status back later. self.send_message({'url': settings.SOURCE_URL + real_file}, query='source', target_uid=player.id) # Some logging log.info(u"Player %s is playing %s", self.sock.uid, source.id) # Update last played media track to db s = db_session() player.last = media.id player.status = 1 s.add(player) s.commit() # Announce playback status change for all users self.broadcast('player', {'state': player.serialize()}, query="change", client_type='user', avoid_self=False) s.close()
def handle(self, packet_msg): if not self.is_user_auth(): return # Add new empty player if self.query == "add" and self.is_admin(): event_id = packet_msg.get("event_id") s = db_session() player = Player( event=event_id, name=u"Player {}".format(generate_session()[:4]), token=generate_session()[:16] ) s.add(player) s.commit() self.send_message(player.serialize(show_token=True)) s.close() log.info(u"[%s] New player added", self.sock.sid[0:6]) return if self.query == "edit" and self.is_admin(): player_id = packet_msg.get("id") name = packet_msg.get("name") # Get user s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() player.name = name s.add(player) s.commit() self.send_message(player.serialize(show_token=True)) log.info(u"[%s] Player %d edited", player_id, self.sock.sid[0:6]) except NoResultFound: log.info(u"[%s] error while editing player %d: no player found", player_id, self.sock.sid[0:6]) return finally: s.close() # Fetch all players. Use this to init client state if self.query == "fetchall": s = db_session() players = [p.serialize(show_token=self.is_admin()) for p in s.query(Player).all()] s.close() self.send_message(players) return if self.query == "now_playing": player_id = packet_msg.get("player_id") # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() self.send_message({"state": player.serialize()}, query="change") return # Request skip if self.query == "skip": player_id = packet_msg.get("player_id") # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() # Add our own skip s = db_session() try: skip = Skip(user=self.sock.uid, player=player.id, media=player.last) s.add(skip) s.commit() except IntegrityError: log.debug(u"Hissatsu! Double skip!") return finally: s.close() log.info(u"User %s requested skipping media %s on player %s", self.sock.uid, player.last, player.id) # Broadcast skip count skips = s.query(Skip).filter_by(player=player.id, media=player.last).count() if skips >= self.get_req_skip_count(): log.info(u"All skip votes gathered; skipping %d", player.last) PlayerDeviceHandler(self.sock, "playerdev", "status_change").send_source(player) else: self.broadcast( "player", {"count": skips}, query="current_skip_count", req_auth=True, avoid_self=False, client_type="user", ) # That's that. return if self.query == "get_media_skip_count": player_id = packet_msg.get("player_id") # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() # Get current skip count for requested player skips = s.query(Skip).filter_by(player=player.id, media=player.last).count() self.send_message({"count": skips}, query="current_skip_count") return if self.query == "req_skip_count": self.send_req_skip_count() return if self.query == "pause" and self.is_admin(): player_id = packet_msg.get("player_id") if player_id: log.info(u"ADMIN: Force status = 2 on player %d", player_id) self._send_message("playerdev", {"status": 2}, query="set_status", target_uid=player_id) return if self.query == "play" and self.is_admin(): player_id = packet_msg.get("player_id") s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() if player.status > 0: log.info(u"ADMIN: Force status = 1 on player %d", player_id) self._send_message("playerdev", {"status": 1}, query="set_status", target_uid=player_id) else: PlayerDeviceHandler(self.sock, "playerdev", "status_change").send_source(player) return if self.query == "force_skip" and self.is_admin(): player_id = packet_msg.get("player_id") s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() log.info(u"ADMIN: Force skipping on player %d", player_id) PlayerDeviceHandler(self.sock, "playerdev", "status_change").send_source(player) return # Maintenance mode, not stop (even if it is named so) if self.query == "stop" and self.is_admin(): player_id = packet_msg.get("player_id") if player_id: log.info(u"ADMIN: Force status = 4 on player %d", player_id) self._send_message("playerdev", {"status": 4}, query="set_status", target_uid=player_id) return
def handle(self, packet_msg): # If we are already authenticated, don't bother with this if self.sock.authenticated: return username = packet_msg.get('username', '') password = packet_msg.get('password', '') token = packet_msg.get('token') if token: s = db_session() try: player = s.query(Player).filter_by(token=token).one() except NoResultFound: self.send_error('Incorrect token', 401) log.info(u"Invalid token.") return finally: s.close() self.sock.authenticated = True self.sock.client_type = 'token' self.sock.uid = player.id self.sock.level = 0 # Send login success message self.send_message({ 'name': player.name }) log.info(u"[%s] Token logged in", token[0:6]) else: s = db_session() try: user = s.query(User).filter_by(username=username).one() except NoResultFound: self.send_error('Incorrect username or password', 401) log.info(u"Invalid username or password.") return finally: s.close() # If user exists and password matches, pass onwards! if user and pbkdf2_sha256.verify(password, user.password): session_id = generate_session() # Add new session s = db_session() ses = Session(key=session_id, user=user.id) s.add(ses) s.commit() s.close() # Mark connection as authenticated, and save session id self.sock.sid = session_id self.sock.uid = user.id self.sock.authenticated = True self.sock.level = user.level self.sock.client_type = 'user' # Send login success message self.send_message({ 'uid': self.sock.uid, 'sid': self.sock.sid, 'user': user.serialize() }) # Send required skip count self.send_req_skip_count() # Dump out log log.info(u"[%s] Logged in", self.sock.sid[0:6]) else: self.send_error('Incorrect username or password', 401) log.info(u"Invalid username or password.")
def handle_fetchall_sig(self): s = db_session() queues = [queue.serialize() for queue in s.query(SourceQueue).filter_by(user=self.sock.uid).all()] s.close() self.send_message(queues, query="fetchall")
def handle(self, packet_msg): if not self.is_user_auth(): return if self.query == 'fetch_ratings': s = db_session() # We probably _could_ combine these queries. # Count skips received by media posted by users skips_recv = s.query(User, Media, Skip, func.count()).\ filter(Media.user == User.id).\ filter(Media.id == Skip.media).\ group_by(User.id) # Count skip requests issued by users skips_sent = s.query(User, Skip, func.count()).\ filter(User.id == Skip.user).\ group_by(User.id) # Count total posts by each user posts = s.query(User, Media, func.count()).\ filter(Media.user == User.id).\ group_by(User.id) s.close() names_by_id = {} # relevant users skips_recv_by_id = {} # # of skips received by userid's media skips_sent_by_id = {} # skips sent per user id posts_by_id = {} # posts per user id collated = [] for s in skips_recv.all(): user = s[0] userid = s[0].id if userid not in names_by_id: names_by_id[userid] = user.username skips_recv_by_id[userid] = s[3] # get value of func.count() for s in skips_sent.all(): user = s[0] userid = user.id if userid not in names_by_id: names_by_id[userid] = user.username skips_sent_by_id[userid] = s[2] for p in posts.all(): user = p[0] userid = user.id if userid not in names_by_id: names_by_id[userid] = user.username posts_by_id[userid] = p[2] for userid in names_by_id: name = names_by_id[userid] skips_recv = skips_recv_by_id.get(userid, 0) skips_sent = skips_sent_by_id.get(userid, 0) posts = posts_by_id.get(userid, 0) collated.append(( name, skips_recv, skips_sent, posts, float(skips_recv) / posts if posts > 0 else 0)) out = [] k = 1 for c in collated: out.append({ 'number': k, 'name': c[0], 'skips_sent': c[1], 'skips_recv': c[2], 'posts': c[3], 'rating': c[4], }) k += 1 self.send_message(out)
def handle(self): """ Handle incoming messages from the websocket interface """ try: for method_frame, properties, body in self.channel.consume(self.QUEUE_DOWNLOAD): data = json.loads(body) # Get DB entry for this source s = db_session() try: source = s.query(Source).filter_by(id=data['source_id']).one() except NoResultFound: s.close() log.warn(u"Could not find source!") self.channel.basic_ack(method_frame.delivery_tag) continue # Update status s.add(source) source.status = MEDIASTATUS['fetching_metadata'] s.commit() self.send_msg('status', { 'status': MEDIASTATUS['fetching_metadata'], 'source_id': source.id }) # Check how we need to handle this if source.youtube_hash: if data['no_limit']: req_format = 'bestvideo[height <=? 720][fps <=? 30]+bestaudio[abr<=128]/best' else: req_format = 'bestvideo[height <=? 720][fps <=? 30][filesize<{}]+bestaudio[filesize<{}]/best'\ .format(settings.LIMIT_VIDEO_SIZE, settings.LIMIT_AUDIO_SIZE) ydl_opts = { 'logger': log, 'cachedir': settings.TMP_DIR, 'format': req_format, 'outtmpl': '%(id)s.%(ext)s' } # Download information with youtube_dl.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info('http://www.youtube.com/watch?v=' + source.youtube_hash, download=False) file_name = ydl.prepare_filename(info) # Form a correct path to the file file_path = os.path.join(settings.CACHE_DIR, file_name) ydl_opts['outtmpl'] = file_path # If we are in debug mode, also dump the file information as json # if settings.DEBUG: # with open(os.path.join(settings.TMP_DIR, file_name)+'.json', 'wb') as f: # f.write(json.dumps(info)) # Update status source.status = MEDIASTATUS['downloading'] source.length_seconds = info['duration'] source.file_ext = os.path.splitext(file_name)[1][1:] source.file_name = file_name source.mime_type = mimetypes.guess_type('file://'+file_path)[0] # Save video and audio information if 'requested_formats' in info: v, a = info['requested_formats'] source.video_bitrate = int(v['tbr']) source.video_codec = v['vcodec'] source.video_w = v['width'] source.video_h = v['height'] source.audio_bitrate = int(a.get('abr', 0)) source.audio_codec = a['acodec'] else: source.video_codec = info['vcodec'] source.video_w = info['width'] source.video_h = info['height'] source.audio_codec = info['acodec'] # Dump to DB s.add(source) s.commit() self.send_msg('single', source.serialize()) # Start downloading log.info(u"Downloading %s to %s", source.youtube_hash, file_path) try: with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(['http://www.youtube.com/watch?v=' + source.youtube_hash]) except Exception, e: source.status = MEDIASTATUS['error'] source.message = "DL Error" s.add(source) s.commit() self.channel.basic_ack(method_frame.delivery_tag) s.close() log.info(u"Error while attempting to download: %s", str(e)) continue else: log.warn(u"Cannot yet download other urls!") # Save everything and dequeue this entry log.info(u"Download finished.") source.status = MEDIASTATUS['finished'] source.message = 'Video downloaded successfully.' s.add(source) s.commit() self.send_msg('status', { 'status': MEDIASTATUS['finished'], 'source_id': source.id, 'message': source.message }) # Finish up s.close() self.channel.basic_ack(method_frame.delivery_tag) log.info(u"Tag %s marked done.", method_frame.delivery_tag) except KeyboardInterrupt: return
def handle(self, packet_msg): if not self.is_user_auth(): return # Fetch all queues. Use this to init client state if self.query == 'fetchall': self.handle_fetchall_sig() # Fetch a single queue (for refreshing) if self.query == 'fetchone': s = db_session() try: queue = s.query(SourceQueue).filter_by(user=self.sock.uid).one().serialize() self.send_message(queue) except NoResultFound: self.send_error('No queue found', 404) finally: s.close() # Add new item to the queue. Log to DB, send signal if self.query == 'add': url = packet_msg.get('url') player_id = packet_msg.get('player_id') if not url or not player_id: self.send_error('Invalid input data', 500) return queue_id = self.ensure_sourcequeue(player_id) # Attempt to parse the url youtube_hash = self.get_youtube_code(url) other_url = None # if not youtube_hash: # other_url = self.validate_other_url(url) # Error out if necessary if not youtube_hash and not other_url: self.send_error('Invalid URL', 500) return # First, attempt to find the source from database. If it exists, simply use that. s = db_session() found_src = None try: if youtube_hash: found_src = s.query(Source).filter_by(youtube_hash=youtube_hash).one() elif other_url: found_src = s.query(Source).filter_by(other_url=other_url).one() except NoResultFound: pass finally: s.close() # If the existing source entry belongs to current user, show error. # Don't let user post the same video again and again (rudimentary protection) if found_src: s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: self.send_error('Invalid input data', 500) return finally: s.close() s = db_session() try: res = s.query(Media)\ .filter(Media.user == self.sock.uid, Media.source == found_src.id, Media.queue == queue_id)\ .all() for r in res: if r.id > player.last: self.send_error('Url is already in the queue', 500) return except NoResultFound: pass finally: s.close() # First title and description for new video first_title = "Unknown" first_desc = "" first_duration = 0 # If this is a youtube url, attempt to fetch information for it if youtube_hash: with youtube_dl.YoutubeDL({'logger': log}) as ydl: try: info = ydl.extract_info('http://www.youtube.com/watch?v=' + youtube_hash, download=False) except youtube_dl.DownloadError, e: self.send_error(str(e), 500) return # Check video duration if info['duration'] > settings.LIMIT_DURATION and not self.is_admin(): current_str = format_time_delta(info['duration']) limit_str = format_time_delta(settings.LIMIT_DURATION) self.send_error('Video is too long ({}). Current limit is {}.' .format(current_str, limit_str), 500) return # Use video desc and title first_duration = info['duration'] first_title = info['title'] first_desc = info['description'] if found_src: # Add a new media entry s = db_session() media = Media( source=found_src.id, user=self.sock.uid, queue=queue_id ) s.add(media) s.commit() s.close() # Inform playerdevices about this (in case they are waiting for news about new media) self.broadcast('playerdev', {}, query='poke', client_type="token") else: # Okay, Let's save the first draft and then poke at the downloader with MQ message s = db_session() source = Source( youtube_hash=youtube_hash if youtube_hash else '', other_url=other_url if other_url else '', title=first_title, description=first_desc, length_seconds=first_duration, ) s.add(source) s.commit() media = Media( source=source.id, user=self.sock.uid, queue=queue_id ) s.add(media) s.commit() # Send message to kick the downloader self.sock.mq.send_msg(self.sock.mq.KEY_DOWNLOAD, { 'source_id': source.id, 'no_limit': self.is_admin() }) s.close() # Resend all queue data (for now) self.handle_fetchall_sig() self.send_message({}) log.info("[%s] New media added to queue", self.sock.sid[0:6])
def handle(self, packet_msg): if not self.is_user_auth(): return # Add new empty player if self.query == 'add' and self.is_admin(): event_id = packet_msg.get('event_id') s = db_session() player = Player( event=event_id, name=u'Player {}'.format(generate_session()[:4]), token=generate_session()[:16]) s.add(player) s.commit() self.send_message(player.serialize(show_token=True)) s.close() log.info(u"[%s] New player added", self.sock.sid[0:6]) return if self.query == 'edit' and self.is_admin(): player_id = packet_msg.get('id') name = packet_msg.get('name') # Get user s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() player.name = name s.add(player) s.commit() self.send_message(player.serialize(show_token=True)) log.info(u"[%s] Player %d edited", player_id, self.sock.sid[0:6]) except NoResultFound: log.info(u"[%s] error while editing player %d: no player found", player_id, self.sock.sid[0:6]) return finally: s.close() # Fetch all players. Use this to init client state if self.query == 'fetchall': s = db_session() players = [p.serialize(show_token=self.is_admin()) for p in s.query(Player).all()] s.close() self.send_message(players) return if self.query == 'now_playing': player_id = packet_msg.get('player_id') # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() self.send_message({'state': player.serialize()}, query='change') return # Request skip if self.query == 'skip': player_id = packet_msg.get('player_id') # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() # Add our own skip s = db_session() try: skip = Skip(user=self.sock.uid, player=player.id, media=player.last) s.add(skip) s.commit() except IntegrityError: log.debug(u"Hissatsu! Double skip!") return finally: s.close() log.info(u"User %s requested skipping media %s on player %s", self.sock.uid, player.last, player.id) # Broadcast skip count skips = s.query(Skip).filter_by(player=player.id, media=player.last).count() if skips >= self.get_req_skip_count(): log.info(u"All skip votes gathered; skipping %d", player.last) PlayerDeviceHandler(self.sock, 'playerdev', 'status_change').send_source(player) else: self.broadcast('player', { 'count': skips, }, query='current_skip_count', req_auth=True, avoid_self=False, client_type='user') # That's that. return if self.query == 'get_media_skip_count': player_id = packet_msg.get('player_id') # Make sure we have a valid player s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() # Get current skip count for requested player skips = s.query(Skip).filter_by(player=player.id, media=player.last).count() self.send_message({'count': skips}, query='current_skip_count') return if self.query == 'req_skip_count': self.send_req_skip_count() return if self.query == 'pause' and self.is_admin(): player_id = packet_msg.get('player_id') if player_id: log.info(u"ADMIN: Force status = 2 on player %d", player_id) self._send_message('playerdev', {'status': 2}, query='set_status', target_uid=player_id) return if self.query == 'play' and self.is_admin(): player_id = packet_msg.get('player_id') s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() if player.status > 0: log.info(u"ADMIN: Force status = 1 on player %d", player_id) self._send_message('playerdev', {'status': 1}, query='set_status', target_uid=player_id) else: PlayerDeviceHandler(self.sock, 'playerdev', 'status_change').send_source(player) return if self.query == 'force_skip' and self.is_admin(): player_id = packet_msg.get('player_id') s = db_session() try: player = s.query(Player).filter_by(id=player_id).one() except NoResultFound: return finally: s.close() log.info(u"ADMIN: Force skipping on player %d", player_id) PlayerDeviceHandler(self.sock, 'playerdev', 'status_change').send_source(player) return # Maintenance mode, not stop (even if it is named so) if self.query == 'stop' and self.is_admin(): player_id = packet_msg.get('player_id') if player_id: log.info(u"ADMIN: Force status = 4 on player %d", player_id) self._send_message('playerdev', {'status': 4}, query='set_status', target_uid=player_id) return
def handle(self, packet_msg): if not self.is_user_auth(): return password = packet_msg.get('password') password2 = packet_msg.get('password2') email = packet_msg.get('email', '') nickname = packet_msg.get('nickname') if len(nickname) < 5 or len(nickname) > 32: self.send_error('Nickname should be between 5 and 32 characters long', 400) return if email and len(email) > 0: if len(email) < 3 or len(email) > 128: self.send_error('Email should be between 3 and 128 characters long', 400) return if (password and len(password) > 0) or (password2 and len(password2) > 0): if (password and len(password) < 8) or (password2 and len(password2) < 8): self.send_error('Password should be at least 8 characters long', 400) return if password != password2: self.send_error('Passwords don\'t match!', 400) return # Make sure the nickname is not yet reserved s = db_session() try: s.query(User).filter(User.nickname == nickname).filter(User.id != self.sock.uid).one() self.send_error('Nickname is already reserved', 405) return except NoResultFound: pass finally: s.close() # Get user try: user = s.query(User).filter_by(id=self.sock.uid).one() username = user.username user.nickname = nickname user.email = email if password: user.password = pbkdf2_sha256.encrypt(password) s.add(user) s.commit() self.send_message(message={ 'user': user.serialize() }) except NoResultFound: log.warn(u"User %d not found.", self.sock.uid) return finally: s.close() # Send simple notification log.info(u"Updated profile for user %s.", username)
def handle(self): """ Handle incoming messages from the websocket interface """ try: for method_frame, properties, body in self.channel.consume( self.QUEUE_DOWNLOAD): data = json.loads(body) # Get DB entry for this source s = db_session() try: source = s.query(Source).filter_by( id=data['source_id']).one() except NoResultFound: s.close() log.warn(u"Could not find source!") self.channel.basic_ack(method_frame.delivery_tag) continue # Update status s.add(source) source.status = MEDIASTATUS['fetching_metadata'] s.commit() self.send_msg( 'status', { 'status': MEDIASTATUS['fetching_metadata'], 'source_id': source.id }) # Check how we need to handle this if source.youtube_hash: if data['no_limit']: req_format = 'bestvideo[height <=? 720][fps <=? 30]+bestaudio[abr<=128]/best' else: req_format = 'bestvideo[height <=? 720][fps <=? 30][filesize<{}]+bestaudio[filesize<{}]/best'\ .format(settings.LIMIT_VIDEO_SIZE, settings.LIMIT_AUDIO_SIZE) ydl_opts = { 'logger': log, 'cachedir': settings.TMP_DIR, 'format': req_format, 'outtmpl': '%(id)s.%(ext)s' } # Download information with youtube_dl.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info( 'http://www.youtube.com/watch?v=' + source.youtube_hash, download=False) file_name = ydl.prepare_filename(info) # Form a correct path to the file file_path = os.path.join(settings.CACHE_DIR, file_name) ydl_opts['outtmpl'] = file_path # If we are in debug mode, also dump the file information as json # if settings.DEBUG: # with open(os.path.join(settings.TMP_DIR, file_name)+'.json', 'wb') as f: # f.write(json.dumps(info)) # Update status source.status = MEDIASTATUS['downloading'] source.length_seconds = info['duration'] source.file_ext = os.path.splitext(file_name)[1][1:] source.file_name = file_name source.mime_type = mimetypes.guess_type('file://' + file_path)[0] # Save video and audio information if 'requested_formats' in info: v, a = info['requested_formats'] source.video_bitrate = int(v['tbr']) source.video_codec = v['vcodec'] source.video_w = v['width'] source.video_h = v['height'] source.audio_bitrate = int(a['abr']) source.audio_codec = a['acodec'] else: source.video_codec = info['vcodec'] source.video_w = info['width'] source.video_h = info['height'] source.audio_codec = info['acodec'] # Dump to DB s.add(source) s.commit() self.send_msg('single', source.serialize()) # Start downloading log.info(u"Downloading %s to %s", source.youtube_hash, file_path) try: with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([ 'http://www.youtube.com/watch?v=' + source.youtube_hash ]) except Exception, e: source.status = MEDIASTATUS['error'] source.message = "DL Error" s.add(source) s.commit() self.channel.basic_ack(method_frame.delivery_tag) s.close() log.info(u"Error while attempting to download: %s", str(e)) continue else: log.warn(u"Cannot yet download other urls!") # Save everything and dequeue this entry log.info(u"Download finished.") # Attempt to figure out youtube-dl mess m_test = os.path.splitext(file_path) new_file = u'{}{}'.format(m_test[0], '.mkv') if os.path.isfile(new_file): source.file_ext = 'mkv' source.file_name = os.path.basename(new_file) source.mime_type = mimetypes.guess_type('file://' + new_file)[0] source.status = MEDIASTATUS['finished'] source.message = 'Video downloaded successfully.' s.add(source) s.commit() self.send_msg( 'status', { 'status': MEDIASTATUS['finished'], 'source_id': source.id, 'message': source.message }) # Finish up s.close() self.channel.basic_ack(method_frame.delivery_tag) log.info(u"Tag %s marked done.", method_frame.delivery_tag) except KeyboardInterrupt: return