diff --git a/WeatubeDL/MainWindow.xaml b/WeatubeDL/MainWindow.xaml index 424dc70..161cc1c 100644 --- a/WeatubeDL/MainWindow.xaml +++ b/WeatubeDL/MainWindow.xaml @@ -7,28 +7,81 @@ mc:Ignorable="d" Title="Weatube DL" Height="450" Width="810"> - - + + - + + + + + + + + + + + - - - - + + + + - - + - + + + + + + + + + + + diff --git a/WeatubeDL/MainWindow.xaml.cs b/WeatubeDL/MainWindow.xaml.cs index 1d28785..a1a901e 100644 --- a/WeatubeDL/MainWindow.xaml.cs +++ b/WeatubeDL/MainWindow.xaml.cs @@ -19,12 +19,13 @@ using System.Windows.Media.Animation; namespace WeatubeDL { public partial class MainWindow : Window - { - private YoutubeVideo[] videos; + { + private Dictionary AddedVids; private YoutubeVideo current; public MainWindow() { InitializeComponent(); + AddedVids = new Dictionary(); } private async void Link_TextChanged(object sender, TextChangedEventArgs e) @@ -43,7 +44,7 @@ namespace WeatubeDL return; } var text = (sender as TextBox).Text; - if (current != null && current.id == YoutubeVideo.IdFromUrl(text)) + if (current != null && current.Id == YoutubeVideo.IdFromUrl(text)) { EnableSuggestion(); return; @@ -52,28 +53,76 @@ namespace WeatubeDL if (await vid.Exists()) { current = vid; - while (current.name == null) await Task.Delay(500); + while (current.Name == null) await Task.Delay(500); EnableSuggestion(); } else DisableSuggestion(); } + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + Application.Current.Shutdown(); + } + + private void AddLink_Click(object sender, RoutedEventArgs e) + { + if (current == null) return; + DockPanel _s = new DockPanel + { + Tag = AddedVids.Count, + Style = (Style)FindResource("VideoPanel") + }; + _s.Children.Add(new Image + { + Source = current.Image, + Style = (Style)FindResource("VideoImage") + }); + _s.Children.Add(new TextBlock + { + Text = current.Name, + VerticalAlignment = VerticalAlignment.Center, + Style = (Style)FindResource("VideoText") + }); + var button = new Button + { + Style = (Style)FindResource("VideoButtonClose") + }; + button.Click += new RoutedEventHandler(CloseButtonClick); + _s.Children.Add(button); + VideosPanel.Children.Add(_s); + ShowPanel(_s); + AddedVids.Add(_s, current); + Link.Text = ""; + current = null; + } + + private async void CloseButtonClick(object sender, RoutedEventArgs e) + { + var p = (sender as Button).Parent; + HidePanel((DockPanel)p); + while ((p as DockPanel).Margin.Left != -((p as DockPanel).Width*2)) await Task.Delay(100); + AddedVids.Remove((DockPanel)p); + VideosPanel.Children.Remove((DockPanel)p); + } + + #region WPF animations public void EnableSuggestion() { AddLink.IsEnabled = true; - if (SuggestedVideo.Margin.Top < 0) + if (SuggestedVideo.Margin.Top == 0) { Storyboard sb = Resources["ShowSuggested"] as Storyboard; sb.Begin(SuggestedVideo); } - SuggestedItem.Text = current.name; - SuggestedImage.Source = current.image; + SuggestedItem.Text = current.Name; + SuggestedImage.Source = current.Image; } public void DisableSuggestion() { AddLink.IsEnabled = false; - if (SuggestedVideo.Margin.Top == 0) + if (SuggestedVideo.Margin.Top > 0) { Storyboard sb = Resources["HideSuggested"] as Storyboard; sb.Begin(SuggestedVideo); @@ -81,10 +130,18 @@ namespace WeatubeDL SuggestedItem.Text = null; SuggestedImage.Source = null; } - protected override void OnClosed(EventArgs e) + + public void ShowPanel(DockPanel panel) { - base.OnClosed(e); - Application.Current.Shutdown(); + Storyboard sb = Resources["ShowPanel"] as Storyboard; + sb.Begin(panel); } + + public void HidePanel(DockPanel panel) + { + Storyboard sb = Resources["HidePanel"] as Storyboard; + sb.Begin(panel); + } + #endregion } } diff --git a/WeatubeDL/Properties/YoutubeVideo.cs b/WeatubeDL/Properties/YoutubeVideo.cs index 88e25d2..ae13248 100644 --- a/WeatubeDL/Properties/YoutubeVideo.cs +++ b/WeatubeDL/Properties/YoutubeVideo.cs @@ -14,73 +14,97 @@ namespace WeatubeDL { class YoutubeVideo { - public string src { get; private set; } - public string id { get; private set; } - public string[] types { get; private set; } - public string imagePath { get; private set; } - public BitmapImage image { get; private set; } - public string name { get; private set; } + public string Src { get; private set; } // original link provided by user + public string Id { get; private set; } // YouTube video id + public string[] Types { get; private set; } // All download types available + public string ImagePath { get; private set; } // Path to image stored in user's temp folder + public BitmapImage Image { get; private set; } // YouTube video preview image + public string Name { get; private set; } // YouTube video name private WebClient client; + + /// + /// Creates YouTube video object + /// public YoutubeVideo(string src) { - this.src = src; - DownloadPreview(); + this.Src = src; + DownloadPreview(); } + + /// + /// Destroys YouTube video object + /// ~YoutubeVideo() { - if (File.Exists(imagePath)) File.Delete(imagePath); + if (File.Exists(ImagePath)) File.Delete(ImagePath); if (client != null) client.Dispose(); } + /// + /// Takes video id from given url + /// + /// YouTube video Id string / null public static string IdFromUrl(string src) { if (src.Contains("youtu.be/")) return src.Replace("/youtu.be/", "").Split('/')[1].Split('&')[0]; else if (src.Contains("youtube.com/watch?v=") && src.Contains("&")) return src.Split('=')[1].Split('&')[0]; - return " "; + return null; } + /// + /// Downloads YouTube video preview image + /// private void DownloadPreview() { - id = IdFromUrl(src); - if (id == null || id == " ") return; + Id = IdFromUrl(Src); + if (Id == null) return; client = new WebClient(); client.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0)"); client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); - imagePath = Path.GetTempPath() + "WeatubeDL/" + id + ".png"; + ImagePath = Path.GetTempPath() + "WeatubeDL/" + Id + ".png"; //path to /%LOCALAPPDATA%/Temp/WeatubeDL/image.png Directory.CreateDirectory(Path.GetTempPath() + "WeatubeDL"); - client.DownloadFileAsync(new Uri("http://img.youtube.com/vi/" + id + "/mqdefault.jpg"), imagePath); + client.DownloadFileAsync(new Uri("http://img.youtube.com/vi/" + Id + "/mqdefault.jpg"), ImagePath); } + /// + /// Disposes WebClient when download is complete + /// static void Completed(object sender, AsyncCompletedEventArgs e) { (sender as WebClient).Dispose(); } + + /// + /// Checks if given video exists on YouTube + /// + /// true = video exists ; false = video does not exist public async Task Exists() { - if (id == null || client == null) return false; - while(client.IsBusy) - { - await Task.Delay(100); - } - if (id.Length > 2 && File.Exists(imagePath) && new FileInfo(imagePath).Length > 0) - { - image = new BitmapImage(); - - using (var stream = File.OpenRead(imagePath)) + if (Id == null) return false; + if (client != null) + while(client.IsBusy) //wait until preview is downloaded { - image.BeginInit(); - image.CacheOption = BitmapCacheOption.OnLoad; - image.StreamSource = stream; - image.EndInit(); + await Task.Delay(100); + } + if (File.Exists(ImagePath) && new FileInfo(ImagePath).Length > 0) //checks whether preview is downloaded or not + { + Image = new BitmapImage(); + + using (var stream = File.OpenRead(ImagePath)) //load image + { + Image.BeginInit(); + Image.CacheOption = BitmapCacheOption.OnLoad; + Image.StreamSource = stream; + Image.EndInit(); } - if (image.Width == 120) + if (Image.Width == 120) //if video not found, mqdefault.png would be 120px wide instead of default 320px { - File.Delete(imagePath); + File.Delete(ImagePath); return false; } GetVideoName(); @@ -89,19 +113,24 @@ namespace WeatubeDL return false; } - // TODO: После того как прога понимает, что видос существует, она должна подтягивать его форматы + /// + /// Downloads webpage from url given by user + /// private void GetVideoName() { client = new WebClient(); client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted); - client.DownloadStringAsync(new Uri(src)); + client.DownloadStringAsync(new Uri(Src)); } + /// + /// Gets YouTube video name from webpage title when page is loaded + /// void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { - name = Regex.Match(e.Result, @"\]*\>\s*(?[\s\S]*?)\</title\>", + Name = Regex.Match(e.Result, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value; - name = name.Remove(name.Length - 10); + Name = Name.Remove(Name.Length - 10); (sender as WebClient).Dispose(); }