#!/usr/bin/python2.5
# -*- coding: utf-8 -*-
# Zoutube - Youtube browser and player
# Copyright (C) 2009 Zaheer Abbas Merali <zaheerabbas at merali dot org>
# Borrowed code from: Canola2 Youtube Plugin (license of which shown below)
# Copyright (C) 2008 Instituto Nokia de Tecnologia
# Author: Adriano Rezende <adriano.rezende@openbossa.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Additional permission under GNU GPL version 3 section 7
#
# If you modify this Program, or any covered work, by linking or combining it
# with Canola2 and its core components (or a modified version of any of those),
# containing parts covered by the terms of Instituto Nokia de Tecnologia End
# User Software Agreement, the licensors of this Program grant you additional
# permission to convey the resulting work.

import re
import httplib
import urllib
import urllib2
import socket
import gobject
gobject.threads_init()
import gtk
gtk.gdk.threads_init()
import hildon
import gst
from threading import Thread
import dbus
import osso

from portrait import FremantleRotation

rotation = None
try:
    from xml.etree import cElementTree as ElementTree
except ImportError:
    try:
        import cElementTree as ElementTree
    except ImportError:
 	    from elementtree import ElementTree


# taken from play.py in gst-python examples
class VideoWidget(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.imagesink = None
        self.unset_flags(gtk.DOUBLE_BUFFERED)
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK)

    def do_expose_event(self, event):
        if self.imagesink:
            self.imagesink.expose()
            return False
        else:
            return True

    def set_sink(self, sink):
        assert self.window.xid
        self.imagesink = sink
        self.imagesink.set_xwindow_id(self.window.xid)

def to_utf8(str):
    return unicode(str).decode('utf8')

class YouTube(object):
    """YouTube Backend.

    This class provides an interface to search for videos on youtube server.

    @see YouTubeEntry
    """
    url_standardfeeds = "http://gdata.youtube.com/feeds/standardfeeds"
    url_video_search = "http://gdata.youtube.com/feeds/api/videos"
    url_video_request = "http://www.youtube.com/watch?v=%s"
    url_video_request_flv = "http://www.youtube.com/get_video?video_id=%s&t=%s"
    url_categories = "http://gdata.youtube.com/schemas/2007/categories.cat"
    url_video_by_category = "http://gdata.youtube.com/feeds/videos/-"
    url_channels = "http://gdata.youtube.com/feeds/api/channels"

    def __init__(self):
        self.last_summary = {}

    def _request(self, url, *params):
        """Return feed content of a specific url."""
        self.last_url = url % params
        xml = urllib2.urlopen(url % params).read()
        self.last_summary, entries = parse_youtube_xml(xml)
        return entries

    def search(self, query):
        """Search for video by keywords."""
        return self._request("%s?vq=%s",
                             self.url_video_search, urllib2.quote(query))

    def top_rated(self):
        """Return the top rated videos."""
        return self._request("%s/top_rated", self.url_standardfeeds)

    def most_viewed(self):
        """Return the most viewed videos."""
        return self._request("%s/most_viewed", self.url_standardfeeds)

    def most_recent(self):
        """Return the most recently posted videos."""
        return self._request("%s?vq=*&orderby=published",
                             self.url_video_search)

    def recently_featured(self):
        """Return the recently featured videos."""
        return self._request("%s/recently_featured",
                             self.url_standardfeeds)

    def category_list(self):
        """Return a list of video categories."""
        xml = urllib2.urlopen(self.url_categories).read()

        tree = ElementTree.fromstring(xml)
        categories = {}
        for child in tree.getchildren():
            categories[child.get('term')] = child.get('label')
        return categories

    def video_by_category(self, category_id):
        """Return videos from a specific category."""
        return self._request("%s/%s", self.url_video_by_category,
                             category_id)

    def related_videos(self, video):
        url = video.get_related_videos_link()[1]
        return self._request(url)

    @classmethod
    def format_to_string(cls, fmt_id):
        formats = {
            22: "HD Quality",
            35: "High Quality",
            34: "Reasonable Quality",
            18: "Good Quality",
            5: "Bad Quality" }
        return formats[fmt_id]

    @classmethod
    def resolve_video_url(cls, video_id):
        std_headers = {
            'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1',
            'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
            'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
            'Accept-Language': 'en-us,en;q=0.5',
        }

        url = cls.url_video_request % str(video_id)

        request = urllib2.Request(url, None, std_headers)
        try:
            video_webpage = urllib2.urlopen(request).read()
        except (urllib2.URLError, httplib.HTTPException, socket.error), err:
            return None

        # Try to find the best video format available for this video
        # (http://forum.videohelp.com/topic336882-1800.html#1912972)
        r3 = re.compile('.*"fmt_map"\:\s+"([^"]+)".*').search(video_webpage)
        if r3:
            formats_available = urllib.unquote(r3.group(1)).split(',')
        else:
            formats_available = []

        # This is the proritized list of formats that gPodder will
        # use, depending on what is available from top to bottom.
        format_priorities = [
                '22/2000000/9/0/115', # 1280x720
                '35/640000/9/0/115',  # 640x360
                '18/512000/9/0/115',  # 480x270
                '34/0/9/0/115',       # 320x180
                '5/0/7/0/0',          # 320x180
        ]

        fmt_id = 5
        available = []
        for wanted in format_priorities:
            if wanted in formats_available:
                format, rest_ = wanted.split('/', 1)
                fmt_id = int(format)
                available.append(fmt_id)
  
        r2 = re.compile('.*"t"\:\s+"([^"]+)".*').search(video_webpage)
        if r2:
            video_real_url = 'http://www.youtube.com/get_video?video_id=' + video_id + '&t=' + r2.group(1)
            return video_real_url, available
        return None, None

class InfoVideo(object):
    """Store information of a YouTube video."""

    def __init__(self, id, title):
        self.id = id
        self.title = title
        self.links = None
        self.rating = None
        self.authors = None
        self.view_count = 0
        self.thumbnails = None
        self.description = ""
        self.duration = 0

    def get_small_thumbnail(self):
        """Get the smallest thumb in size."""
        if not self.thumbnails:
            return None
        else:
            sizes = self.thumbnails.keys()
            sizes.sort()
            return self.thumbnails[sizes[0]][0]

    def get_large_thumbnail(self):
        """Get the largest thumb in size."""
        if not self.thumbnails:
            return None
        else:
            sizes = self.thumbnails.keys()
            sizes.sort()
            return self.thumbnails[sizes[-1]][0]

    def get_related_videos_link(self):
        if self.links:
            return self.links.get(
                "http://gdata.youtube.com/schemas/2007#video.related",
                None)

class InfoVideoAuthor(object):
    """Store information of a YouTube video author."""

    def __init__(self, name, uri=None, email=None):
        self.name = name
        self.uri = uri
        self.email = email


class InfoVideoRating(object):
    """Store information of a YouTube video rating."""

    def __init__(self, min=0, max=0, avg=0, num=0):
        self.min = min
        self.max = max
        self.avg = avg
        self.num = num


def get_namespaces(xml):
    space = {}
    ir = re.compile("<feed ([^>]+)")
    for c in ir.findall(xml)[0].split(' '):
        name, value = c.split("=")
        name = name.strip()
        value = value.strip()[1:-1]
        space[name] = value

    return space


def parse_youtube_xml(xml):
    """Parse an entry from youtube feed.

    Parse youtube feed and return summary and entries.
    """
    space = get_namespaces(xml)
    tree = ElementTree.fromstring(xml)

    summary = {}
    summary['total'] = int(tree.find("{%s}totalResults" % space['xmlns:openSearch']).text)
    summary['index'] = int(tree.find("{%s}startIndex" % space['xmlns:openSearch']).text)
    summary['items'] = int(tree.find("{%s}itemsPerPage" % space['xmlns:openSearch']).text)
    summary['links'] = {}

    for child in tree.findall("{%s}link" % space['xmlns']):
        rel = child.get("rel")
        if rel not in summary['links']:
            summary['links'][rel] = child.get("href")

    lst = []
    for child in tree.findall("{%s}entry" % space['xmlns']):
        id = child.find("{%s}id" % space['xmlns'])
        title = child.find("{%s}title" % space['xmlns'])

        info = InfoVideo(id=id.text.split("/")[-1],
                         title=title.text)

        info.updated = child.find('{%s}updated' % space['xmlns']).text
        info.published = child.find('{%s}published' % space['xmlns']).text

        info.links = {}
        for c in child.findall("{%s}link" % space['xmlns']):
            info.links[c.get("rel")] = ("", c.get("href"))

        info.authors = []
        for c in child.findall("{%s}author" % space['xmlns']):
            uri = c.find("{%s}uri" % space['xmlns'])
            name = c.find("{%s}name" % space['xmlns'])

            author = InfoVideoAuthor(name=name.text,
                                     uri=uri.text)
            info.authors.append(author)

        # rating
        tr = child.find("{%s}rating" % space['xmlns:gd'])
        info.rating = InfoVideoRating()
        if tr is not None:
            info.rating.min = float(tr.get("min", 0))
            info.rating.max = float(tr.get("max", 0))
            info.rating.avg = float(tr.get("average", 0))
            info.rating.num = float(tr.get("numRaters", 0))

        # viewcount
        tr = child.find("{%s}statistics" % space['xmlns:yt'])
        if tr is None:
            info.view_count = 0
        else:
            info.view_count = int(tr.get("viewCount", 0))

        # video thumbnails
        info.thumbnails = {}
        for tr in child.findall(".//{%s}group" % space['xmlns:media']):
            for description in tr.findall("{%s}description" % space['xmlns:media']):
                info.description = description.text
                if info.description:
                    info.description = info.description.replace("\n", "<br>")
                else:
                    info.description = ""
            for content in tr.findall('{%s}content' % space['xmlns:media']):
                info.duration = int(content.get('duration', 0))


            for c in tr.findall("{%s}thumbnail" % space['xmlns:media']):
                url = c.get("url")
                size = (int(c.get("width")), int(c.get("height")))
                if size not in info.thumbnails:
                    info.thumbnails[size] = [url,]
                else:
                    info.thumbnails[size].append(url)

        lst.append(info)

    return (summary, lst)

class MainScreen:

    def __init__(self):
        global rotation
        self.p = hildon.Program.get_instance()
        gtk.set_application_name("zoutube")
        self.w = hildon.StackableWindow()
        rotation = FremantleRotation("zoutube", main_window=self.w,
            version="0.1")
        self.mainvbox = gtk.VBox()
        self.pan = hildon.PannableArea()
        box = gtk.VBox()
        self.w.connect("delete-event", lambda x, y: gtk.main_quit())
        b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
            hildon.BUTTON_ARRANGEMENT_VERTICAL,
            title = "Recently Featured")
        b.connect("clicked", self.on_button_clicked, "recentlyfeatured")
        box.pack_start(b, False, False, 0)
        b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
            hildon.BUTTON_ARRANGEMENT_VERTICAL,
            title = "Most Viewed")
        b.connect("clicked", self.on_button_clicked, "mostviewed")
        box.pack_start(b, False, False, 0)
        b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
            hildon.BUTTON_ARRANGEMENT_VERTICAL,
            title = "Most Recent")
        b.connect("clicked", self.on_button_clicked, "mostrecent")
        box.pack_start(b, False, False, 0)
        b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
            hildon.BUTTON_ARRANGEMENT_VERTICAL,
            title = "Top Rated")
        b.connect("clicked", self.on_button_clicked, "toprated")
        box.pack_start(b, False, False, 0)
        self.box = box
        self.y = YouTube()
        Thread(target=self.retrieve_categories).start()
        self.pan.add_with_viewport(box)
        self.mainvbox.pack_start(self.pan, True, True, 0)
        self.w.add(self.mainvbox)
        self.w.connect("key-press-event", self.key_pressed)
        self.w.show_all()
        hildon.hildon_gtk_window_set_progress_indicator(self.w, 1)
        self.p.add_window(self.w)
        self.searchbox = None
        self.ctx = osso.context.Context("zoutube", "0.1")
    
    def key_pressed(self, widget, event):
        if not self.searchbox:
            self.searchbox = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
            self.searchbox.set_property('hildon-input-mode',
                gtk.HILDON_GTK_INPUT_MODE_FULL)
            self.mainvbox.pack_start(self.searchbox, False, False, 0)
            self.mainvbox.show_all()
            self.searchbox.grab_focus()
            self.searchbox.emit("key-press-event", event)
            self.searchbox.connect("activate", self.search_activated)
        return False

    def search_activated(self, widget):
        searchtext = widget.props.text
        self.mainvbox.remove(self.searchbox)
        self.searchbox = None
        if searchtext:
            videolist = YoutubeUI(self.ctx, self.y)
            Thread(target=self.search_video_list, args=(searchtext, videolist)).start()
                    
    def retrieve_categories(self):
        try:
            c = self.y.category_list()
            gobject.idle_add(self.show_categories, c)
        except:
            gobject.idle_add(hildon.hildon_banner_show_information, self.w, '',
                "Could not connect to Youtube. Please check your network.")

    def show_categories(self, c):
        for k in c:
            v = c[k]
            b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
                hildon.BUTTON_ARRANGEMENT_VERTICAL,
                title = v)
            b.connect("clicked", self.on_button_clicked, k)
            self.box.pack_start(b, False, False, 0)
        self.box.show_all()
        hildon.hildon_gtk_window_set_progress_indicator(self.w, 0)

    def on_button_clicked(self, button, data):
        videolist = YoutubeUI(self.ctx, self.y)
        Thread(target=self.retrieve_video_list, args=(data, videolist)).start()
    
    def retrieve_video_list(self, data, videolist):
        try:
            if data == "toprated":
                videos = self.y.top_rated()
            elif data == "mostviewed":
                videos = self.y.most_viewed()        
            elif data == "mostrecent":
                videos = self.y.most_recent()
            elif data == "recentlyfeatured":
                videos = self.y.recently_featured()
            else:
                videos = self.y.video_by_category(data)
            gobject.idle_add(videolist.set_videos, videos)
        except Exception, e:
            print e
            gobject.idle_add(hildon.hildon_banner_show_information, self.w, '',
                "Could not connect to Youtube. Please check your network.")

    def search_video_list(self, searchtext, videolist):
        try:
            videos = self.y.search(searchtext)
            gobject.idle_add(videolist.set_videos, videos)
        except:
            gobject.idle_add(hildon.hildon_banner_show_information, self.w, '',
                "Could not connect to Youtube. Please check your network.")

class YoutubeUI:
    def __init__(self, ctx, y):
        # hildon has one program instance per app, so get instance
        self.p = hildon.Program.get_instance()
        self.ctx = ctx
        self.y = y
        self.w = hildon.StackableWindow()
        self.w.connect("configure-event", self.on_configure_event)
        self.pan = hildon.PannableArea()
        self.model = gtk.ListStore(str, gtk.gdk.Pixbuf, int)
        self.iconview = hildon.GtkIconView(gtk.HILDON_UI_MODE_NORMAL)
        self.iconview.set_model(self.model)
        self.iconview.set_pixbuf_column(1)
        self.iconview.set_text_column(0)
        self.iconview.connect("item-activated", self.on_item_activated)
        self.box = gtk.VBox()
        self.buttons = []
        #self.pan.add_with_viewport(self.box)
        self.pan.add(self.iconview)
        self.w.add(self.pan)
        self.w.show_all()
        hildon.hildon_gtk_window_set_progress_indicator(self.w, 1)
        self.p.add_window(self.w)
        if self.which('gpodder'):
            self.menu = hildon.AppMenu()
            b = gtk.Button("Add this feed to gPodder")
            b.connect_after("clicked", self.on_add_feed) 
            self.menu.append(b)
            self.menu.show_all()
            self.w.set_app_menu(self.menu)

    def on_add_feed(self, button):
        Thread(target=self.add_gpodder_feed).start()

    def add_gpodder_feed(self):
        import os
        os.system("gpodder --fremantle --subscribe=%s" % (self.y.last_url))

    def which(self, program):
        import os
        def is_exe(fpath):
            return os.path.exists(fpath) and os.access(fpath, os.X_OK)

        fpath, fname = os.path.split(program)
        if fpath:
            if is_exe(program):
                return program
        else:
            for path in os.environ["PATH"].split(os.pathsep):
                exe_file = os.path.join(path, program)
                if is_exe(exe_file):
                    return exe_file

        return None

    def on_configure_event(self, window, event):
        if event.height > event.width:
            self.iconview.set_columns(1)
        else:
            self.iconview.set_columns(2)

    def set_videos(self, videos):
        self.videos = videos
        Thread(target=self.retrieve_thumbnails).start()

    def retrieve_thumbnails(self):
        i = 0
        for v in self.videos:
            print "thumbnails: %r" % (v.thumbnails,)
            thumbnail = urllib2.urlopen(v.thumbnails[(120, 90)][0]).read()
            pixbufloader = gtk.gdk.PixbufLoader()
            pixbufloader.write(thumbnail)
            pixbufloader.close()
            pixbuf = pixbufloader.get_pixbuf()
            self.model.append([v.title, pixbuf, i])
            i = i + 1
            #b = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT, 
            #    hildon.BUTTON_ARRANGEMENT_VERTICAL,
            #    title = v.title)
            #menu = self.create_popup_menu(v)
            #b.tap_and_hold_setup(menu, None)
            #b.connect("clicked", self.on_button_clicked, v)
            #self.buttons.append(b)
            #self.box.pack_start(b, False, False, 0)
        #self.box.show_all()
        hildon.hildon_gtk_window_set_progress_indicator(self.w, 0)

    def on_item_activated(self, iconview, path):
        tree_iter = self.model.get_iter(path)
        v = self.videos[self.model.get_value(tree_iter, 2)]
        video = YoutubePlayer(v, self.ctx)

    def create_popup_menu(self, v):
        menu = gtk.Menu()
        download = gtk.MenuItem("Open in browser")
        download.connect("activate", self.open_browser, v)
        menu.append(download)
        menu.show_all()
        return menu

    def open_browser(self, widget, v):
        url = YouTube.url_video_request % str(v.id)
        bus = dbus.SessionBus()
        remote_object = bus.get_object("com.nokia.osso_browser", 
            "/com/nokia/osso_browser/request")
        remote_object.open_new_window(url, dbus_interface = "com.nokia.osso_browser")

    def on_button_clicked(self, button, v):
        # display video
        video = YoutubePlayer(v, self.ctx)

class YoutubePlayer:
    UPDATE_INTERVAL = 500

    def __init__(self, video, ctx):
        global rotation
        # position and duration values
        self.p_position = gst.CLOCK_TIME_NONE
        self.p_duration = gst.CLOCK_TIME_NONE
        self.is_playing = False
        self.update_id = -1
        self.changed_id = -1
        self.seek_id = -1

        # hildon has one program instance per app, so get instance
        self.p = hildon.Program.get_instance()
        # set name of application: this shows in titlebar
        # stackable window in case we want more windows in future in app
        self.w = hildon.StackableWindow()
        box = gtk.VBox()
        self.video_widget = VideoWidget()
        # video widget we want to expand to size
        box.pack_start(self.video_widget, True, True, 0)
        # handle button presses
        self.video_widget.connect("button-press-event", self.button_pressed)
        # a button finger height to play/pause
        self.hbox = gtk.HBox()
        self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0)
        self.seekbar = hildon.GtkHScale(self.adjustment)
        self.seekbar.set_digits(2)
        self.seekbar.set_update_policy(gtk.UPDATE_CONTINUOUS)
        self.seekbar.connect('button-press-event', self.on_seekbar_button_pressed)
        self.seekbar.connect('button-release-event', self.on_seekbar_button_release)
        self.seekbar.connect('format-value', self.on_seekbar_format_value)
        #self.hbox.pack_start(self.seekbar, True, True, 0)
        self.button = gtk.Button()
        self.pause_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE,
                                                    gtk.ICON_SIZE_BUTTON)
        self.pause_image.show()
        self.play_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY,
                                                   gtk.ICON_SIZE_BUTTON)
        self.play_image.show()
        self.button.add(self.pause_image)
        self.button.connect_after("clicked", self.on_button_clicked)
        # don't want button to expand or fill, just stay finger height
        self.hbox.pack_start(self.button, False, False, 0)
        self.hbox.pack_start(self.seekbar, True, True, 0)
        box.pack_start(self.hbox, False, False, 0)
        self.w.add(box)
        self.w.connect("delete-event", lambda x, y: self.on_delete_event())
        self.p.add_window(self.w)
        self.w.show_all()
        rotation.set_mode(FremantleRotation.NEVER)
        self.is_fullscreen = False
        #self.w.fullscreen()
        self.video = video
        hildon.hildon_gtk_window_set_progress_indicator(self.w, 1)
        self.ctx = ctx

        self.start_streaming()

    def query_position(self):
        "Returns a (position, duration) tuple"
        try:
            position, format = self.pipeline.query_position(gst.FORMAT_TIME)
        except:
            position = gst.CLOCK_TIME_NONE

        try:
            duration, format = self.pipeline.query_duration(gst.FORMAT_TIME)
        except:
            duration = gst.CLOCK_TIME_NONE

        return (position, duration)

    def seek(self, location):
        """
        @param location: time to seek to, in nanoseconds
        """
        event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
            gst.SEEK_TYPE_SET, location,
            gst.SEEK_TYPE_NONE, 0)

        res = self.pipeline.send_event(event)
        if res:
            print "setting new stream time to 0"
            self.pipeline.set_new_stream_time(0L)
        else:
            print "seek to %r failed" % (location,)

    def on_seekbar_format_value(self, scale, value):
        if self.p_duration == gst.CLOCK_TIME_NONE:
            real = 0
        else:
            real = value * self.p_duration / 100
        
        seconds = real/gst.SECOND
        return "%02d:%02d" % (seconds / 60, seconds % 60)

    def on_seekbar_value_changed(self, scale):
        # see seek.c:seek_cb
        real = long(scale.get_value() * self.p_duration / 100) # in ns
        gst.debug('value changed, perform seek to %r' % real)
        self.seek(real)
        # allow for a preroll
        self.pipeline.get_state(timeout=50*gst.MSECOND) # 50 ms

    def on_seekbar_button_pressed(self, widget, event):
        # see seek.c:start_seek
        print 'starting seek'
        
        #self.button.set_sensitive(False)
        self.was_playing = self.is_playing
        if self.was_playing:
            self.pipeline.set_state(gst.STATE_PAUSED)
            self.is_playing = False
        # don't timeout-update position during seek
        if self.update_id != -1:
            gobject.source_remove(self.update_id)
            self.update_id = -1

        # make sure we get changed notifies
        if self.changed_id == -1:
            self.changed_id = self.seekbar.connect('value-changed',
                self.on_seekbar_value_changed)

    def on_seekbar_button_release(self, widget, event):
        # see seek.cstop_seek
        widget.disconnect(self.changed_id)
        self.changed_id = -1

        #self.button.set_sensitive(True)
        if self.seek_timeout_id != -1:
            gobject.source_remove(self.seek_timeout_id)
            self.seek_timeout_id = -1
        else:
            print 'released slider, setting back to playing'
            if self.was_playing:
                self.pipeline.set_state(gst.STATE_PLAYING)
                self.is_playing = True
            else:
                print "weird"
        if self.update_id != -1:
            self.error('Had a previous update timeout id')
        else:
            self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
                self.update_seekbar)

    def update_seekbar(self):
        self.p_position, self.p_duration = self.query_position()
        if self.p_position != gst.CLOCK_TIME_NONE:
            value = self.p_position * 100.0 / self.p_duration
            self.adjustment.set_value(value)
        return True

    def heartbeat(self):
        self.ds.display_state_on()
        self.ds.display_blanking_pause()

    def button_pressed(self, w, event):
        if event.type == gtk.gdk._2BUTTON_PRESS:
            if self.is_fullscreen:
                self.w.unfullscreen()
                self.is_fullscreen = False
            else:
                self.w.fullscreen()
                self.is_fullscreen = True
        return True

    def start_streaming(self):
        # we use ximagesink solely for screenshotting ability
        # less cpu usage would happen with videotestsrc ! xvimagesink
        v = self.video        
        url, formats = YouTube.resolve_video_url(v.id)
        self.url = url
        self.formats = formats
        self.menu = hildon.AppMenu()
        b = gtk.Button("Download")
        b.connect_after("clicked", self.download) 
        self.menu.append(b)
        b = gtk.Button("Related Videos")
        b.connect_after("clicked", self.get_related_videos)
        self.menu.append(b)
        for fmt in formats:
            if fmt != 22 and fmt != 35 and fmt != 34:
                b = gtk.Button(YouTube.format_to_string(fmt))
                b.connect_after("clicked", self.on_change_url, url, fmt)
                self.menu.append(b)
        self.menu.show_all()
        self.w.set_app_menu(self.menu)
        print "url: %r formats: %r" % (url, formats)
        for fmt in formats:
            if fmt != 22 and fmt != 35 and fmt != 34:
                break
        self.current_format = fmt
        url = "%s&fmt=%d" % (url, fmt)
        self.play_url(url)

    def try_lower_quality(self):
        found_current = False
        for fmt in self.formats:
            if fmt != 22 and fmt != 35 and fmt != self.current_format:
                if found_current:
                    self.on_change_url(None, self.url, fmt)
                    break
            elif fmt == self.current_format:
                found_current = True

    def on_change_url(self, button, url, fmt):
        url = "%s&fmt=%d" % (url, fmt)
        self.stop_streaming()
        self.current_format = fmt
        self.play_url(url)

    def download(self, button):
        url = "%s&fmt=%d" % (self.url, self.current_format)
        bus = dbus.SessionBus()
        remote_object = bus.get_object("com.nokia.osso_browser", 
            "/com/nokia/osso_browser/request")
        remote_object.open_new_window(url, dbus_interface = "com.nokia.osso_browser")

    def get_related_videos(self, button):
        self.stop_streaming()
        self.button.add(self.play_image)
        videolist = YoutubeUI(self.ctx, self.y)
        Thread(target=self.retrieve_video_list, args=(videolist,)).start()
    
    def retrieve_video_list(self, videolist):
        self.y = YouTube()
        videos = self.y.related_videos(self.video)
        gobject.idle_add(videolist.set_videos, videos)

    def play_url(self, url):
        # need to do the display state stuff to stop the screen being dimmed on us
        self.ds = osso.DeviceState(self.ctx)
        self.heartbeat()
        self.timer = gobject.timeout_add_seconds(30, self.heartbeat)

        print "real url: %r" % (url,)
        self.pipeline = \
            gst.parse_launch("playbin2 uri=%s" % (url,))
        bus = self.pipeline.get_bus()
        # need to connect to sync message handler so we get the sink to be
        # embedded at the right time and not have a temporary new window
        bus.enable_sync_message_emission()
        bus.add_signal_watch()
        bus.connect("sync-message::element", self.on_sync_message)
        bus.connect("message", self.on_message)
        self.pipeline.set_state(gst.STATE_PLAYING)
        self.is_playing = True
        if self.update_id == -1:
            self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
                                                 self.update_seekbar)

    def on_sync_message(self, bus, message):
        if message.structure is None:
            return
        if message.structure.get_name() == 'prepare-xwindow-id':
            # all this is needed to sync with the X server before giving the
            # x id to the sink
            gtk.gdk.threads_enter()
            gtk.gdk.display_get_default().sync()
            self.video_widget.set_sink(message.src)
            message.src.set_property("force-aspect-ratio", True)
            gtk.gdk.threads_leave()

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_ERROR:
            err, debug = message.parse_error()
            hildon.hildon_banner_show_information(self.w, '', 
                "Error: %s Trying lower quality format" % err)
            self.try_lower_quality()
        elif message.type == gst.MESSAGE_STATE_CHANGED:
            if message.src == self.pipeline:
                old, new, pending = message.parse_state_changed()
                if (old, new, pending) == (gst.STATE_PAUSED, gst.STATE_PLAYING,
                    gst.STATE_VOID_PENDING):
                     hildon.hildon_gtk_window_set_progress_indicator(self.w, 0)
                     print "paused to playing"
        elif message.type == gst.MESSAGE_EOS:
            self.seek(0L)

    def on_button_clicked(self, widget):
        success, state, pending = self.pipeline.get_state(1)
        # do not listen if in middle of state change
        if not pending:
            self.button.remove(self.button.child)
            if state == gst.STATE_PLAYING:
                self.pipeline.set_state(gst.STATE_PAUSED)
                self.is_playing = False
                self.button.add(self.play_image)
            else:
                self.pipeline.set_state(gst.STATE_PLAYING)
                self.is_playing = True
                self.button.add(self.pause_image)

    def on_delete_event(self):
        global rotation        
        self.stop_streaming()
        rotation.set_mode(FremantleRotation.AUTOMATIC)

    def stop_streaming(self):
        self.pipeline.set_state(gst.STATE_NULL)
        gobject.source_remove(self.timer)
        self.is_playing = False

if __name__ == "__main__":
    ui = MainScreen()
    gtk.main()
