Commit 079af2ac authored by Alexander Kojevnikov's avatar Alexander Kojevnikov

Merge branch 'youtube'

parents 8aabc6bd cb4c519a
......@@ -120,6 +120,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Emusic", "src\Exten
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.Audiobook", "src\Extensions\Banshee.Audiobook\Banshee.Audiobook.csproj", "{05148D80-6C5C-4BE5-9BD7-89613753D027}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Banshee.YouTube", "src\Extensions\Banshee.YouTube\Banshee.YouTube.csproj", "{387DC336-45F9-4263-9606-2881907D2105}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
......@@ -208,6 +210,12 @@ Global
{379034DF-F34D-409B-8DE9-097BE78E37C8}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{379034DF-F34D-409B-8DE9-097BE78E37C8}.Release|Any CPU.Build.0 = Debug|Any CPU
{379034DF-F34D-409B-8DE9-097BE78E37C8}.Windows|Any CPU.ActiveCfg = Windows|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Debug|Any CPU.Build.0 = Debug|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Release|Any CPU.ActiveCfg = Release|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Release|Any CPU.Build.0 = Release|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Windows|Any CPU.ActiveCfg = Debug|Any CPU
{387DC336-45F9-4263-9606-2881907D2105}.Windows|Any CPU.Build.0 = Debug|Any CPU
{3935AE8A-E283-4C0D-9094-7435A937DC90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3935AE8A-E283-4C0D-9094-7435A937DC90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3935AE8A-E283-4C0D-9094-7435A937DC90}.Release|Any CPU.ActiveCfg = Debug|Any CPU
......@@ -426,6 +434,7 @@ Global
{9A5328D7-B7FB-4966-BF03-A4BA541541F5} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{10A5B2EE-C9F0-4B7B-B79B-87B7DA9C1DC1} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{05148D80-6C5C-4BE5-9BD7-89613753D027} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{387DC336-45F9-4263-9606-2881907D2105} = {4DD1DE63-F20B-4FC3-8FDA-F0BDF4183722}
{95374549-9553-4C1E-9D89-667755F90E12} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
{C856EFD8-E812-4E61-8B76-E3583D94C233} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
{C1F63FC5-4B96-48B2-B7F7-5B33FCC4F2A2} = {4F47D6F1-4047-4A89-AE85-3AE5EF9F2961}
......
......@@ -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)
......
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
])
......@@ -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
......
......@@ -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
}
......
//
// 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 {
using (var response = (HttpWebResponse) request.GetResponse ()) {
using (var stream = response.GetResponseStream ()) {
using (var 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;
}
}
}
}
//
// 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
//
// 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 video_title;
private string video_uploader;
private string primary_text;
private string secondary_text;
public VideoStreamTile ()
{
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; set; }
public string BrowserPlaybackUri { get; set; }
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 () {
TrackTitle = video_title,
ArtistName = video_uploader,
AlbumTitle = "YouTube",
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);
}
}
}
//
// YouTubeTile.cs
//
// Authors:
// Kevin Duffus <KevinDuffus@gmail.com>
// Alexander Kojevnikov <alexander@kojevnikov.com>
//
// Copyright (C) 2009 Kevin Duffus
// Copyright (C) 2010 Alexander Kojevnikov
//
// 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 (YouTubeTileData data)
{
BansheePlaybackUri = data.BansheePlaybackUri;
BrowserPlaybackUri = data.BrowserPlaybackUri;
Title = data.Title;
Uploader = data.Uploader;
RatingValue = data.RatingValue;
if (data.Thumbnail != null) {
Pixbuf = new Gdk.Pixbuf (data.Thumbnail);
} else {
Pixbuf = Banshee.Gui.IconThemeUtils.LoadIcon ("generic-artist", 48);
}
}
}
public class YouTubeTileData
{
public string BansheePlaybackUri { get; private set; }
public string BrowserPlaybackUri { get; private set; }
public string Title { get; private set; }
public string Uploader { get; private set; }
public int RatingValue { get; private set; }
public string Thumbnail { get; private set; }
public YouTubeTileData (Video video)
{
BansheePlaybackUri = GetPlaybackUri (video);
BrowserPlaybackUri = video.WatchPage.AbsoluteUri;
Title = video.Title;
Uploader = video.Uploader;
try {
RatingValue = (int) Math.Round (video.RatingAverage);
} catch (Exception e) {
Log.DebugException (e);
}
try {
DataFetch df = new DataFetch ();
Thumbnail = df.DownloadContent (video.Thumbnails[0].Url, CacheDuration.Normal);
} catch (Exception e) {
Log.DebugException (e);
Thumbnail = null;
}
}
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