PageRenderTime 20ms CodeModel.GetById 1ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 1ms

/Application/Core/YouTubeManager.cs

http://yet-another-music-application.googlecode.com/
C# | 391 lines | 226 code | 51 blank | 114 comment | 20 complexity | 6216d6651ab50769c4d206d01b32473e MD5 | raw file
  1/**
  2 * YouTubeManager.cs
  3 * 
  4 * Takes care of searching and finding music on YouTube
  5 * as well as converting results into TrackData structures.
  6 * 
  7 * * * * * * * * *
  8 * 
  9 * Copyright 2011 Simplare
 10 * 
 11 * This code is part of the Stoffi Music Player Project.
 12 * Visit our website at: stoffiplayer.com
 13 *
 14 * This program is free software; you can redistribute it and/or
 15 * modify it under the terms of the GNU General Public License
 16 * as published by the Free Software Foundation; either version
 17 * 3 of the License, or (at your option) any later version.
 18 * 
 19 * See stoffiplayer.com/license for more information.
 20 **/
 21
 22using System;
 23using System.Collections.Generic;
 24using System.Collections.ObjectModel;
 25using System.Linq;
 26using System.Runtime.InteropServices;
 27using System.Text;
 28
 29using Google.GData.Client;
 30using Google.GData.Extensions;
 31using Google.GData.YouTube;
 32using Google.GData.Extensions.MediaRss;
 33using Google.YouTube;
 34
 35namespace Stoffi
 36{
 37	/// <summary>
 38	/// Represents a manager that takes care of talking to YouTube
 39	/// </summary>
 40	public static class YouTubeManager
 41	{
 42		#region Members
 43		private static YouTubeRequestSettings settings = new YouTubeRequestSettings("Stoffi", "AI39si4y_vkAW2Ngyc2BlMdgkBghua2w5hheyesEI-saNU_CNDIMs5YMPpIBk-HpmFG4qDPAHAvE_YYNWH5qV5S1x5euKKRodw");
 44		#endregion
 45
 46		#region Properties
 47		/// <summary>
 48		/// Gets or sets whether the user has Adobe Flash installed or not
 49		/// </summary>
 50		public static bool HasFlash { get; set; }
 51
 52		/// <summary>
 53		/// Gets the current source of tracks used as ItemsSource for the YouTube track list.
 54		/// </summary>
 55		public static ObservableCollection<TrackData> TrackSource { get; private set; }
 56		#endregion
 57
 58		#region Methods
 59
 60		#region Public
 61
 62		/// <summary>
 63		/// Returns a list of tracks from one of the YouTube feeds
 64		/// </summary>
 65		/// <param name="feed">The feed</param>
 66		/// <returns>An observable collection of TrackData that represents the most viewed YouTube tracks</returns>
 67		public static ObservableCollection<TrackData> TopFeed(string feed)
 68		{
 69			ObservableCollection<TrackData> tracks = new ObservableCollection<TrackData>();
 70			YouTubeRequest request = new YouTubeRequest(settings);
 71
 72			int maxFeedItems = 50;
 73
 74			int i = 1;
 75			Feed<Video> videoFeed = request.Get<Video>(new Uri("http://gdata.youtube.com/feeds/api/standardfeeds/" + feed + "?format=5"));
 76			foreach (Video entry in videoFeed.Entries)
 77			{
 78				if (i++ > maxFeedItems) break;
 79				tracks.Add(CreateTrack(entry));
 80			}
 81
 82			TrackSource = tracks;
 83
 84			return tracks;
 85		}
 86
 87		/// <summary>
 88		/// Searches YouTube for tracks matching a certain query
 89		/// </summary>
 90		/// <param name="query">The query to search for</param>
 91		/// <returns>An observable collection of TrackData with all YouTube tracks that match query</returns>
 92		public static ObservableCollection<TrackData> Search(string query)
 93		{
 94			ObservableCollection<TrackData> tracks = new ObservableCollection<TrackData>();
 95
 96			YouTubeQuery q = new YouTubeQuery(YouTubeQuery.DefaultVideoUri);
 97			q.OrderBy = "relevance";
 98			q.Query = query;
 99			q.Formats.Add(YouTubeQuery.VideoFormat.Embeddable);
100			q.NumberToRetrieve = 50;
101			q.SafeSearch = YouTubeQuery.SafeSearchValues.None;
102
103			YouTubeRequest request = new YouTubeRequest(settings);
104
105			Feed<Video> videoFeed = request.Get<Video>(q);
106			foreach (Video entry in videoFeed.Entries)
107			{
108				tracks.Add(CreateTrack(entry));
109			}
110
111			TrackSource = tracks;
112
113			return tracks;
114		}
115
116		/// <summary>
117		/// Retrieves the URL to the thumbnail for a YouTube track
118		/// </summary>
119		/// <param name="track">The YouTube track</param>
120		public static string GetThumbnail(TrackData track)
121		{
122			if (IsYouTube(track))
123				return "https://img.youtube.com/vi/" + GetYouTubeID(track.Path) + "/1.jpg";
124			else
125				return "";
126		}
127
128		/// <summary>
129		/// Retrieves the URL for a YouTube track
130		/// </summary>
131		/// <param name="track">The YouTube track</param>
132		public static string GetURL(TrackData track)
133		{
134			if (IsYouTube(track))
135				return "https://www.youtube.com/watch?v=" + GetYouTubeID(track.Path);
136			else
137				return "";
138		}
139
140		/// <summary>
141		/// Creates a track using a YouTube video ID
142		/// </summary>
143		/// <param name="id">The video ID</param>
144		/// <returns>A TrackData structure representing the YouTube track</returns>
145		public static TrackData CreateTrack(string id)
146		{
147			YouTubeRequest request = new YouTubeRequest(settings);
148			Uri url = new Uri("http://gdata.youtube.com/feeds/api/videos/" + id);
149			Video v = request.Retrieve<Video>(url);
150			if (v == null)
151			{
152				U.L(LogLevel.Warning, "YOUTUBE", "Could not find video with ID '" + id + "'");
153				return null;
154			}
155			return CreateTrack(v);
156		}
157
158		/// <summary>
159		/// Creates a track using a YouTube video entry
160		/// </summary>
161		/// <param name="v">The video entry</param>
162		/// <returns>A TrackData structure representing the YouTube track</returns>
163		public static TrackData CreateTrack(Video v)
164		{
165			TrackData track = new TrackData();
166			track.Path = "youtube://" + v.VideoId;
167			track.Icon = "pack://application:,,,/Platform/Windows/GUI/Images/Icons/YouTube.ico";
168			track.Bookmarks = new List<double>();
169			track.Processed = true;
170			int len = Convert.ToInt32(v.Media.Duration.Seconds);
171			track.Length = U.TimeSpanToString(new TimeSpan(0, 0, len));
172			string[] str = splitTitle(v.Title);
173			track.Artist = str[0];
174			track.Title = str[1];
175			track.RawViews = v.ViewCount;
176			track.RawLength = len;
177			return track;
178		}
179
180		/// <summary>
181		/// Checks whether a given track is a youtube track
182		/// </summary>
183		/// <param name="t">The track to check</param>
184		/// <returns>True if the track is a youtube track</returns>
185		public static bool IsYouTube(TrackData t)
186		{
187			return (t != null && IsYouTube(t.Path));
188		}
189
190		/// <summary>
191		/// Checks whether a given track path corresponds to a youtube track
192		/// </summary>
193		/// <param name="path">The path of the track to check</param>
194		/// <returns>True if the track is a youtube track</returns>
195		public static bool IsYouTube(string path)
196		{
197			return path.StartsWith("youtube://");
198		}
199
200		/// <summary>
201		/// Extracts the video ID of a YouTube track's path
202		/// </summary>
203		/// <param name="path">The path of the track</param>
204		/// <returns>The video ID</returns>
205		public static string GetYouTubeID(string path)
206		{
207			if (IsYouTube(path))
208				return path.Substring(10);
209
210			throw new Exception("Trying to extract YouTube video ID from non-YouTube track: " + path);
211		}
212
213		#endregion
214
215		#region Private
216
217		/// <summary>
218		/// Separates a title by a list of separators
219		/// and identifies artist and title.
220		/// </summary>
221		/// <param name="title">The title to split</param>
222		/// <returns>An array holding artist and title</returns>
223		private static string[] splitTitle(string title)
224		{
225			foreach (string sep in new[] { "-", ":" })
226			{
227				string[] variants = new[]
228				{
229					" " + sep + " ",
230					" " + sep,
231					sep + " "
232				};
233				foreach (string var in variants)
234				{
235					if (title.Contains(var))
236					{
237						string[] str = title.Split(new[] { var }, 2, StringSplitOptions.None);
238
239						string[] prefixes = new[]
240						{
241							"by ",
242							"ft ",
243							"ft.",
244							"feat ",
245							"feat."
246						};
247
248						foreach (string pref in prefixes)
249						{
250							if (str[0].ToLower().StartsWith(pref))
251								return new[] { str[0].Substring(pref.Length), str[1] };
252							else if (str[1].ToLower().StartsWith(pref))
253								return new[] { str[1].Substring(pref.Length), str[0] };
254						}
255						return new[] { str[0], str[1] };
256					}
257				}
258			}
259			return new[] { "", title };
260		}
261
262		#endregion
263
264		#endregion
265	}
266
267	/// <summary>
268	/// Describes the interface that the chromeless YouTube player can call via JavaScript
269	/// </summary>
270	[ComVisibleAttribute(true)]
271	public class YouTubePlayerInterface
272	{
273		/// <summary>
274		/// Invoked when an error occurs within the YouTube player
275		/// </summary>
276		/// <param name="errorCode">The error code</param>
277		public void OnVideoError(int errorCode)
278		{
279			switch (errorCode)
280			{
281				case 2:
282					U.L(LogLevel.Error, "YOUTUBE", "Player reported that we used bad parameters");
283					break;
284
285				case 100:
286					U.L(LogLevel.Error, "YOUTUBE", "Player reported that the track has either been removed or marked as private");
287					break;
288
289				case 101:
290				case 150:
291					U.L(LogLevel.Error, "YOUTUBE", "Player reported that the track is restricted");
292					break;
293
294				default:
295					U.L(LogLevel.Error, "YOUTUBE", "Player reported an unknown error code: " + errorCode);
296					break;
297			}
298			DispatchError(errorCode.ToString());
299		}
300
301		/// <summary>
302		/// Invoked when user tries to play a youtube track but doesn't have flash installed
303		/// </summary>
304		public void OnNoFlash()
305		{
306			DispatchNoFlashDetected();
307		}
308
309		/// <summary>
310		/// Invoked when the player changes state
311		/// </summary>
312		/// <param name="state">The new state of the player</param>
313		public void OnStateChanged(int state)
314		{
315			switch (state)
316			{
317				case -1: // unstarted
318					break;
319
320				case 0: // ended
321					SettingsManager.MediaState = MediaState.Ended;
322					break;
323
324				case 1: // playing
325					SettingsManager.MediaState = MediaState.Playing;
326					break;
327
328				case 2: // paused
329					SettingsManager.MediaState = MediaState.Paused;
330					break;
331
332				case 3: // buffering
333					break;
334
335				case 5: // cued
336					break;
337			}
338		}
339
340		/// <summary>
341		/// Invoked when player is ready
342		/// </summary>
343		public void OnPlayerReady()
344		{
345			DispatchPlayerReady();
346		}
347
348		/// <summary>
349		/// Dispatches the ErrorOccured event
350		/// </summary>
351		/// <param name="message">The error message</param>
352		private void DispatchError(string message)
353		{
354			if (ErrorOccured != null)
355				ErrorOccured(this, message);
356		}
357
358		/// <summary>
359		/// Dispatches the NoFlashDetected event
360		/// </summary>
361		private void DispatchNoFlashDetected()
362		{
363			if (NoFlashDetected != null)
364				NoFlashDetected(this, new EventArgs());
365		}
366
367		/// <summary>
368		/// Dispatches the PlayerReady event
369		/// </summary>
370		private void DispatchPlayerReady()
371		{
372			if (PlayerReady != null)
373				PlayerReady(this, new EventArgs());
374		}
375
376		/// <summary>
377		/// Occurs when there's an error from the player
378		/// </summary>
379		public event ErrorEventHandler ErrorOccured;
380
381		/// <summary>
382		/// Occurs when the user tries to play a youtube track but there's no flash installed
383		/// </summary>
384		public event EventHandler NoFlashDetected;
385
386		/// <summary>
387		/// Occurs when the player is ready
388		/// </summary>
389		public event EventHandler PlayerReady;
390	}
391}