view browser_side/radiocol.py @ 189:67365f17069e

browser side: removed "\n" -> <br> conversion in html_sanitize, and use "white-space: pre" CSS property in chat messages instead (thx Link Mauve !).
author Goffi <goffi@goffi.org>
date Sun, 03 Mar 2013 20:44:17 +0100
parents 9763dec220ed
children 63e9b680d3e7
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Libervia: a Salut à Toi frontend
Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import pyjd # this is dummy in pyjs
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui.FlexTable import FlexTable
from pyjamas.ui.FormPanel import FormPanel
from pyjamas.ui.NamedFrame import NamedFrame
from pyjamas.ui.FileUpload import FileUpload
from pyjamas.ui.Label import Label
from pyjamas.ui.Button import Button
from pyjamas.ui.ClickListener import ClickHandler
from pyjamas.ui.MouseListener import MouseHandler
from pyjamas.ui.Hidden import Hidden
from pyjamas.ui.HTML import HTML
from pyjamas import Window
from pyjamas.Timer import Timer
from __pyjamas__ import JS

from jid import JID
from tools import html_sanitize


class MetadataPanel(FlexTable):

    def __init__(self):
        FlexTable.__init__(self)
        title_lbl =  Label("title:")
        title_lbl.setStyleName('radiocol_metadata_lbl')
        artist_lbl = Label("artist:")
        artist_lbl.setStyleName('radiocol_metadata_lbl')
        album_lbl = Label("album:")
        album_lbl.setStyleName('radiocol_metadata_lbl')
        self.title =  Label("")
        self.title.setStyleName('radiocol_metadata')
        self.artist = Label("")
        self.artist.setStyleName('radiocol_metadata')
        self.album = Label("")
        self.album.setStyleName('radiocol_metadata')
        self.setWidget(0,0,title_lbl)
        self.setWidget(1,0,artist_lbl)
        self.setWidget(2,0,album_lbl)
        self.setWidget(0,1,self.title)
        self.setWidget(1,1,self.artist)
        self.setWidget(2,1,self.album)
        self.setStyleName("radiocol_metadata_pnl")

    def setTitle(self, title):
        self.title.setText(title)

    def setArtist(self, artist):
        self.artist.setText(artist)

    def setAlbum(self, album):
        self.album.setText(album)

class ControlPanel(FormPanel):
    """Panel used to show controls to add a song, or vote for the current one"""

    def __init__(self, referee):
        FormPanel.__init__(self)
        self._timer = Timer(notify=self._timeCb)
        self.setEncoding(FormPanel.ENCODING_MULTIPART)
        self.setMethod(FormPanel.METHOD_POST)
        self.setAction("upload_radiocol")
        vPanel = VerticalPanel()

        hPanel = HorizontalPanel()
        hPanel.setSpacing(5)
        self.file_upload = FileUpload()
        self.file_upload.setName("song")
        hPanel.add(self.file_upload)

        self.upload_btn = Button("Upload song", getattr(self, "onBtnClick"))
        hPanel.add(self.upload_btn)

        self.status = Label()
        hPanel.add(self.status)

        vPanel.add(hPanel)
        
        #We need to know the referee
        referee_field = Hidden('referee', referee)
        vPanel.add(referee_field)

        self.add(vPanel)
        self.addFormHandler(self)
    
    def _timeCb(self, timer):
        self.status.setText('')

    def onBtnClick(self):
        self.submit()
    
    def onSubmit(self, event):
        pass

    def blockUpload(self):
        self.file_upload.setVisible(False)
        self.upload_btn.setEnabled(False)

    def unblockUpload(self):
        self.file_upload.setVisible(True)
        self.upload_btn.setEnabled(True)


    def onSubmitComplete(self, event):
        result = event.getResults()
        if result == "OK":
            self.status.setText('[Your song has been added to queue]')
            self.status.setStyleName('radiocol_upload_status_ok')
            self._timer.schedule(5000)
        elif result == "KO":
            self.status.setText('[Something went wrong during your song upload]')
            self.status.setStyleName('radiocol_upload_status_ko')
        else:
            Window.alert('Submit error: %s' % result)

class Player(HTML):

    def __init__(self, player_id, metadata_panel):
        HTML.__init__(self)
        self._id = player_id
        self.metadata = metadata_panel
        self.title=""
        self.artist=""
        self.album=""
        self.filename = None
        self.played = False #True when song is playing/played, become False on preload

    def preload(self, filename, title, artist, album):
        """preload the song but doesn't play it"""
        self.filename = filename
        self.title = title
        self.artist = artist
        self.album = album
        self.played = False
        self.setHTML('<audio id="%s" style="display: none" preload="auto" src="radiocol/%s" />' % (self._id, html_sanitize(filename)))
        print "preloading %s in %s" % (title, self._id)
       
    def play(self):
        """actually play the song"""
        self.played = True
        self.metadata.setTitle(self.title)
        self.metadata.setArtist(self.artist)
        self.metadata.setAlbum(self.album)

        JS("""
        var player = top.document.getElementById(this._id);
        player.play();
        """)
        


class RadioColPanel(HorizontalPanel, ClickHandler):

    def __init__(self, parent, referee, player_nick):
        HorizontalPanel.__init__(self)
        ClickHandler.__init__(self)
        self._parent = parent
        self.referee = referee
        self.setStyleName("radiocolPanel")
        self.setHeight('30%')
        
        # Now we set up the layout
        self.left_panel = VerticalPanel()
        self.left_panel.setStyleName("radiocol_left_panel")
        self.left_panel.setHeight('100%')
        self.add(self.left_panel)
        self.right_panel = VerticalPanel()
        self.metadata_panel = MetadataPanel()
        self.right_panel.add(self.metadata_panel)
        self.control_panel = ControlPanel(self.referee)
        self.right_panel.add(self.control_panel)
        self.add(self.right_panel)
        #self.right_panel.setBorderWidth(1)
        self.next_songs = []
        self.players = [Player("player_%d" % i, self.metadata_panel) for i in range(4)]
        self.current_player = None
        for player in self.players:
            self.right_panel.add(player)
        self.addClickListener(self)

    def pushNextSong(self, title):
        """Add a song to the left panel's next songs queue"""
        next_song = Label(title)
        next_song.setStyleName("radiocol_next_song")
        self.next_songs.append(next_song)
        self.left_panel.append(next_song)

    def popNextSong(self):
        """Remove the first song of next songs list
        should be called when the song is played"""
        #FIXME: should check that the song we remove is the one we play
        next_song = self.next_songs.pop(0)
        self.left_panel.remove(next_song)
    
    def radiocolPreload(self, filename, title, artist, album):
        preloaded = False
        for player in self.players:
            if not player.filename or \
               (player.played and player != self.current_player):
                #if player has no file loaded, or it has already played its song
                #we use it to preload the next one
                player.preload(filename, title, artist, album)
                preloaded = True
                break
        if not preloaded:
            print("WARNING: Can't preload song, we are getting too many songs to preload, we shouldn't have more than 2 at once")
        else:
            self.pushNextSong(title)
    
    def radiocolPlay(self, filename):
        for player in self.players:
            if player.filename == filename:
                player.play()
                self.popNextSong()
                self.current_player = player
                return
        print("WARNING: Song not found in queue, can't play it. This should not happen")

    def radiocolNoUpload(self):
        self.control_panel.blockUpload()

    def radiocolUploadOk(self):
        self.control_panel.unblockUpload()

    def radiocolSongRejected(self, reason):
        Window.alert("Song rejected: %s" % reason)