My son came home as I was taking his door off its hinges and asked: "Dad what are you doing?" I said, "We've updated our privacy policy."
This commit is contained in:
+63
-10
@@ -7,28 +7,81 @@
|
|||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Weatube DL" Height="450" Width="810">
|
Title="Weatube DL" Height="450" Width="810">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<Storyboard x:Key="ShowSuggested">
|
<Storyboard x:Key="ShowSuggested">
|
||||||
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,-50,0,0" To="0,0,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,0,0" To="0,50,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
||||||
</Storyboard>
|
</Storyboard>
|
||||||
<Storyboard x:Key="HideSuggested">
|
<Storyboard x:Key="HideSuggested">
|
||||||
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,0,0" To="0,-50,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,50,0,0" To="0,0,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
||||||
</Storyboard>
|
</Storyboard>
|
||||||
|
<Storyboard x:Key="ShowPanel">
|
||||||
|
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="-1500,0,0,0" To="0,0,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
||||||
|
</Storyboard>
|
||||||
|
<Storyboard x:Key="HidePanel">
|
||||||
|
<ThicknessAnimation Storyboard.TargetProperty="Margin" From="0,0,0,0" To="-1500,0,0,0" DecelerationRatio="0.9" Duration="0:0:0.3"/>
|
||||||
|
</Storyboard>
|
||||||
|
<Style x:Key="VideoPanel" TargetType="DockPanel">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="Background" Value="#33606E"/>
|
||||||
|
<Setter Property="Height" Value="40" />
|
||||||
|
<Setter Property="Width" Value="750"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="VideoText" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="16"/>
|
||||||
|
<Setter Property="Foreground" Value="#B9CECD"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="VideoImage" TargetType="Image">
|
||||||
|
<Setter Property="Width" Value="70"/>
|
||||||
|
<Setter Property="Height" Value="40"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
<Setter Property="Stretch" Value="Fill"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,10,0"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="VideoButtonClose" TargetType="Button">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||||
|
<Setter Property="Width" Value="25"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
<Setter Property="Content" Value="x"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,5,0"/>
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Top"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Foreground" Value="#B9CECD"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Foreground" Value="#1f2433"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
<Border HorizontalAlignment="Center" Background="#2F384E">
|
<Border HorizontalAlignment="Center" Background="#2F384E" Height="420">
|
||||||
<StackPanel x:Name="MainPanel">
|
<Grid x:Name="MainPanel" Height="420">
|
||||||
<StackPanel Height="20" Background="#2F384E"></StackPanel>
|
<StackPanel Height="20" Background="#2F384E" VerticalAlignment="Top"></StackPanel>
|
||||||
<DockPanel Height="30">
|
<DockPanel Height="30" VerticalAlignment="Top" Margin="0,20,0,0">
|
||||||
<TextBox x:Name="Link" Width="600" TextChanged="Link_TextChanged" Background="#33606E" Foreground="#B9CECD" VerticalContentAlignment="Center"></TextBox>
|
<TextBox x:Name="Link" Width="600" TextChanged="Link_TextChanged" Background="#33606E" Foreground="#B9CECD" VerticalContentAlignment="Center"></TextBox>
|
||||||
<ComboBox Name="VideoType" Width="100" Background="#C1A398" Foreground="#2F384E">
|
<ComboBox Name="VideoType" Width="100" Background="#C1A398" Foreground="#2F384E">
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
<Button x:Name="AddLink" Width="100" HorizontalAlignment="Right" IsEnabled="False" Background="#C1A398" HorizontalContentAlignment="Center">
|
<Button x:Name="AddLink" Width="100" HorizontalAlignment="Right" IsEnabled="False" Background="#C1A398" HorizontalContentAlignment="Center" Click="AddLink_Click">
|
||||||
<TextBlock Foreground="#2F384E" HorizontalAlignment="Center" TextAlignment="Center">Add to queue</TextBlock>
|
<TextBlock Foreground="#2F384E" HorizontalAlignment="Center" TextAlignment="Center">Add to queue</TextBlock>
|
||||||
</Button>
|
</Button>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
<StackPanel x:Name="SuggestedVideo" Orientation="Horizontal" Visibility="Visible" Background="#1f2433" Panel.ZIndex="-1" Margin="0,-50,0,0">
|
<StackPanel x:Name="SuggestedVideo" Orientation="Horizontal" Visibility="Visible" Background="#1f2433" Panel.ZIndex="-1" Margin="0,0,0,0" VerticalAlignment="Top">
|
||||||
<Image Stretch="Fill" Name="SuggestedImage" Height="40" Width="70" HorizontalAlignment="Left" Margin="0, 0, 10, 0"/>
|
<Image Stretch="Fill" Name="SuggestedImage" Height="40" Width="70" HorizontalAlignment="Left" Margin="0, 0, 10, 0"/>
|
||||||
<TextBlock x:Name="SuggestedItem" FontSize="16" Foreground="#B9CECD" VerticalAlignment="Center"></TextBlock>
|
<TextBlock x:Name="SuggestedItem" FontSize="16" Foreground="#B9CECD" VerticalAlignment="Center"></TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
<ScrollViewer Margin="5,0,5,5" Height="300" VerticalAlignment="Bottom" BorderThickness="0">
|
||||||
|
|
||||||
|
<StackPanel Panel.ZIndex="0" x:Name="VideosPanel">
|
||||||
|
<DockPanel Style="{DynamicResource VideoPanel}" Margin="0, 0, 0,0" Visibility="Collapsed">
|
||||||
|
<Image Style="{DynamicResource VideoImage}"/>
|
||||||
|
<TextBlock Text="Greatest hits of Johnny Silverhand 2013" Style="{DynamicResource VideoText}"/>
|
||||||
|
<Button Style="{DynamicResource VideoButtonClose}"></Button>
|
||||||
|
</DockPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ namespace WeatubeDL
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
private YoutubeVideo[] videos;
|
private Dictionary<DockPanel, YoutubeVideo> AddedVids;
|
||||||
private YoutubeVideo current;
|
private YoutubeVideo current;
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
AddedVids = new Dictionary<DockPanel, YoutubeVideo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Link_TextChanged(object sender, TextChangedEventArgs e)
|
private async void Link_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
@@ -43,7 +44,7 @@ namespace WeatubeDL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var text = (sender as TextBox).Text;
|
var text = (sender as TextBox).Text;
|
||||||
if (current != null && current.id == YoutubeVideo.IdFromUrl(text))
|
if (current != null && current.Id == YoutubeVideo.IdFromUrl(text))
|
||||||
{
|
{
|
||||||
EnableSuggestion();
|
EnableSuggestion();
|
||||||
return;
|
return;
|
||||||
@@ -52,28 +53,76 @@ namespace WeatubeDL
|
|||||||
if (await vid.Exists())
|
if (await vid.Exists())
|
||||||
{
|
{
|
||||||
current = vid;
|
current = vid;
|
||||||
while (current.name == null) await Task.Delay(500);
|
while (current.Name == null) await Task.Delay(500);
|
||||||
EnableSuggestion();
|
EnableSuggestion();
|
||||||
}
|
}
|
||||||
else DisableSuggestion();
|
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()
|
public void EnableSuggestion()
|
||||||
{
|
{
|
||||||
AddLink.IsEnabled = true;
|
AddLink.IsEnabled = true;
|
||||||
if (SuggestedVideo.Margin.Top < 0)
|
if (SuggestedVideo.Margin.Top == 0)
|
||||||
{
|
{
|
||||||
Storyboard sb = Resources["ShowSuggested"] as Storyboard;
|
Storyboard sb = Resources["ShowSuggested"] as Storyboard;
|
||||||
sb.Begin(SuggestedVideo);
|
sb.Begin(SuggestedVideo);
|
||||||
}
|
}
|
||||||
SuggestedItem.Text = current.name;
|
SuggestedItem.Text = current.Name;
|
||||||
SuggestedImage.Source = current.image;
|
SuggestedImage.Source = current.Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisableSuggestion()
|
public void DisableSuggestion()
|
||||||
{
|
{
|
||||||
AddLink.IsEnabled = false;
|
AddLink.IsEnabled = false;
|
||||||
if (SuggestedVideo.Margin.Top == 0)
|
if (SuggestedVideo.Margin.Top > 0)
|
||||||
{
|
{
|
||||||
Storyboard sb = Resources["HideSuggested"] as Storyboard;
|
Storyboard sb = Resources["HideSuggested"] as Storyboard;
|
||||||
sb.Begin(SuggestedVideo);
|
sb.Begin(SuggestedVideo);
|
||||||
@@ -81,10 +130,18 @@ namespace WeatubeDL
|
|||||||
SuggestedItem.Text = null;
|
SuggestedItem.Text = null;
|
||||||
SuggestedImage.Source = null;
|
SuggestedImage.Source = null;
|
||||||
}
|
}
|
||||||
protected override void OnClosed(EventArgs e)
|
|
||||||
|
public void ShowPanel(DockPanel panel)
|
||||||
{
|
{
|
||||||
base.OnClosed(e);
|
Storyboard sb = Resources["ShowPanel"] as Storyboard;
|
||||||
Application.Current.Shutdown();
|
sb.Begin(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void HidePanel(DockPanel panel)
|
||||||
|
{
|
||||||
|
Storyboard sb = Resources["HidePanel"] as Storyboard;
|
||||||
|
sb.Begin(panel);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,73 +14,97 @@ namespace WeatubeDL
|
|||||||
{
|
{
|
||||||
class YoutubeVideo
|
class YoutubeVideo
|
||||||
{
|
{
|
||||||
public string src { get; private set; }
|
public string Src { get; private set; } // original link provided by user
|
||||||
public string id { get; private set; }
|
public string Id { get; private set; } // YouTube video id
|
||||||
public string[] types { get; private set; }
|
public string[] Types { get; private set; } // All download types available
|
||||||
public string imagePath { get; private set; }
|
public string ImagePath { get; private set; } // Path to image stored in user's temp folder
|
||||||
public BitmapImage image { get; private set; }
|
public BitmapImage Image { get; private set; } // YouTube video preview image
|
||||||
public string name { get; private set; }
|
public string Name { get; private set; } // YouTube video name
|
||||||
private WebClient client;
|
private WebClient client;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates YouTube video object
|
||||||
|
/// </summary>
|
||||||
public YoutubeVideo(string src)
|
public YoutubeVideo(string src)
|
||||||
{
|
{
|
||||||
this.src = src;
|
this.Src = src;
|
||||||
DownloadPreview();
|
DownloadPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys YouTube video object
|
||||||
|
/// </summary>
|
||||||
~YoutubeVideo()
|
~YoutubeVideo()
|
||||||
{
|
{
|
||||||
if (File.Exists(imagePath)) File.Delete(imagePath);
|
if (File.Exists(ImagePath)) File.Delete(ImagePath);
|
||||||
if (client != null) client.Dispose();
|
if (client != null) client.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Takes video id from given url
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>YouTube video Id string / null</returns>
|
||||||
public static string IdFromUrl(string src)
|
public static string IdFromUrl(string src)
|
||||||
{
|
{
|
||||||
if (src.Contains("youtu.be/"))
|
if (src.Contains("youtu.be/"))
|
||||||
return src.Replace("/youtu.be/", "").Split('/')[1].Split('&')[0];
|
return src.Replace("/youtu.be/", "").Split('/')[1].Split('&')[0];
|
||||||
else if (src.Contains("youtube.com/watch?v=") && src.Contains("&"))
|
else if (src.Contains("youtube.com/watch?v=") && src.Contains("&"))
|
||||||
return src.Split('=')[1].Split('&')[0];
|
return src.Split('=')[1].Split('&')[0];
|
||||||
return " ";
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads YouTube video preview image
|
||||||
|
/// </summary>
|
||||||
private void DownloadPreview()
|
private void DownloadPreview()
|
||||||
{
|
{
|
||||||
id = IdFromUrl(src);
|
Id = IdFromUrl(Src);
|
||||||
if (id == null || id == " ") return;
|
if (Id == null) return;
|
||||||
client = new WebClient();
|
client = new WebClient();
|
||||||
client.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0)");
|
client.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0)");
|
||||||
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
|
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");
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes WebClient when download is complete
|
||||||
|
/// </summary>
|
||||||
static void Completed(object sender, AsyncCompletedEventArgs e)
|
static void Completed(object sender, AsyncCompletedEventArgs e)
|
||||||
{
|
{
|
||||||
(sender as WebClient).Dispose();
|
(sender as WebClient).Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if given video exists on YouTube
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true = video exists ; false = video does not exist</returns>
|
||||||
public async Task<bool> Exists()
|
public async Task<bool> Exists()
|
||||||
{
|
{
|
||||||
if (id == null || client == null) return false;
|
if (Id == null) return false;
|
||||||
while(client.IsBusy)
|
if (client != null)
|
||||||
{
|
while(client.IsBusy) //wait until preview is downloaded
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
image.BeginInit();
|
await Task.Delay(100);
|
||||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
}
|
||||||
image.StreamSource = stream;
|
if (File.Exists(ImagePath) && new FileInfo(ImagePath).Length > 0) //checks whether preview is downloaded or not
|
||||||
image.EndInit();
|
{
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
GetVideoName();
|
GetVideoName();
|
||||||
@@ -89,19 +113,24 @@ namespace WeatubeDL
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: После того как прога понимает, что видос существует, она должна подтягивать его форматы
|
/// <summary>
|
||||||
|
/// Downloads webpage from url given by user
|
||||||
|
/// </summary>
|
||||||
private void GetVideoName()
|
private void GetVideoName()
|
||||||
{
|
{
|
||||||
client = new WebClient();
|
client = new WebClient();
|
||||||
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
|
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
|
||||||
client.DownloadStringAsync(new Uri(src));
|
client.DownloadStringAsync(new Uri(Src));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets YouTube video name from webpage title when page is loaded
|
||||||
|
/// </summary>
|
||||||
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
|
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
|
||||||
{
|
{
|
||||||
name = Regex.Match(e.Result, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>",
|
Name = Regex.Match(e.Result, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>",
|
||||||
RegexOptions.IgnoreCase).Groups["Title"].Value;
|
RegexOptions.IgnoreCase).Groups["Title"].Value;
|
||||||
name = name.Remove(name.Length - 10);
|
Name = Name.Remove(Name.Length - 10);
|
||||||
(sender as WebClient).Dispose();
|
(sender as WebClient).Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user