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:
2021-07-14 14:08:11 +03:00
parent 74ab75cf5f
commit 1089864f32
3 changed files with 194 additions and 55 deletions
+63 -10
View File
@@ -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>
+68 -11
View File
@@ -19,12 +19,13 @@ using System.Windows.Media.Animation;
namespace WeatubeDL 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
} }
} }
+63 -34
View File
@@ -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();
} }