diff --git a/build/build.environment.mk b/build/build.environment.mk
index 0e9b736..bce7fee 100644
--- a/build/build.environment.mk
+++ b/build/build.environment.mk
@@ -23,6 +23,7 @@ LINK_TAGLIB = $(TAGLIB_SHARP_LIBS)
 LINK_IPOD = $(IPODSHARP_LIBS)
 LINK_BOO = $(BOO_LIBS)
 LINK_WEBKIT = $(WEBKIT_LIBS)
+LINK_GDATA = $(GDATASHARP_LIBS)
 LINK_MONOTORRENT_DBUS = $(MONOTORRENT_DBUS_LIBS)
 LINK_MONO_ADDINS_DEPS = $(MONO_ADDINS_LIBS)
 LINK_MONO_ADDINS_SETUP_DEPS = $(MONO_ADDINS_SETUP_LIBS)
diff --git a/build/m4/banshee/gdata.m4 b/build/m4/banshee/gdata.m4
new file mode 100644
index 0000000..5e1de2e
--- /dev/null
+++ b/build/m4/banshee/gdata.m4
@@ -0,0 +1,17 @@
+AC_DEFUN([BANSHEE_CHECK_GDATA],
+[
+	GDATASHARP_REQUIRED_VERSION=1.4
+	AC_SUBST(GDATASHARP_REQUIRED_VERSION)
+
+	AC_ARG_ENABLE(gdata, AC_HELP_STRING([--enable-gdata], [Enable experimental Youtube extension - unfinished, likely broken]), , enable_gdata="no")
+
+	if test "x$enable_gdata" = "xyes"; then
+		PKG_CHECK_MODULES(GDATASHARP,
+			gdata-sharp-core >= $GDATASHARP_REQUIRED_VERSION
+			gdata-sharp-youtube >= $GDATASHARP_REQUIRED_VERSION)
+		AC_SUBST(GDATASHARP_LIBS)
+		AM_CONDITIONAL(HAVE_GDATA, true)
+	else
+		AM_CONDITIONAL(HAVE_GDATA, false)
+	fi
+])
diff --git a/configure.ac b/configure.ac
index fdd49f4..b9dbb1f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,9 @@ SHAMROCK_CHECK_MONODOC
 dnl webkit (optional through --disable-webkit)
 BANSHEE_CHECK_WEBKIT
 
+dnl gdata (optional through --enable-gdata)
+BANSHEE_CHECK_GDATA
+
 dnl gtk#-beans and gio#
 BANSHEE_CHECK_GIO_SHARP
 
@@ -306,6 +309,7 @@ src/Extensions/Banshee.Sample/Makefile
 src/Extensions/Banshee.Torrent/Makefile
 src/Extensions/Banshee.RemoteAudio/Makefile
 src/Extensions/Banshee.Wikipedia/Makefile
+src/Extensions/Banshee.YouTube/Makefile
 
 extras/metrics/Makefile
 ])
@@ -360,6 +364,7 @@ EOF
 if test "x$enable_moonlight" = "xyes";    then br=yes; echo "  Moonlight Effects:   ${enable_moonlight}";    fi
 if test "x$enable_remote_audio" = "xyes"; then br=yes; echo "  Remote Audio:        ${enable_remote_audio}"; fi
 if test "x$enable_torrent" = "xyes";      then br=yes; echo "  Torrent Podcasts:    ${enable_torrent}";      fi
+if test "x$enable_gdata" = "xyes";        then br=yes; echo "  Google Data API:     ${enable_gdata}";        fi
 
 if test -d ${expanded_libdir}/${PACKAGE}; then
 	if test x$br = xyes; then echo; fi
diff --git a/data/addin-xml-strings.cs b/data/addin-xml-strings.cs
index 2861eb7..286261c 100644
--- a/data/addin-xml-strings.cs
+++ b/data/addin-xml-strings.cs
@@ -185,6 +185,11 @@ internal static class AddinXmlStringCatalog
         Catalog.GetString (@"View artist information from Wikipedia in the context pane.");
         Catalog.GetString (@"Context Pane");
 
+        // ../src/Extensions/Banshee.YouTube/Banshee.YouTube.addin.xml
+        Catalog.GetString (@"YouTube");
+        Catalog.GetString (@"Show related YouTube videos in the context pane");
+        Catalog.GetString (@"Context Pane");
+
         // ../src/Extensions/Template/Template.addin.xml
 
     }
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/DataFetch.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/DataFetch.cs
new file mode 100644
index 0000000..04c5f73
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/DataFetch.cs
@@ -0,0 +1,159 @@
+//
+// DataFetch.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Net;
+using System.Web;
+using System.Xml;
+using System.Collections;
+using System.Collections.Generic;
+using ICSharpCode.SharpZipLib.GZip;
+
+using Banshee.Base;
+using Hyena;
+
+namespace Banshee.YouTube.Data
+{
+    public class DataFetch
+    {
+        private const int CACHE_VERSION = 2;
+        public static string UserAgent = Banshee.Web.Browser.UserAgent;
+        public static string CachePath = Path.Combine (Banshee.Base.Paths.ExtensionCacheRoot, "youtube");
+        public static TimeSpan NormalCacheTime = TimeSpan.FromHours (2);
+
+        private static bool initialized = false;
+
+        public DataFetch ()
+        {
+            if (!initialized) {
+                initialized = true;
+
+                if (CachePath == null || UserAgent == null) {
+                    throw new NotSupportedException ("CachePath and/or Useragent are null. Applications must set this value.");
+                }
+
+                VerifyCachePath ();
+            }
+        }
+
+        public string GetWatchPageContents (string url)
+        {
+            string contents = String.Empty;
+
+            HttpWebRequest request = (HttpWebRequest) WebRequest.Create (url);
+
+            try {
+                HttpWebResponse response = (HttpWebResponse) request.GetResponse ();
+                Stream stream = response.GetResponseStream ();
+                StreamReader stream_reader = new StreamReader (stream);
+                contents = stream_reader.ReadToEnd ();
+
+                return contents;
+            } catch (Exception e) {
+                Log.DebugException (e);
+            }
+
+            return contents;
+        }
+
+        public string DownloadContent (string data_url)
+        {
+            return DownloadContent (data_url, CacheDuration.Infinite);
+        }
+
+        public string DownloadContent (string data_url, CacheDuration cache_duration)
+        {
+            string [] split_data_url = data_url.Split ('/');
+            string fname = split_data_url[split_data_url.Length - 2] + Path.GetExtension (data_url);
+
+            return DownloadContent (data_url, GetCachedPath (fname), cache_duration);
+        }
+
+        internal static string DownloadContent (string data_url, string cache_file, CacheDuration cache_duration)
+        {
+            SafeUri uri = new SafeUri (cache_file);
+
+            if (String.IsNullOrEmpty (data_url) || String.IsNullOrEmpty (cache_file)) {
+                return null;
+            }
+
+            // See if we have a valid cached copy
+            if (cache_duration != CacheDuration.None) {
+                if (Banshee.IO.File.Exists (uri)) {
+                    DateTime last_updated_time = DateTime.FromFileTime (Banshee.IO.File.GetModifiedTime (uri));
+                    if (cache_duration == CacheDuration.Infinite || DateTime.Now - last_updated_time < DataFetch.NormalCacheTime) {
+                        return cache_file;
+                    }
+                }
+            }
+
+            HttpWebRequest request = (HttpWebRequest) WebRequest.Create (data_url);
+            request.UserAgent = DataFetch.UserAgent;
+            request.KeepAlive = false;
+
+            try {
+                using (HttpWebResponse response = (HttpWebResponse) request.GetResponse ()) {
+                    Banshee.IO.StreamAssist.Save (GetResponseStream (response),
+                                                  Banshee.IO.File.OpenWrite (uri, true));
+                }
+            } catch (Exception e) {
+                Log.DebugException (e);
+            }
+            return cache_file;
+        }
+
+        private static string GetCachedPath (string fname)
+        {
+            if (fname == null) {
+                return null;
+            }
+
+            if (fname.Length > 2) {
+                return Path.Combine (DataFetch.CachePath, fname);
+            } else {
+                return String.Empty;
+            }
+        }
+
+        private static Stream GetResponseStream (HttpWebResponse response)
+        {
+            return response.ContentEncoding == "gzip"
+                ? new GZipInputStream (response.GetResponseStream ())
+                : response.GetResponseStream ();
+        }
+
+        private static void VerifyCachePath ()
+        {
+            if (!Banshee.IO.Directory.Exists (CachePath)) {
+                Banshee.IO.Directory.Create (CachePath);
+                return;
+            }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/YouTubeData.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/YouTubeData.cs
new file mode 100644
index 0000000..ff7ff35
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Data/YouTubeData.cs
@@ -0,0 +1,92 @@
+//
+// YouTubeData.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Mono.Unix;
+using Gtk;
+
+using Banshee.Widgets;
+
+using Google.GData.Client;
+using Google.GData.Extensions;
+using Google.GData.YouTube;
+using Google.GData.Extensions.MediaRss;
+using Google.YouTube;
+
+namespace Banshee.YouTube.Data
+{
+    public enum CacheDuration
+    {
+        None = 0,
+        Normal,
+        Infinite
+    }
+
+    public class DataCore
+    {
+        private Feed<Video> video_results;
+        private YouTubeRequest yt_request;
+        private const string app_name = "Banshee.YouTube";
+        private const string client_id = "ytapi-KevinDuffus-BansheeMediaPlay-11toa30i-0";
+        private const string developer_key = "AI39si7lcnwsjfic8V-K-Is-nwt2EqwnpZSBNGRuF-rUmcZ4WGB_pDSZxXI5uwMDePtMfJwvmYwDln625leh0IOBtuZ1DjV7vg";
+
+        public bool InitYouTubeRequest ()
+        {
+            YouTubeRequestSettings yt_request_settings = new YouTubeRequestSettings (app_name, client_id, developer_key);
+            this.yt_request = new YouTubeRequest (yt_request_settings);
+
+            if (this.yt_request != null && yt_request_settings != null) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public void PerformSearch (string searchVal)
+        {
+            YouTubeQuery query = new YouTubeQuery (YouTubeQuery.DefaultVideoUri);
+
+            //order results by the number of views (most viewed first)
+            query.OrderBy = "relevance";
+
+            // perform querying with restricted content included in the results
+            // query.SafeSearch could also be set to YouTubeQuery.SafeSearchValues.Moderate
+            query.Query = searchVal;
+            query.SafeSearch = YouTubeQuery.SafeSearchValues.None;
+
+            this.video_results = yt_request.Get<Video> (query);
+        }
+
+        public Feed<Video> Videos {
+            get { return this.video_results; }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/VideoStreamTile.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/VideoStreamTile.cs
new file mode 100755
index 0000000..9abc75a
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/VideoStreamTile.cs
@@ -0,0 +1,252 @@
+//
+// VideoStreamTile.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using Mono.Unix;
+using Gtk;
+
+using Banshee.Base;
+using Banshee.Web;
+using Banshee.Collection;
+using Banshee.ServiceStack;
+
+using Hyena;
+using Hyena.Widgets;
+
+/*
+    Tile layout:
+        +===========================+====+
+        | +-------+                 |    |
+        | |       |   Primary Text  |    |
+        | | Image |  Secondary Text | \/ |
+        | |       |    * * * * *    |    |
+        | +-------+                 |    |
+        +===========================+----+
+                | Play in Banshee...     |
+                | Play in Web Browser... |
+                |------------------------|
+                | Add to Play Queue...   |
+                | Import video...        |
+                +------------------------+
+*/
+
+namespace Banshee.YouTube.Gui
+{
+    public class VideoStreamTile : MenuButton
+    {
+        private Button button = new Button ();
+        private Menu menu = new Menu ();
+        private ActionGroup action_group;
+        private TrackInfo track_info;
+
+        private HBox hbox = new HBox ();
+        private VBox vbox = new VBox ();
+        private Image image = new Image ();
+        private Label primary_label = new Label ();
+        private Label secondary_label = new Label ();
+        private RatingEntry rating = new RatingEntry ();
+
+        private string banshee_playback_uri;
+        private string browser_playback_uri;
+        private string video_title;
+        private string video_uploader;
+        private string primary_text;
+        private string secondary_text;
+
+        public VideoStreamTile () : base ()
+        {
+            hbox.BorderWidth = 2;
+            hbox.Spacing = 6;
+
+            vbox.PackStart (primary_label, true, true, 0);
+            vbox.PackStart (secondary_label, true, true, 0);
+            vbox.PackStart (rating, true, true, 0);
+
+            hbox.PackStart (image, true, true, 0);
+            hbox.PackStart (vbox, true, false, 0);
+
+            hbox.ShowAll ();
+            button.Add (hbox);
+            button.Clicked += new EventHandler (OnButtonClicked);
+
+            primary_label.WidthChars = 24;
+            secondary_label.WidthChars = 24;
+            primary_label.Ellipsize = Pango.EllipsizeMode.End;
+            secondary_label.Ellipsize = Pango.EllipsizeMode.End;
+
+            rating.Sensitive = false;
+            rating.HasFrame = false;
+            rating.AlwaysShowEmptyStars = true;
+
+            try {
+                StyleSet += delegate {
+                    primary_label.ModifyFg (StateType.Normal, Style.Text (StateType.Normal));
+                    secondary_label.ModifyFg (StateType.Normal, Hyena.Gui.GtkUtilities.ColorBlend (
+                        Style.Foreground (StateType.Normal), Style.Background (StateType.Normal)));
+                };
+            } catch (Exception e) {
+                Log.DebugException (e);
+            }
+
+            button.Relief = ReliefStyle.None;
+            ConstructTile ();
+        }
+
+        public string BansheePlaybackUri {
+            get { return banshee_playback_uri; }
+            set { banshee_playback_uri = value; }
+        }
+
+        public string BrowserPlaybackUri {
+            get { return browser_playback_uri; }
+            set { browser_playback_uri = value; }
+        }
+
+        public string Title {
+            get { return primary_text; }
+            set {
+                video_title = value;
+                primary_text = video_title;
+                primary_label.Text = video_title;
+            }
+        }
+
+        public string Uploader {
+            get { return secondary_text; }
+            set {
+                video_uploader = value;
+                secondary_text = video_uploader;
+                secondary_label.Markup = String.Format ("<small>{0} {1}</small>",
+                                                        Catalog.GetString ("Uploaded by"),
+                                                        GLib.Markup.EscapeText (video_uploader));
+            }
+        }
+
+        public Gdk.Pixbuf Pixbuf {
+            get { return image.Pixbuf; }
+            set {
+                if(value == null) {
+                   return;
+                }
+
+                image.Pixbuf = value;
+            }
+        }
+
+        public int RatingValue {
+            get { return rating.Value; }
+            set { rating.Value = value; }
+        }
+
+        public new Menu Menu {
+            get { return menu; }
+            set { menu = value; }
+        }
+
+        public void AppendMenuItem (Widget menu_item)
+        {
+            menu.Append (menu_item);
+        }
+
+        private Menu CreateMenu ()
+        {
+            bool separator = false;
+
+            foreach (Gtk.Action action in action_group.ListActions ()) {
+                AppendMenuItem (action.CreateMenuItem ());
+                if (!separator) {
+                    separator = true;
+                    AppendMenuItem (new SeparatorMenuItem ());
+                }
+            }
+
+            menu.ShowAll ();
+            return menu;
+        }
+
+        private void SetTrackInfo ()
+        {
+            if (track_info == null) {
+                try {
+                    track_info = new TrackInfo ();
+                    track_info.TrackTitle = video_title;
+                    track_info.ArtistName = video_uploader;
+                    track_info.Uri = new SafeUri(BansheePlaybackUri);
+                } catch (Exception e) {
+                    Log.DebugException (e);
+                }
+            }
+        }
+
+        private void OnButtonClicked (object o, EventArgs args)
+        {
+            if (String.IsNullOrEmpty (BansheePlaybackUri)) {
+                Log.Debug ("Banshee supported playback Uri not set");
+                return;
+            }
+
+            SetTrackInfo ();
+
+            if (track_info != null) {
+                ServiceManager.PlayerEngine.OpenPlay (track_info);
+            }
+        }
+
+        private void OnBansheePlaybackAction (object o, EventArgs args)
+        {
+            OnButtonClicked (o, args);
+        }
+
+        private void OnBrowserPlaybackAction (object o, EventArgs args)
+        {
+            if (String.IsNullOrEmpty (BrowserPlaybackUri)) {
+                Log.Debug ("Browser playback Uri not set");
+                return;
+            }
+
+            Browser.Open (BrowserPlaybackUri);
+        }
+
+        private void ConstructTile ()
+        {
+            action_group = new ActionGroup ("VideoStreamTileMenuActionGroup");
+            action_group.Add (new ActionEntry [] {
+                new ActionEntry ("VideoStreamTileBansheePlaybackAction", null,
+                    Catalog.GetString ("Play in Banshee..."), null,
+                    Catalog.GetString ("Play in Banshee..."), OnBansheePlaybackAction),
+
+                new ActionEntry ("VideoStreamTileBrowserPlaybackAction", null,
+                    Catalog.GetString ("Play in Web Browser..."), null,
+                    Catalog.GetString ("Play in Web Browser..."), OnBrowserPlaybackAction),
+            });
+
+            CreateMenu ();
+            Construct (button, menu, true);
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/YouTubeTile.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/YouTubeTile.cs
new file mode 100755
index 0000000..8b99cd7
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.Gui/YouTubeTile.cs
@@ -0,0 +1,145 @@
+//
+// YouTubeTile.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Text.RegularExpressions;
+
+using Mono.Unix;
+
+using Banshee.Base;
+using Banshee.Web;
+using Banshee.Collection;
+using Banshee.ServiceStack;
+
+using Hyena;
+
+using Google.GData.Client;
+using Google.GData.Extensions;
+using Google.GData.YouTube;
+using Google.YouTube;
+
+using Banshee.YouTube.Data;
+using Banshee.YouTube.Gui;
+
+namespace Banshee.YouTube.Gui
+{
+    public class YouTubeTile : VideoStreamTile
+    {
+        public YouTubeTile (Video video_entry)
+        {
+            Info = video_entry;
+        }
+
+        private static string GetTParam (string yt_video_uri)
+        {
+            string t_param;
+            DataFetch df = new DataFetch ();
+            string watch_page_contents = df.GetWatchPageContents (yt_video_uri);
+
+            if (String.IsNullOrEmpty (watch_page_contents)) {
+                return null;
+            }
+
+            Regex regex = new Regex ("'SWF_ARGS'.*\"t\": \"([^\"]+)\"");
+            Match match = regex.Match (watch_page_contents);
+
+            if (!match.Success) {
+                return null;
+            }
+
+            t_param = Regex.Unescape (match.Result ("$1"));
+            if (t_param == null) {
+                t_param = match.Result ("$1");
+            }
+
+            return t_param;
+        }
+
+        private static string GetPlaybackUri (Video yt_video)
+        {
+            int flv = YouTubeQuery.VideoFormat.Embeddable.value__;
+            int mobile = YouTubeQuery.VideoFormat.Mobile.value__;
+            const string format_param = "&fmt=18"; // Assumes user has broadband connection
+            string video_id = yt_video.VideoId;
+            string playback_uri = String.Empty;
+            string flv_url = String.Empty;
+
+            string t_param = GetTParam (yt_video.WatchPage.AbsoluteUri);
+
+            if (String.IsNullOrEmpty (t_param)) {
+                foreach (MediaContent m in yt_video.Media.Contents) {
+                    uint content_format = Convert.ToUInt32 (m.Format);
+
+                    if (content_format == flv) {
+                        flv_url = m.Url;
+                    }
+
+                    // [RTSP] 3gp (MPEG-4 SP Video w/ AAC Audio)
+                    if (content_format == mobile) {
+                        playback_uri = m.Url;
+                        break;
+                    }
+                }
+
+                if (String.IsNullOrEmpty (playback_uri)) {
+                    playback_uri = flv_url;
+                }
+            } else {
+                playback_uri = String.Concat ("http://www.youtube.com/get_video?video_id=", video_id, "&t=", t_param, format_param);
+            }
+
+            return playback_uri;
+        }
+
+        public Video Info {
+            set {
+                BansheePlaybackUri = GetPlaybackUri (value);
+                BrowserPlaybackUri = value.WatchPage.AbsoluteUri;
+                Title = value.Title;
+                Uploader = value.Uploader;
+
+                try {
+                    RatingValue = (int) Math.Round (value.RatingAverage);
+                } catch (Exception e) {
+                    Log.DebugException (e);
+                }
+
+                try {
+                    DataFetch df = new DataFetch ();
+                    string thumbnail = df.DownloadContent (value.Thumbnails[0].Url, CacheDuration.Normal);
+
+                    Pixbuf = new Gdk.Pixbuf (thumbnail);
+                } catch (Exception e) {
+                    Log.DebugException (e);
+                    Pixbuf = Banshee.Gui.IconThemeUtils.LoadIcon ("generic-artist", 48);
+                }
+            }
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.addin.xml b/src/Extensions/Banshee.YouTube/Banshee.YouTube.addin.xml
new file mode 100644
index 0000000..1616507
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.addin.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Addin
+    id="Banshee.YouTube"
+    version="1.0"
+    compatVersion="1.0"
+    copyright="© Kevin Duffus. Licensed under the MIT X11 license."
+    name="YouTube"
+    category="Context Pane"
+    description="Show related YouTube videos in the context pane"
+    author="Kevin Duffus"
+    url="http://banshee-project.org/"
+    defaultEnabled="true">
+
+  <Dependencies>
+    <Addin id="Banshee.Services" version="1.0"/>
+    <Addin id="Banshee.ThickClient" version="1.0"/>
+  </Dependencies>
+
+  <Extension path="/Banshee/ThickClient/ContextPane">
+    <ContextPage class="Banshee.YouTube.ContextPage"/>
+  </Extension>
+
+</Addin>
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube.csproj b/src/Extensions/Banshee.YouTube/Banshee.YouTube.csproj
new file mode 100644
index 0000000..395c543
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube.csproj
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.21022</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{387DC336-45F9-4263-9606-2881907D2105}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AssemblyName>Banshee.YouTube</AssemblyName>
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+    <RootNamespace>Banshee.YouTube</RootNamespace>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>3</WarningLevel>
+    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+    <CustomCommands>
+      <CustomCommands>
+        <Command type="Build" command="make" workingdir="${SolutionDir}" />
+        <Command type="Execute" command="make run" workingdir="${SolutionDir}" />
+      </CustomCommands>
+    </CustomCommands>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>none</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <Compile Include="Banshee.YouTube\ContextPage.cs" />
+    <Compile Include="Banshee.YouTube.Gui\YouTubeTile.cs" />
+    <Compile Include="Banshee.YouTube.Data\DataFetch.cs" />
+    <Compile Include="Banshee.YouTube.Data\YouTubeData.cs" />
+    <Compile Include="Banshee.YouTube\YouTubePane.cs" />
+    <Compile Include="Banshee.YouTube.Gui\VideoStreamTile.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="Banshee.Core, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-core</Package>
+    </Reference>
+    <Reference Include="Banshee.Services, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-services</Package>
+    </Reference>
+    <Reference Include="Banshee.ThickClient, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-thickclient</Package>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="Hyena, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-hyena</Package>
+    </Reference>
+    <Reference Include="ICSharpCode.SharpZipLib" />
+    <Reference Include="Hyena.Gui, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-hyena-gui</Package>
+    </Reference>
+    <Reference Include="Banshee.Widgets, Version=1.5.0.0, Culture=neutral">
+      <Package>banshee-1-thickclient</Package>
+    </Reference>
+    <Reference Include="Google.GData.YouTube, Version=1.4.0.2, Culture=neutral, PublicKeyToken=af04a32718ae8833">
+      <Package>gdata-sharp-youtube</Package>
+    </Reference>
+    <Reference Include="Google.GData.Extensions, Version=1.4.0.2, Culture=neutral, PublicKeyToken=0b4c5df2ebf20876">
+      <Package>gdata-sharp-core</Package>
+    </Reference>
+    <Reference Include="Google.GData.Client, Version=1.4.0.2, Culture=neutral, PublicKeyToken=04a59ca9b0273830">
+      <Package>gdata-sharp-core</Package>
+    </Reference>
+  </ItemGroup>
+  <ProjectExtensions>
+    <MonoDevelop>
+      <Properties>
+        <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am" RelativeConfigureInPath="../../..">
+          <BuildFilesVar Sync="true" Name="SOURCES" />
+          <DeployFilesVar />
+          <ResourcesVar Sync="true" Name="RESOURCES" />
+          <OthersVar Name="LINK" />
+          <GacRefVar />
+          <AsmRefVar />
+          <ProjectRefVar />
+        </MonoDevelop.Autotools.MakefileInfo>
+      </Properties>
+    </MonoDevelop>
+  </ProjectExtensions>
+  <ItemGroup>
+    <EmbeddedResource Include="Banshee.YouTube.addin.xml" />
+  </ItemGroup>
+</Project>
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube/ContextPage.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube/ContextPage.cs
new file mode 100644
index 0000000..7efcbe3
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube/ContextPage.cs
@@ -0,0 +1,66 @@
+//
+// ContextPage.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using Mono.Unix;
+using Gtk;
+
+using Banshee.ContextPane;
+
+using Hyena;
+
+namespace Banshee.YouTube
+{
+    public class ContextPage : BaseContextPage
+    {
+        public ContextPage ()
+        {
+            Id = "YouTube";
+            Name = Catalog.GetString ("YouTube");
+            IconNames = new string[] { "applications-multimedia" };
+        }
+
+        private YouTubePane yt_videos;
+        public override Widget Widget {
+            get { return yt_videos ?? (yt_videos = new YouTubePane (this)); }
+        }
+
+        internal void SetState (ContextState state)
+        {
+            State = state;
+        }
+
+        public override void SetTrack (Banshee.Collection.TrackInfo track)
+        {
+            // Prevent the reloading of the context pane when we play a video
+            if (!track.Uri.AbsoluteUri.Contains ("youtube.com")) {
+                yt_videos.Query = track.TrackTitle + " by " + track.ArtistName;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Extensions/Banshee.YouTube/Banshee.YouTube/YouTubePane.cs b/src/Extensions/Banshee.YouTube/Banshee.YouTube/YouTubePane.cs
new file mode 100644
index 0000000..9a39c57
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Banshee.YouTube/YouTubePane.cs
@@ -0,0 +1,241 @@
+//
+// YouTubePane.cs
+//
+// Author:
+//   Kevin Duffus <KevinDuffus@gmail.com>
+//
+// Copyright (C) 2009 Kevin Duffus
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+using Mono.Unix;
+using Gtk;
+
+using Google.GData.Client;
+using Google.GData.Extensions;
+using Google.GData.YouTube;
+using Google.YouTube;
+
+using Hyena;
+using Hyena.Widgets;
+
+using Banshee.Gui;
+using Banshee.Gui.Widgets;
+using Banshee.Widgets;
+using Banshee.Collection;
+
+using Banshee.YouTube.Data;
+using Banshee.YouTube.Gui;
+
+namespace Banshee.YouTube
+{
+    public class YouTubePane : HBox
+    {
+        private ContextPage yt_context_page;
+        private Gtk.ScrolledWindow results_sw;
+        private TileView results_tv;
+        private Label no_results_label = new Label (Catalog.GetString ("No videos found"));
+        private Hyena.Jobs.Scheduler scheduler = new Hyena.Jobs.Scheduler ();
+        private Hyena.Jobs.Job refresh_videos_jobs;
+
+        private int max_results_display = 12;
+        private bool showing_results = true;
+        private bool ready = false;
+        private bool refreshing = false;
+        private bool show_when_ready = true;
+        private bool ShowWhenReady {
+            get { return show_when_ready; }
+            set {
+                show_when_ready = value;
+                ShowIfReady ();
+
+                if (!show_when_ready) {
+                    CancelTasks ();
+                } else if (!ready && !refreshing) {
+                    RefreshVideos ();
+                }
+            }
+        }
+
+        private void ShowIfReady ()
+        {
+            if (ShowWhenReady && ready) {
+                ShowAll ();
+            }
+        }
+
+        private string query;
+        public string Query {
+            get { return query; }
+            set {
+                if (query == value) {
+                    return;
+                }
+
+                ready = false;
+                query = value;
+
+                if (!String.IsNullOrEmpty (query)) {
+                    RefreshVideos ();
+                }
+            }
+        }
+
+        private void RefreshVideos ()
+        {
+            CancelTasks ();
+
+            if (show_when_ready && !String.IsNullOrEmpty (Query)) {
+                refreshing = true;
+                yt_context_page.SetState (Banshee.ContextPane.ContextState.Loading);
+                refresh_videos_jobs = new RefreshVideosJob (this, Query);
+                scheduler.Add (refresh_videos_jobs);
+            }
+        }
+
+        private void CancelTasks ()
+        {
+            scheduler.Cancel (refresh_videos_jobs);
+            refreshing = false;
+        }
+
+        public void HideWithTimeout ()
+        {
+            GLib.Timeout.Add (200, OnHideTimeout);
+        }
+
+        public bool OnHideTimeout ()
+        {
+            if (!ShowWhenReady || !ready) {
+                Hide ();
+            }
+            return false;
+        }
+
+        public YouTubePane (ContextPage context_page) : base ()
+        {
+            this.yt_context_page = context_page;
+
+            results_tv = new TileView (max_results_display);
+
+            results_sw = new Gtk.ScrolledWindow ();
+            results_sw.SetPolicy (PolicyType.Never, PolicyType.Automatic);
+            results_sw.ShadowType = ShadowType.EtchedIn;
+            results_sw.SizeAllocated += HandleSizeAllocated;
+
+            results_sw.Add (results_tv);
+            results_sw.ShowAll ();
+
+            Add (results_sw);
+            ShowAll ();
+        }
+
+        void HandleSizeAllocated(object o, SizeAllocatedArgs args)
+        {
+            SetSizeRequest (-1, args.Allocation.Height + (Allocation.Height - args.Allocation.Height));
+        }
+
+        protected override void OnStyleSet (Style previous_style)
+        {
+            base.OnStyleSet (previous_style);
+            results_tv.ModifyBg (StateType.Normal, Style.Base (StateType.Normal));
+        }
+
+        private class RefreshVideosJob : Hyena.Jobs.Job
+        {
+            private YouTubePane yt_pane;
+            private string yt_query_val;
+
+            public RefreshVideosJob (YouTubePane pane, string query_val)
+            {
+                this.yt_pane = pane;
+                this.yt_query_val = query_val;
+            }
+
+            protected override void RunJob ()
+            {
+                DataCore yt_data = new DataCore();
+                bool init_request = yt_data.InitYouTubeRequest();
+
+                if (yt_data == null || !init_request) {
+                    return;
+                }
+
+                yt_data.PerformSearch (yt_query_val);
+                yt_pane.UpdateForQuery (yt_data.Videos);
+            }
+        }
+
+        private void UpdateForQuery (Feed<Video> video_feed)
+        {
+            int result_display_count = 0;
+            List<YouTubeTile> tiles = new List<YouTubeTile> ();
+
+            results_tv.ClearWidgets ();
+
+            ThreadAssist.Spawn (delegate {
+                if (video_feed.TotalResults > 0) {
+                    if (!showing_results) {
+                        Remove (no_results_label);
+                        Add (results_sw);
+                        ShowAll ();
+                    }
+
+                    foreach (Video entry in video_feed.Entries) {
+                        // Don't include videos that are not live
+                        if (entry.IsDraft) {
+                            continue;
+                        } else if (result_display_count++ < max_results_display) {
+                            tiles.Add (new YouTubeTile (entry));
+                        }
+                    }
+
+                    results_tv.ShowAll ();
+                    showing_results = true;
+                } else {
+                    Log.Debug ("YouTube: No videos found");
+                    if (showing_results) {
+                        Remove (results_sw);
+                        Add (no_results_label);
+                        showing_results = false;
+                        ShowAll ();
+                    }
+                }
+
+                if (showing_results) {
+                    ThreadAssist.ProxyToMain (delegate {
+                        foreach (YouTubeTile tile in tiles) {
+                            results_tv.AddWidget (tile);
+                        }
+                    });
+                }
+
+                ready = true;
+                refreshing = false;
+                yt_context_page.SetState (Banshee.ContextPane.ContextState.Loaded);
+            });
+        }
+    }
+}
diff --git a/src/Extensions/Banshee.YouTube/Makefile.am b/src/Extensions/Banshee.YouTube/Makefile.am
new file mode 100644
index 0000000..86731b4
--- /dev/null
+++ b/src/Extensions/Banshee.YouTube/Makefile.am
@@ -0,0 +1,19 @@
+ASSEMBLY = Banshee.YouTube
+TARGET = library
+LINK = $(LINK_BANSHEE_THICKCLIENT_DEPS) $(LINK_GDATA)
+
+SOURCES =  \
+	Banshee.YouTube.Data/DataFetch.cs \
+	Banshee.YouTube.Data/YouTubeData.cs \
+	Banshee.YouTube.Gui/VideoStreamTile.cs \
+	Banshee.YouTube.Gui/YouTubeTile.cs \
+	Banshee.YouTube/ContextPage.cs \
+	Banshee.YouTube/YouTubePane.cs
+
+RESOURCES = Banshee.YouTube.addin.xml
+
+if HAVE_GDATA
+include $(top_srcdir)/build/build.mk
+else
+EXTRA_DIST = $(SOURCES) $(RESOURCES)
+endif
diff --git a/src/Extensions/Makefile.am b/src/Extensions/Makefile.am
index bec0c11..1963418 100644
--- a/src/Extensions/Makefile.am
+++ b/src/Extensions/Makefile.am
@@ -24,7 +24,8 @@ SUBDIRS = \
 	Banshee.SqlDebugConsole \
 	Banshee.Torrent \
 	Banshee.RemoteAudio \
-	Banshee.Wikipedia
+	Banshee.Wikipedia \
+	Banshee.YouTube
 
 
 MAINTAINERCLEANFILES = Makefile.in
