- History page re-design
- Added app history management (doesn't affect web site's history) - Extended history information for videos (watching progress) - Continue where you left off feature - Watch later playlist now acts like regular playlist - If video is longer than 1 hour ads will be shown every 30 minutes - Added incognito mode (available in video card context menu) - Search suggestions now run smoother Related Work Items: #140, #252
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<items>
|
||||
<item time="2019-04-21" version="0.6">
|
||||
<item time="2019-04-28" version="0.6">
|
||||
<content>
|
||||
<en-US>### What's new:
|
||||
- Fixed fails when trying to retrieve history, WL or recommended
|
||||
@@ -9,6 +9,14 @@
|
||||
- Fixed videos loading
|
||||
- Fixed special characters appearing in toast notifications
|
||||
- Cursor now hides on playback
|
||||
- History page re-design
|
||||
- Added app history management (doesn't affect web site's history)
|
||||
- Extended history information for videos (watching progress)
|
||||
- Continue where you left off feature
|
||||
- Watch later playlist now acts like regular playlist
|
||||
- If video is longer than 1 hour ads will be shown every 30 minutes
|
||||
- Added incognito mode (available in video card context menu)
|
||||
- Search suggestions now run smoother
|
||||
</en-US>
|
||||
<ru-RU>### Что нового:
|
||||
- Исправлена проблема получения истории, "Посмотреть позже" и рекомендаций
|
||||
@@ -17,6 +25,14 @@
|
||||
- Исправлена загрузка видео
|
||||
- Исправлено появление особых символов в уведомлениях
|
||||
- Теперь курсор скрывается при просмотре
|
||||
- Редизайн страницы истории
|
||||
- Добавлено управление историей просмотра приложения (не влияет на историю просмотров на сайте)
|
||||
- Расширенная информация о просмотренном видео (прогресс просмотра)
|
||||
- Функция продолжения просмотра
|
||||
- Плейлист "Посмотреть позже" теперь ведет себя как обычный плейлист
|
||||
- Если видео длится более 1 часа, рекламный баннер будет появляться каждые 30 минут
|
||||
- Добавлен режим инкогнито (доступен в контекстном меню видео карточки)
|
||||
- Подсказки при поиске работают плавнее
|
||||
</ru-RU>
|
||||
</content>
|
||||
</item>
|
||||
|
||||
@@ -25,6 +25,50 @@ namespace FoxTube
|
||||
{
|
||||
object Parameter { get; set; }
|
||||
}
|
||||
|
||||
public class HistoryItem
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public TimeSpan LeftOn { get; set; } = TimeSpan.FromSeconds(0);
|
||||
}
|
||||
|
||||
public static class HistorySet
|
||||
{
|
||||
public static List<HistoryItem> Items { get; set; } = new List<HistoryItem>();
|
||||
|
||||
public static void Update(HistoryItem item)
|
||||
{
|
||||
if(Items.Exists(i => i.Id == item.Id))
|
||||
Items.RemoveAll(i => i.Id == item.Id);
|
||||
|
||||
Items.Insert(0, item);
|
||||
Save();
|
||||
}
|
||||
|
||||
public static void Delete(HistoryItem item)
|
||||
{
|
||||
Items.Remove(item);
|
||||
Save();
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
Save();
|
||||
}
|
||||
|
||||
private static void Save()
|
||||
{
|
||||
ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] = JsonConvert.SerializeObject(Items);
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
if (ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] != null)
|
||||
Items = JsonConvert.DeserializeObject<List<HistoryItem>>(ApplicationData.Current.RoamingSettings.Values[$"history-{SecretsVault.AccountId}"] as string);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Methods
|
||||
{
|
||||
private static ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace FoxTube
|
||||
};
|
||||
public static YouTubeService Service => IsAuthorized ? new YouTubeService(Initializer) : NoAuthService;
|
||||
public static HttpClient HttpClient { get; } = new HttpClient();
|
||||
private static bool TestAds => false; //Change this bool
|
||||
private static bool TestAds => true; //Change this bool
|
||||
public static string AppId => TestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
|
||||
public static string AdUnitId => TestAds ? "test" : "1100044398";
|
||||
public static bool AdsDisabled { get; private set; } = true;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YoutubeExplode.Models.MediaStreams;
|
||||
using Windows.Media.Editing;
|
||||
using Windows.Media.Core;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FoxTube.Classes
|
||||
{
|
||||
public class VideoProcessor
|
||||
{
|
||||
public VideoPlayer Player { get; set; }
|
||||
MediaComposition composition = new MediaComposition();
|
||||
|
||||
StorageFolder roaming = ApplicationData.Current.RoamingFolder;
|
||||
StorageFile audioCache;
|
||||
StorageFile videoCache;
|
||||
|
||||
public async Task<MediaStreamSource> GetStream(MediaStreamInfo video, MediaStreamInfo audio)
|
||||
{
|
||||
audioCache = await roaming.CreateFileAsync("audioCache.mp4", CreationCollisionOption.ReplaceExisting);
|
||||
videoCache = await roaming.CreateFileAsync("videoCache.mp4", CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
|
||||
|
||||
composition.BackgroundAudioTracks.Add(await BackgroundAudioTrack.CreateFromFileAsync(audioCache));
|
||||
composition.Clips.Add(await MediaClip.CreateFromFileAsync(videoCache));
|
||||
|
||||
return composition.GenerateMediaStreamSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
RequestedTheme="Dark"
|
||||
PointerMoved="UserControl_PointerMoved">
|
||||
|
||||
<Grid Background="White" Name="grid">
|
||||
<Grid Background="{StaticResource SystemChromeMediumColor}">
|
||||
<MediaElement Name="videoSource" MarkerReached="VideoSource_MarkerReached" AreTransportControlsEnabled="True" PosterSource="ms-appx:///Assets/videoThumbSample.png" CurrentStateChanged="VideoSource_CurrentStateChanged" MediaOpened="VideoSource_MediaOpened" VolumeChanged="VideoSource_VolumeChanged">
|
||||
<MediaElement.TransportControls>
|
||||
<foxtube:PlayerControls IsCompactOverlayButtonVisible="True" IsCompactOverlayEnabled="True"
|
||||
@@ -20,6 +20,5 @@
|
||||
IsSkipForwardButtonVisible="True" IsSkipForwardEnabled="True"/>
|
||||
</MediaElement.TransportControls>
|
||||
</MediaElement>
|
||||
<MediaElement Name="audioSource" Width="0" AreTransportControlsEnabled="False" Visibility="Collapsed" Height="0" VerticalAlignment="Top" HorizontalAlignment="Left" CurrentStateChanged="AudioSource_CurrentStateChanged" MediaOpened="AudioSource_MediaOpened"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -11,6 +11,7 @@ using YoutubeExplode.Models.MediaStreams;
|
||||
using YoutubeExplode;
|
||||
using System.Diagnostics;
|
||||
using Windows.Foundation;
|
||||
using FoxTube.Classes;
|
||||
|
||||
namespace FoxTube
|
||||
{
|
||||
@@ -18,16 +19,19 @@ namespace FoxTube
|
||||
{
|
||||
public Video item;
|
||||
public string avatar;
|
||||
public HistoryItem history;
|
||||
bool incognito = false;
|
||||
|
||||
public event Event NextClicked;
|
||||
public event ObjectEventHandler MiniMode;
|
||||
public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
|
||||
public TimeSpan Position => videoSource.Position;
|
||||
public TimeSpan Position
|
||||
{
|
||||
get { return videoSource.Position; }
|
||||
set { videoSource.Position = value; }
|
||||
}
|
||||
|
||||
bool audioLoaded = false;
|
||||
bool videoLoaded = false;
|
||||
bool isMuxed = false;
|
||||
DispatcherTimer muxedTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
|
||||
VideoProcessor processor = new VideoProcessor();
|
||||
|
||||
DispatcherTimer cursorTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
|
||||
|
||||
@@ -43,8 +47,13 @@ namespace FoxTube
|
||||
|
||||
public async void Initialize(Video meta, string channelAvatar, bool privateMode = false)
|
||||
{
|
||||
incognito = privateMode;
|
||||
item = meta;
|
||||
avatar = channelAvatar;
|
||||
history = new HistoryItem
|
||||
{
|
||||
Id = item.Id
|
||||
};
|
||||
|
||||
videoSource.PosterSource = new BitmapImage((meta.Snippet.Thumbnails.Maxres ?? meta.Snippet.Thumbnails.Medium).Url.ToUri());
|
||||
Controls.SetMeta(meta.Snippet.Localized.Title, meta.Snippet.ChannelTitle);
|
||||
@@ -54,6 +63,10 @@ namespace FoxTube
|
||||
InitializeContols();
|
||||
if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
|
||||
videoSource.Markers.Add(new Windows.UI.Xaml.Media.TimelineMarker { Time = Methods.GetDuration(item.ContentDetails.Duration) - TimeSpan.FromMinutes(1) });
|
||||
if(Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes >= 60)
|
||||
for (int k = 1; k < Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes / 30; k++)
|
||||
videoSource.Markers.Add(new Windows.UI.Xaml.Media.TimelineMarker { Time = TimeSpan.FromMinutes(k * 30) });
|
||||
|
||||
Controls.SetQualities(await new YoutubeClient().GetVideoMediaStreamInfosAsync(item.Id));
|
||||
Controls.SetCaptions(await new YoutubeClient().GetVideoClosedCaptionTrackInfosAsync(item.Id));
|
||||
}
|
||||
@@ -70,8 +83,7 @@ namespace FoxTube
|
||||
videoSource.AreTransportControlsEnabled = false;
|
||||
|
||||
if (!privateMode)
|
||||
Debug.WriteLine("TODO: history entry creation");
|
||||
// TODO: Create history entry
|
||||
HistorySet.Update(history);
|
||||
|
||||
Visibility = Visibility.Visible;
|
||||
}
|
||||
@@ -85,12 +97,6 @@ namespace FoxTube
|
||||
{
|
||||
videoSource.Volume = SettingsStorage.Volume;
|
||||
|
||||
muxedTimer.Tick += (s, e) =>
|
||||
{
|
||||
if (!Enumerable.Range(-100, 100).Contains((int)(videoSource.Position - audioSource.Position).TotalMilliseconds))
|
||||
audioSource.Position = videoSource.Position;
|
||||
};
|
||||
|
||||
cursorTimer.Tick += (s, e) =>
|
||||
{
|
||||
Point cursorPoint = CoreWindow.GetForCurrentThread().PointerPosition;
|
||||
@@ -98,8 +104,8 @@ namespace FoxTube
|
||||
cursorPoint.Y -= Window.Current.Bounds.Y;
|
||||
Rect playerBounds = TransformToVisual(Methods.MainPage).TransformBounds(new Rect(0, 0, ActualWidth, ActualHeight));
|
||||
|
||||
if(cursorPoint.Y > playerBounds.Top && cursorPoint.Y < playerBounds.Bottom &&
|
||||
cursorPoint.X > playerBounds.Left && cursorPoint.X < playerBounds.Right)
|
||||
if((cursorPoint.Y > playerBounds.Top && cursorPoint.Y < playerBounds.Bottom &&
|
||||
cursorPoint.X > playerBounds.Left && cursorPoint.X < playerBounds.Right) || videoSource.IsFullWindow)
|
||||
CoreWindow.GetForCurrentThread().PointerCursor = null;
|
||||
cursorTimer.Stop();
|
||||
};
|
||||
@@ -108,7 +114,6 @@ namespace FoxTube
|
||||
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
|
||||
Controls.QualityChanged += Controls_QualityChanged;
|
||||
Controls.MiniModeChanged += Controls_MiniModeChanged;
|
||||
Controls.MuteClicked += Controls_MuteClicked;
|
||||
Controls.Player = videoSource;
|
||||
|
||||
#region System Media Transport Controls
|
||||
@@ -128,12 +133,6 @@ namespace FoxTube
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void Controls_MuteClicked()
|
||||
{
|
||||
if (audioSource != null)
|
||||
audioSource.IsMuted = videoSource.IsMuted;
|
||||
}
|
||||
|
||||
public void Controls_MiniModeChanged(object sender, bool e)
|
||||
{
|
||||
videoSource.IsFullWindow = false;
|
||||
@@ -146,33 +145,17 @@ namespace FoxTube
|
||||
Controls.Minimize();
|
||||
}
|
||||
|
||||
private void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
|
||||
private async void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
|
||||
{
|
||||
/*new MediaStreamSourceSampleRequest()
|
||||
new MediaStreamSource()
|
||||
|
||||
MediaSource.
|
||||
new MediaPlaybackItem(null).VideoTracks[0]
|
||||
IMediaPlaybackSource
|
||||
videoSource.Sou*/
|
||||
|
||||
videoSource.Pause();
|
||||
|
||||
timecodeBackup = videoSource.Position;
|
||||
needUpdateTimecode = true;
|
||||
|
||||
audioLoaded = false;
|
||||
videoLoaded = false;
|
||||
muxedTimer.Stop();
|
||||
|
||||
if (requestedQuality is MuxedStreamInfo)
|
||||
videoSource.Source = requestedQuality.Url.ToUri();
|
||||
if(requestedQuality is MuxedStreamInfo)
|
||||
{
|
||||
isMuxed = true;
|
||||
audioSource.Source = null;
|
||||
}
|
||||
else
|
||||
audioSource.Source = list.Audio.First().Url.ToUri();
|
||||
videoSource.SetMediaStreamSource(await processor.GetStream(requestedQuality, list.Audio.First()));
|
||||
}
|
||||
|
||||
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
|
||||
@@ -180,8 +163,13 @@ namespace FoxTube
|
||||
if(systemControls != null)
|
||||
systemControls.IsEnabled = false;
|
||||
|
||||
if (!incognito)
|
||||
{
|
||||
history.LeftOn = videoSource.Position;
|
||||
HistorySet.Update(history);
|
||||
}
|
||||
|
||||
videoSource.Stop();
|
||||
audioSource.Stop();
|
||||
|
||||
Methods.MainPage.CloseVideo();
|
||||
}
|
||||
@@ -190,9 +178,6 @@ namespace FoxTube
|
||||
{
|
||||
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
if (args.Button == SystemMediaTransportControlsButton.Next)
|
||||
NextClicked?.Invoke();
|
||||
|
||||
switch (args.Button)
|
||||
{
|
||||
case SystemMediaTransportControlsButton.Play:
|
||||
@@ -202,7 +187,7 @@ namespace FoxTube
|
||||
videoSource.Pause();
|
||||
break;
|
||||
case SystemMediaTransportControlsButton.Next:
|
||||
NextClicked.Invoke();
|
||||
NextClicked?.Invoke();
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -220,48 +205,29 @@ namespace FoxTube
|
||||
case Windows.UI.Xaml.Media.MediaElementState.Buffering:
|
||||
case Windows.UI.Xaml.Media.MediaElementState.Paused:
|
||||
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
|
||||
// TODO: Create history entry
|
||||
audioSource?.Pause();
|
||||
if(!incognito)
|
||||
{
|
||||
history.LeftOn = videoSource.Position;
|
||||
HistorySet.Update(history);
|
||||
}
|
||||
break;
|
||||
case Windows.UI.Xaml.Media.MediaElementState.Playing:
|
||||
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
|
||||
audioSource?.Play();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AudioSource_CurrentStateChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(audioSource.CurrentState == Windows.UI.Xaml.Media.MediaElementState.Playing)
|
||||
muxedTimer.Start();
|
||||
else
|
||||
muxedTimer.Stop();
|
||||
}
|
||||
|
||||
private void VideoSource_MediaOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
videoLoaded = true;
|
||||
if (needUpdateTimecode)
|
||||
{
|
||||
if (!needUpdateTimecode)
|
||||
return;
|
||||
|
||||
videoSource.Position = timecodeBackup;
|
||||
needUpdateTimecode = false;
|
||||
}
|
||||
if (audioLoaded || isMuxed)
|
||||
videoSource.Play();
|
||||
}
|
||||
|
||||
private void AudioSource_MediaOpened(object sender, RoutedEventArgs e)
|
||||
{
|
||||
audioLoaded = true;
|
||||
if (needUpdateTimecode)
|
||||
audioSource.Position = timecodeBackup;
|
||||
if (videoLoaded)
|
||||
videoSource.Play();
|
||||
}
|
||||
|
||||
private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
audioSource.Volume = videoSource.Volume;
|
||||
SettingsStorage.Volume = videoSource.Volume;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<UserControl.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem x:Uid="/Cards/play" Icon="Play" Text="Play" Name="play" Click="Button_Click"/>
|
||||
<MenuFlyoutItem x:Uid="/Cards/incognitoPlay" Text="Play incognito" Visibility="Collapsed">
|
||||
<MenuFlyoutItem x:Uid="/Cards/incognitoPlay" Text="Play incognito" Name="incognito" Click="Button_Click">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon Glyph=""/>
|
||||
</MenuFlyoutItem.Icon>
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FoxTube.Controls
|
||||
public string playlistId;
|
||||
public string videoId;
|
||||
Video item;
|
||||
HistoryItem history;
|
||||
|
||||
public VideoCard(string id, string playlist = null)
|
||||
{
|
||||
@@ -103,8 +104,13 @@ namespace FoxTube.Controls
|
||||
try { avatar.ProfilePicture = new BitmapImage((await new YoutubeClient().GetChannelAsync(item.Snippet.ChannelId)).LogoUrl.ToUri()) { DecodePixelWidth = 46, DecodePixelHeight = 46 }; }
|
||||
catch { }
|
||||
|
||||
if(SecretsVault.History.Contains(videoId))
|
||||
if (SecretsVault.History.Contains(videoId))
|
||||
watched.Visibility = Visibility.Visible;
|
||||
if (HistorySet.Items.Exists(i => i.Id == item.Id))
|
||||
{
|
||||
watched.Visibility = Visibility.Visible;
|
||||
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
|
||||
}
|
||||
|
||||
show.Begin();
|
||||
}
|
||||
@@ -196,6 +202,8 @@ namespace FoxTube.Controls
|
||||
|
||||
if (SecretsVault.History.Contains(videoId))
|
||||
watched.Visibility = Visibility.Visible;
|
||||
if (HistorySet.Items.Exists(i => i.Id == item.Id))
|
||||
leftOn.Value = 100 * HistorySet.Items.Find(i => i.Id == item.Id).LeftOn.TotalSeconds / Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds;
|
||||
|
||||
show.Begin();
|
||||
}
|
||||
@@ -234,7 +242,7 @@ namespace FoxTube.Controls
|
||||
}
|
||||
}
|
||||
|
||||
Methods.MainPage.GoToVideo(videoId, playlistId);
|
||||
Methods.MainPage.GoToVideo(videoId, playlistId == "HL" ? null : playlistId, ((FrameworkElement)sender).Name == "incognito" ? true : false);
|
||||
}
|
||||
|
||||
private void Share(DataTransferManager sender, DataRequestedEventArgs args)
|
||||
@@ -358,9 +366,35 @@ namespace FoxTube.Controls
|
||||
Item_Click(menuItem, null);
|
||||
}
|
||||
|
||||
private void Wl_Click(object sender, RoutedEventArgs e)
|
||||
private async void Wl_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (wl.IsChecked)
|
||||
{
|
||||
try
|
||||
{
|
||||
PlaylistItem playlist = new PlaylistItem
|
||||
{
|
||||
Snippet = new PlaylistItemSnippet
|
||||
{
|
||||
PlaylistId = "WL",
|
||||
ResourceId = new ResourceId
|
||||
{
|
||||
VideoId = item.Id,
|
||||
Kind = "youtube#video"
|
||||
}
|
||||
}
|
||||
};
|
||||
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
|
||||
|
||||
await request.ExecuteAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
wl.IsChecked = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
wl.IsChecked = true;
|
||||
}
|
||||
|
||||
async void LoadAddTo()
|
||||
@@ -449,6 +483,22 @@ namespace FoxTube.Controls
|
||||
|
||||
private async void Delete_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (playlistId == "WL")
|
||||
{
|
||||
await Launcher.LaunchUriAsync("https://youtube.com/playlist?list=WL".ToUri());
|
||||
return;
|
||||
}
|
||||
else if(playlistId == "HL")
|
||||
{
|
||||
if (history == null)
|
||||
await Launcher.LaunchUriAsync("https://youtube.com/feed/history".ToUri());
|
||||
else
|
||||
{
|
||||
HistorySet.Delete(history);
|
||||
(Methods.MainPage.PageContent as History).Delete(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
PlaylistItemsResource.ListRequest request = SecretsVault.Service.PlaylistItems.List("snippet");
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
<Compile Include="Classes\Methods.cs" />
|
||||
<Compile Include="Classes\SearchPaameters.cs" />
|
||||
<Compile Include="Classes\SettingsStorage.cs" />
|
||||
<Compile Include="Classes\VideoProcessor.cs" />
|
||||
<Compile Include="Controls\Advert.xaml.cs">
|
||||
<DependentUpon>Advert.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
||||
@@ -15,16 +15,57 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Pivot SelectionChanged="Pivot_SelectionChanged">
|
||||
<PivotItem x:Uid="/Playlist/appHistory" Header="Application">
|
||||
<Grid>
|
||||
<ScrollViewer>
|
||||
<StackPanel>
|
||||
<Grid Margin="0,10" BorderBrush="Red" BorderThickness="5" CornerRadius="10" Padding="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<FontIcon Glyph="" FontSize="50" VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="10,0">
|
||||
<TextBlock x:Uid="/Playlist/hlHeader" FontWeight="Bold" FontSize="25" Text="Missing some stuff?" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Uid="/Playlist/hlBody" Text="Unfortunately, for now we are unable to manage your youtube history. If you want to see your web history go to 'Website' tab" TextWrapping="WrapWholeWords"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Button Margin="5" x:Uid="/Playlist/clear" FontFamily="Segoe UI, Segoe MDL2 Assets" Content=" Clear history" Name="clear" Click="Clear_Click"/>
|
||||
<local:VideoGrid x:Name="list"/>
|
||||
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<foxtube:LoadingPage x:Name="loading" Visibility="Collapsed" RefreshPage="Refresh_Click"/>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
<PivotItem x:Uid="/Playlist/webHistory" Header="Website">
|
||||
<Grid>
|
||||
<ScrollViewer>
|
||||
<StackPanel>
|
||||
<local:VideoGrid x:Name="websiteList"/>
|
||||
<controls:ShowMore x:Name="websiteMore" Clicked="WebsiteMore_Clicked"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<foxtube:LoadingPage x:Name="websiteLoading" Visibility="Collapsed" RefreshPage="Refresh_Click"/>
|
||||
</Grid>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
|
||||
<!--<ScrollViewer>
|
||||
<StackPanel>
|
||||
<local:VideoGrid x:Name="list"/>
|
||||
<controls:ShowMore Clicked="ShowMore_Clicked" x:Name="more"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</ScrollViewer>-->
|
||||
|
||||
<CommandBar Grid.Row="1" DefaultLabelPosition="Right">
|
||||
<AppBarButton Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
|
||||
<AppBarButton Label="Open in browser" Icon="Globe" Name="toBrowser" Click="toBrowser_Click"/>
|
||||
<AppBarButton x:Uid="/Playlist/refresh" Icon="Refresh" Label="Refresh" Name="refresh" Click="Refresh_Click"/>
|
||||
<AppBarButton x:Uid="/Playlist/openWeb" Label="Open in browser" Icon="Globe" Name="toBrowser" Click="toBrowser_Click"/>
|
||||
</CommandBar>
|
||||
<foxtube:LoadingPage Visibility="Collapsed" Grid.RowSpan="2" x:Name="loading"/>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.AppCenter.Analytics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.System;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
@@ -15,9 +16,8 @@ namespace FoxTube.Pages
|
||||
public sealed partial class History : Page, NavigationPage
|
||||
{
|
||||
public object Parameter { get; set; } = null;
|
||||
List<string> entries;
|
||||
int page = 1;
|
||||
public string id = "HL";
|
||||
int webPage = 0;
|
||||
|
||||
public History()
|
||||
{
|
||||
@@ -29,11 +29,6 @@ namespace FoxTube.Pages
|
||||
base.OnNavigatedTo(e);
|
||||
Parameter = e.Parameter;
|
||||
|
||||
loading.RefreshPage += Refresh_Click;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(e.Parameter.ToString()))
|
||||
id = e.Parameter.ToString();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
@@ -43,52 +38,127 @@ namespace FoxTube.Pages
|
||||
{
|
||||
loading.Refresh();
|
||||
|
||||
entries = id == "HL" ? SecretsVault.History = await Methods.GetHistory() : SecretsVault.WatchLater = await Methods.GetLater();
|
||||
await Dispatcher.RunIdleAsync((command) =>
|
||||
{
|
||||
for (int k = 0; k < 25 && k < HistorySet.Items.Count; k++)
|
||||
list.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
|
||||
|
||||
for (int k = 0; k < 25 && k < entries.Count; k++)
|
||||
list.Add(new VideoCard(entries[k]));
|
||||
|
||||
if (list.Count >= entries.Count)
|
||||
if (list.Count >= HistorySet.Items.Count)
|
||||
more.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (list.Count == 0)
|
||||
clear.Visibility = Visibility.Collapsed;
|
||||
});
|
||||
|
||||
loading.Close();
|
||||
}
|
||||
catch (System.Net.Http.HttpRequestException)
|
||||
{
|
||||
loading.Error("System.Net.Http.HttpRequestException", "Unable to connect to Google servers.", true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
loading.Error(e.GetType().ToString(), e.Message);
|
||||
Analytics.TrackEvent("History loading error", new Dictionary<string, string>()
|
||||
Analytics.TrackEvent("Local history loading error", new Dictionary<string, string>()
|
||||
{
|
||||
{ "Exception", e.GetType().ToString() },
|
||||
{ "Message", e.Message },
|
||||
{ "ID", id }
|
||||
{ "Message", e.Message }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void toBrowser_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(id == "HL" ? "https://www.youtube.com/feed/history" : "https://www.youtube.com/playlist?list=WL"));
|
||||
await Launcher.LaunchUriAsync("https://www.youtube.com/feed/history".ToUri());
|
||||
}
|
||||
|
||||
private void Refresh_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
list.Clear();
|
||||
Initialize();
|
||||
Methods.MainPage.GoToHistory();
|
||||
}
|
||||
|
||||
private void ShowMore_Clicked()
|
||||
private async void ShowMore_Clicked()
|
||||
{
|
||||
for (int k = 25 * page++; k < 25 * page; k++)
|
||||
list.Add(new VideoCard(entries[k]));
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
for (int k = 25 * page++; k < 25 * page && k < HistorySet.Items.Count; k++)
|
||||
list.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
|
||||
|
||||
if (list.Count >= entries.Count)
|
||||
if (list.Count >= HistorySet.Items.Count)
|
||||
more.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
more.Complete();
|
||||
});
|
||||
}
|
||||
|
||||
private async void Clear_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//TODO: Localize strings
|
||||
MessageDialog dialog = new MessageDialog("Are you sure? This action cannot be undone", "Delete app history");
|
||||
dialog.Commands.Add(new UICommand("No"));
|
||||
dialog.Commands.Add(new UICommand("Yes", (command) =>
|
||||
{
|
||||
HistorySet.Clear();
|
||||
Refresh_Click(this, null);
|
||||
}));
|
||||
|
||||
dialog.CancelCommandIndex = 0;
|
||||
dialog.DefaultCommandIndex = 1;
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
|
||||
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if(webPage == 0)
|
||||
{
|
||||
webPage++;
|
||||
LoadWeb();
|
||||
}
|
||||
}
|
||||
|
||||
async void LoadWeb()
|
||||
{
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
websiteLoading.Refresh();
|
||||
|
||||
SecretsVault.History = await Methods.GetHistory();
|
||||
|
||||
for (int k = 0; k < 25 && k < SecretsVault.History.Count; k++)
|
||||
websiteList.Add(new VideoCard(SecretsVault.History[k], "HL"));
|
||||
|
||||
if (websiteList.Count >= SecretsVault.History.Count)
|
||||
websiteMore.Visibility = Visibility.Collapsed;
|
||||
|
||||
websiteLoading.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
websiteLoading.Error(e.GetType().ToString(), e.Message);
|
||||
Analytics.TrackEvent("History loading error", new Dictionary<string, string>()
|
||||
{
|
||||
{ "Exception", e.GetType().ToString() },
|
||||
{ "Message", e.Message }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void WebsiteMore_Clicked()
|
||||
{
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
for (int k = 25 * webPage++; k < 25 * webPage && k < SecretsVault.History.Count; k++)
|
||||
websiteList.Add(new VideoCard(HistorySet.Items[k].Id, "HL"));
|
||||
|
||||
if (websiteList.Count >= SecretsVault.History.Count)
|
||||
websiteMore.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
websiteMore.Complete();
|
||||
});
|
||||
}
|
||||
|
||||
public void Delete (VideoCard item)
|
||||
{
|
||||
list.DeleteItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace FoxTube
|
||||
bool wasInvoked = false;
|
||||
public static event ObjectEventHandler VideoPageSizeChanged;
|
||||
readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Main");
|
||||
Dictionary<Type, Action> headers;
|
||||
Dictionary<Type, string> headers;
|
||||
|
||||
public Page PageContent => content.Content as Page;
|
||||
|
||||
@@ -57,22 +57,16 @@ namespace FoxTube
|
||||
};
|
||||
SecretsVault.Initialize();
|
||||
|
||||
headers = new Dictionary<Type, Action>()
|
||||
headers = new Dictionary<Type, string>()
|
||||
{
|
||||
{ typeof(Settings), () => Title.Text = resources.GetString("/Main/settings/Content") },
|
||||
{ typeof(ChannelPage), () => Title.Text = resources.GetString("/Main/channel") },
|
||||
{ typeof(PlaylistPage), () => Title.Text = resources.GetString("/Main/playlist") },
|
||||
{ typeof(Search), () => Title.Text = resources.GetString("/Main/searchPlaceholder/PlaceholderText") },
|
||||
{ typeof(Subscriptions), () => Title.Text = resources.GetString("/Main/subscriptions/Content") },
|
||||
{ typeof(History), () =>
|
||||
{
|
||||
if((content.Content as History).id == "HL")
|
||||
Title.Text = resources.GetString("/Main/history/Content");
|
||||
else
|
||||
Title.Text = resources.GetString("/Main/later/Content");
|
||||
} },
|
||||
{ typeof(Home), () => Title.Text = resources.GetString("/Main/home/Content") },
|
||||
{ typeof(Downloads), () => Title.Text = resources.GetString("/Main/downloads/Content") }
|
||||
{ typeof(Settings), resources.GetString("/Main/settings/Content") },
|
||||
{ typeof(ChannelPage), resources.GetString("/Main/channel") },
|
||||
{ typeof(PlaylistPage), resources.GetString("/Main/playlist") },
|
||||
{ typeof(Search), resources.GetString("/Main/searchPlaceholder/PlaceholderText") },
|
||||
{ typeof(Subscriptions), resources.GetString("/Main/subscriptions/Content") },
|
||||
{ typeof(History), resources.GetString("/Main/history/Content") },
|
||||
{ typeof(Home), resources.GetString("/Main/home/Content") },
|
||||
{ typeof(Downloads), resources.GetString("/Main/downloads/Content") }
|
||||
};
|
||||
|
||||
if(StoreServicesFeedbackLauncher.IsSupported())
|
||||
@@ -200,6 +194,7 @@ namespace FoxTube
|
||||
for (int k = 0; k < SecretsVault.Subscriptions.Count && k < 10; k++)
|
||||
nav.MenuItems.Add(SecretsVault.Subscriptions[k]);
|
||||
}
|
||||
HistorySet.Load();
|
||||
break;
|
||||
|
||||
case false:
|
||||
@@ -283,7 +278,7 @@ namespace FoxTube
|
||||
content.Navigate(typeof(Home));
|
||||
}
|
||||
|
||||
public async void GoToVideo(string id, string playlistId = null)
|
||||
public async void GoToVideo(string id, string playlistId = null, bool incognito = false)
|
||||
{
|
||||
if (SettingsStorage.CheckConnection)
|
||||
try
|
||||
@@ -342,7 +337,7 @@ namespace FoxTube
|
||||
nav.IsPaneOpen = false;
|
||||
|
||||
VideoPageSizeChanged?.Invoke(this, true);
|
||||
videoPlaceholder.Navigate(typeof(VideoPage), new string[2] { id, playlistId });
|
||||
videoPlaceholder.Navigate(typeof(VideoPage), new object[3] { id, playlistId, incognito });
|
||||
|
||||
Title.Text = resources.GetString("/Main/video");
|
||||
}
|
||||
@@ -357,6 +352,11 @@ namespace FoxTube
|
||||
content.Navigate(typeof(PlaylistPage), id);
|
||||
}
|
||||
|
||||
public void GoToHistory()
|
||||
{
|
||||
content.Navigate(typeof(History));
|
||||
}
|
||||
|
||||
public void GoToDownloads()
|
||||
{
|
||||
content.Navigate(typeof(Downloads));
|
||||
@@ -372,8 +372,7 @@ namespace FoxTube
|
||||
else
|
||||
(videoPlaceholder.Content as VideoPage).Player.Minimize();
|
||||
|
||||
try { headers[content.SourcePageType](); }
|
||||
catch { }
|
||||
Title.Text = headers[content.SourcePageType];
|
||||
}
|
||||
|
||||
public void MinimizeVideo()
|
||||
@@ -393,8 +392,7 @@ namespace FoxTube
|
||||
|
||||
SetNavigationMenu();
|
||||
|
||||
try { headers[content.SourcePageType](); }
|
||||
catch { }
|
||||
Title.Text = headers[content.SourcePageType];
|
||||
}
|
||||
|
||||
void SetNavigationMenu()
|
||||
@@ -445,8 +443,7 @@ namespace FoxTube
|
||||
|
||||
SetNavigationMenu();
|
||||
|
||||
try { headers[content.SourcePageType](); }
|
||||
catch { }
|
||||
Title.Text = headers[content.SourcePageType];
|
||||
}
|
||||
|
||||
private void Search_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||
@@ -455,9 +452,10 @@ namespace FoxTube
|
||||
GoToSearch(new SearchParameters(search.Text));
|
||||
}
|
||||
|
||||
private void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
private async void Search_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
//TODO: Make it run async
|
||||
if (search.Text.Length > 2 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
try
|
||||
@@ -474,6 +472,7 @@ namespace FoxTube
|
||||
}
|
||||
catch { search.ItemsSource = new List<string>(); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SetNavigationItem(object item)
|
||||
@@ -490,8 +489,7 @@ namespace FoxTube
|
||||
|
||||
public void Content_Navigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
try { headers[e.SourcePageType](); }
|
||||
catch { }
|
||||
Title.Text = headers[content.SourcePageType];
|
||||
|
||||
if (!wasInvoked)
|
||||
{
|
||||
@@ -517,29 +515,18 @@ namespace FoxTube
|
||||
SetNavigationItem(null);
|
||||
}
|
||||
else if(e.SourcePageType == typeof(History))
|
||||
{
|
||||
if (e.Parameter.ToString() == "HL")
|
||||
SetNavigationItem(toHistory);
|
||||
else
|
||||
SetNavigationItem(toLater);
|
||||
}
|
||||
else if(e.SourcePageType == typeof(PlaylistPage))
|
||||
{
|
||||
if (e.Parameter.ToString() == SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes)
|
||||
SetNavigationItem(toLiked);
|
||||
else if (e.Parameter.Equals("WL"))
|
||||
SetNavigationItem(toLater);
|
||||
}
|
||||
}
|
||||
else
|
||||
wasInvoked = false;
|
||||
|
||||
if(e.SourcePageType == typeof(History))
|
||||
{
|
||||
if(e.Parameter.ToString() == "HL")
|
||||
Title.Text = resources.GetString("/Main/history/Content");
|
||||
else if(e.Parameter.ToString() == "WL")
|
||||
Title.Text = resources.GetString("/Main/later/Content");
|
||||
}
|
||||
|
||||
if (content.CanGoBack)
|
||||
nav.IsBackEnabled = true;
|
||||
else
|
||||
@@ -580,11 +567,11 @@ namespace FoxTube
|
||||
if (args.SelectedItem == toHome)
|
||||
content.Navigate(typeof(Home));
|
||||
else if (args.SelectedItem == toHistory)
|
||||
content.Navigate(typeof(History), "HL");
|
||||
content.Navigate(typeof(History));
|
||||
else if (args.SelectedItem == toLiked)
|
||||
content.Navigate(typeof(PlaylistPage), SecretsVault.UserChannel.ContentDetails.RelatedPlaylists.Likes);
|
||||
else if (args.SelectedItem == toLater)
|
||||
content.Navigate(typeof(History), "WL");
|
||||
content.Navigate(typeof(PlaylistPage), "WL");
|
||||
else if (args.SelectedItem == toSubscriptions)
|
||||
content.Navigate(typeof(Subscriptions));
|
||||
else if (args.SelectedItem == toDownloads)
|
||||
|
||||
@@ -58,6 +58,19 @@
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1" Grid.Row="1">
|
||||
<Grid Name="wlAlert" Margin="10" BorderBrush="Red" BorderThickness="5" CornerRadius="10" Padding="10" Visibility="Collapsed">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<FontIcon Glyph="" FontSize="50" VerticalAlignment="Center"/>
|
||||
|
||||
<StackPanel Grid.Column="1" Margin="10,0">
|
||||
<TextBlock x:Uid="/Playlist/wlHeader" FontWeight="Bold" FontSize="25" Text="Don't know how to delete video from 'Watch later' playlist?" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Uid="/Playlist/wlBody" Text="Unfortunately, for now we are unable to delete videos from 'Watch later' playlist. If you want to do that you have to visit YouTube web page" TextWrapping="WrapWholeWords"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<local:VideoGrid x:Name="list"/>
|
||||
<controls:ShowMore x:Name="more" Clicked="ShowMore_Clicked"/>
|
||||
</StackPanel>
|
||||
|
||||
@@ -4,6 +4,7 @@ using Google.Apis.YouTube.v3.Data;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Foundation;
|
||||
@@ -26,6 +27,7 @@ namespace FoxTube.Pages
|
||||
|
||||
PlaylistItemsResource.ListRequest request;
|
||||
string token;
|
||||
int page = 1;
|
||||
|
||||
public PlaylistPage()
|
||||
{
|
||||
@@ -40,6 +42,8 @@ namespace FoxTube.Pages
|
||||
Parameter = e.Parameter;
|
||||
if (e.Parameter == null)
|
||||
loading.Error("NullReferenceException", "Unable to initialize page. Playlist ID is not stated.");
|
||||
else if (e.Parameter as string == "WL")
|
||||
LoadWL();
|
||||
else
|
||||
Initialize(e.Parameter as string);
|
||||
}
|
||||
@@ -103,6 +107,49 @@ namespace FoxTube.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public async void LoadWL()
|
||||
{
|
||||
loading.Refresh();
|
||||
|
||||
try
|
||||
{
|
||||
playlistId = "WL";
|
||||
share.Visibility = Visibility.Collapsed;
|
||||
wlAlert.Visibility = Visibility.Visible;
|
||||
//TODO: Localize strings
|
||||
|
||||
SecretsVault.WatchLater = await Methods.GetLater();
|
||||
|
||||
title.Text = ResourceLoader.GetForCurrentView("Main").GetString("/Main/later/Content");
|
||||
info.Text = $"{SecretsVault.WatchLater.Count} {ResourceLoader.GetForCurrentView("Playlist").GetString("/Playlist/videos")}";
|
||||
description.Text = "";
|
||||
|
||||
channelName.Text = SecretsVault.UserChannel.Snippet.Title;
|
||||
|
||||
try { avatar.ProfilePicture = new BitmapImage(SecretsVault.UserChannel.Snippet.Thumbnails.Medium.Url.ToUri()) { DecodePixelWidth = 50, DecodePixelHeight = 50 }; }
|
||||
catch { }
|
||||
try { thumbnail.Source = new BitmapImage((await new YoutubeExplode.YoutubeClient().GetVideoAsync(SecretsVault.WatchLater.First())).Thumbnails.HighResUrl.ToUri()); }
|
||||
catch { }
|
||||
|
||||
for (int k = 0; k < 25 && k < SecretsVault.WatchLater.Count; k++)
|
||||
list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
|
||||
|
||||
if (list.Count >= SecretsVault.WatchLater.Count)
|
||||
more.Visibility = Visibility.Collapsed;
|
||||
|
||||
loading.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
loading.Error(e.GetType().ToString(), e.Message);
|
||||
Analytics.TrackEvent("WL playlist loading error", new Dictionary<string, string>()
|
||||
{
|
||||
{ "Exception", e.GetType().ToString() },
|
||||
{ "Message", e.Message }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void toChannel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Methods.MainPage.GoToChannel(item.Snippet.ChannelId);
|
||||
@@ -134,6 +181,12 @@ namespace FoxTube.Pages
|
||||
|
||||
private async void ShowMore_Clicked()
|
||||
{
|
||||
if(playlistId == "WL")
|
||||
{
|
||||
MoreWL();
|
||||
return;
|
||||
}
|
||||
|
||||
request.PageToken = token;
|
||||
PlaylistItemListResponse response = await request.ExecuteAsync();
|
||||
|
||||
@@ -149,6 +202,17 @@ namespace FoxTube.Pages
|
||||
list.Add(new VideoCard(i.ContentDetails.VideoId, playlistId));
|
||||
}
|
||||
|
||||
private void MoreWL()
|
||||
{
|
||||
for (int k = 25 * page++; k < 25 * page && k < SecretsVault.WatchLater.Count; k++)
|
||||
list.Add(new VideoCard(SecretsVault.WatchLater[k], "WL"));
|
||||
|
||||
if (list.Count >= SecretsVault.WatchLater.Count)
|
||||
more.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
more.Complete();
|
||||
}
|
||||
|
||||
public void DeleteItem(FrameworkElement card)
|
||||
{
|
||||
list.DeleteItem(card);
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<local:VideoPlayer x:Name="player" NextClicked="Player_NextClicked" MiniMode="Player_Minimize"/>
|
||||
<PivotItem Header="Description" Name="descriptionPanel">
|
||||
<StackPanel Margin="0,10">
|
||||
<Button FontFamily="Segoe UI, Segoe MDL2 Assets" Content=" Continue from: 00:10:37" Name="left" Click="Left_Click" Visibility="Collapsed"/>
|
||||
<TextBlock IsTextSelectionEnabled="True" Name="title" Text="[Video title]" FontSize="25" TextWrapping="WrapWholeWords" HorizontalTextAlignment="Start"/>
|
||||
<TextBlock Text="Published at: " Name="date"/>
|
||||
<Grid>
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace FoxTube.Pages
|
||||
public string videoId;
|
||||
public string playlistId = null;
|
||||
public Video item;
|
||||
public HistoryItem history;
|
||||
public bool incognito = false;
|
||||
|
||||
bool isExtended = false;
|
||||
|
||||
@@ -92,27 +94,28 @@ namespace FoxTube.Pages
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
if (e.Parameter == null || string.IsNullOrWhiteSpace((e.Parameter as string[])[0]))
|
||||
if (e.Parameter == null)
|
||||
loading.Error("NullReferenceException", "Unable to initialize page. Video ID is not stated.");
|
||||
else
|
||||
Initialize(e.Parameter as string[]);
|
||||
Initialize(e.Parameter as object[]);
|
||||
}
|
||||
|
||||
public async void Initialize(string[] ids)
|
||||
public async void Initialize(object[] ids)
|
||||
{
|
||||
loading.Refresh();
|
||||
|
||||
try
|
||||
{
|
||||
videoId = ids[0];
|
||||
videoId = ids[0] as string;
|
||||
incognito = (bool)ids[2];
|
||||
|
||||
if (ids[1] != null)
|
||||
LoadPlaylist(ids[1]);
|
||||
LoadPlaylist(ids[1] as string);
|
||||
else
|
||||
pivot.Items.Remove(playlist);
|
||||
|
||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet,statistics,status,contentDetails,liveStreamingDetails");
|
||||
request.Id = ids[0];
|
||||
request.Id = ids[0] as string;
|
||||
request.Hl = SettingsStorage.RelevanceLanguage;
|
||||
item = (await request.ExecuteAsync()).Items[0];
|
||||
|
||||
@@ -181,6 +184,31 @@ namespace FoxTube.Pages
|
||||
List<VideoPlaylistItem> items = new List<VideoPlaylistItem>();
|
||||
VideoPlaylistItem selection = null;
|
||||
|
||||
if (id == "WL")
|
||||
{
|
||||
SecretsVault.WatchLater = await Methods.GetLater();
|
||||
|
||||
foreach (string i in SecretsVault.WatchLater)
|
||||
{
|
||||
VideosResource.ListRequest request = SecretsVault.Service.Videos.List("snippet");
|
||||
request.Id = i;
|
||||
Video v = (await request.ExecuteAsync()).Items[0];
|
||||
items.Add(new VideoPlaylistItem(v.Snippet.Thumbnails.Medium.Url, v.Snippet.Title, i));
|
||||
}
|
||||
|
||||
selection = items.Find(i => i.Id == item.Id);
|
||||
|
||||
for (int k = 0; k < items.Count; k++)
|
||||
items[k].Number = k + 1;
|
||||
|
||||
playlistName.Text = resources.GetString("/Main/later/Content");
|
||||
playlistChannel.Text = SecretsVault.UserChannel.Snippet.Title;
|
||||
|
||||
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{SecretsVault.WatchLater.Count}";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//Retrieving data
|
||||
PlaylistsResource.ListRequest playlistRequest = SecretsVault.Service.Playlists.List("snippet,contentDetails");
|
||||
playlistRequest.Id = id;
|
||||
@@ -197,7 +225,7 @@ namespace FoxTube.Pages
|
||||
listResponse = await listRequest.ExecuteAsync();
|
||||
listRequest.PageToken = listResponse.NextPageToken;
|
||||
|
||||
foreach(PlaylistItem i in listResponse.Items)
|
||||
foreach (PlaylistItem i in listResponse.Items)
|
||||
items.Add(new VideoPlaylistItem(i.Snippet.Thumbnails.Medium.Url, i.Snippet.Title, i.Snippet.ResourceId.VideoId));
|
||||
}
|
||||
while (!string.IsNullOrWhiteSpace(listRequest.PageToken));
|
||||
@@ -212,6 +240,7 @@ namespace FoxTube.Pages
|
||||
playlistChannel.Text = playlistItem.Snippet.ChannelTitle;
|
||||
|
||||
playlistCounter.Text = $"{items.IndexOf(selection) + 1}/{playlistItem.ContentDetails.ItemCount}";
|
||||
}
|
||||
|
||||
playlistList.ItemsSource = items;
|
||||
playlistList.SelectedItem = selection;
|
||||
@@ -279,8 +308,15 @@ namespace FoxTube.Pages
|
||||
subscribe.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if(HistorySet.Items.Exists(i => i.Id == videoId) && HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30 && Methods.GetDuration(item.ContentDetails.Duration).TotalSeconds - HistorySet.Items.Find(i => i.Id == videoId).LeftOn.TotalSeconds >= 30)
|
||||
{
|
||||
history = HistorySet.Items.Find(i => i.Id == videoId);
|
||||
left.Visibility = Visibility.Visible;
|
||||
left.Content = $"\xE122 {resources.GetString("/VideoPage/continue")}: {history.LeftOn.ToString(@"hh\:mm\:ss")}";
|
||||
}
|
||||
|
||||
//Initializing player
|
||||
player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url);
|
||||
player.Initialize(item, item1.Snippet.Thumbnails.Medium.Url, incognito);
|
||||
|
||||
LoadRelatedVideos();
|
||||
}
|
||||
@@ -657,9 +693,35 @@ namespace FoxTube.Pages
|
||||
await playlistDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private void Wl_Click(object sender, RoutedEventArgs e)
|
||||
private async void Wl_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// TODO: Add video to WL playlist
|
||||
if (wl.IsChecked)
|
||||
{
|
||||
try
|
||||
{
|
||||
PlaylistItem playlist = new PlaylistItem
|
||||
{
|
||||
Snippet = new PlaylistItemSnippet
|
||||
{
|
||||
PlaylistId = "WL",
|
||||
ResourceId = new ResourceId
|
||||
{
|
||||
VideoId = item.Id,
|
||||
Kind = "youtube#video"
|
||||
}
|
||||
}
|
||||
};
|
||||
PlaylistItemsResource.InsertRequest request = SecretsVault.Service.PlaylistItems.Insert(playlist, "snippet");
|
||||
|
||||
await request.ExecuteAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
wl.IsChecked = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
wl.IsChecked = true;
|
||||
}
|
||||
|
||||
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
@@ -713,5 +775,10 @@ namespace FoxTube.Pages
|
||||
|
||||
Item_Click(menuItem, null);
|
||||
}
|
||||
|
||||
private void Left_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Player.Position = history.LeftOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
<data name="editorDelete.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Delete comment</value>
|
||||
</data>
|
||||
<data name="editorSubmin.Content" xml:space="preserve">
|
||||
<data name="editorSubmit.Content" xml:space="preserve">
|
||||
<value>Submit</value>
|
||||
</data>
|
||||
<data name="failedDelete" xml:space="preserve">
|
||||
|
||||
@@ -120,6 +120,18 @@
|
||||
<data name="addTo.Label" xml:space="preserve">
|
||||
<value>Add to</value>
|
||||
</data>
|
||||
<data name="appHistory.Header" xml:space="preserve">
|
||||
<value>Application</value>
|
||||
</data>
|
||||
<data name="clear.Content" xml:space="preserve">
|
||||
<value> Clear history</value>
|
||||
</data>
|
||||
<data name="hlBody.Text" xml:space="preserve">
|
||||
<value>Unfortunately, for now we are unable to manage your youtube history. If you want to see your web history go to Website tab. If you want to delete some items, please go to website</value>
|
||||
</data>
|
||||
<data name="hlHeader.Text" xml:space="preserve">
|
||||
<value>Missing some stuff?</value>
|
||||
</data>
|
||||
<data name="openWeb.Label" xml:space="preserve">
|
||||
<value>Open in browser</value>
|
||||
</data>
|
||||
@@ -132,4 +144,13 @@
|
||||
<data name="videos" xml:space="preserve">
|
||||
<value>videos</value>
|
||||
</data>
|
||||
<data name="webHistory.Header" xml:space="preserve">
|
||||
<value>Website</value>
|
||||
</data>
|
||||
<data name="wlBody.Text" xml:space="preserve">
|
||||
<value>Unfortunately, for now we are unable to delete videos from 'Watch later' playlist. If you want to do that you have to visit YouTube web page</value>
|
||||
</data>
|
||||
<data name="wlHeader.Text" xml:space="preserve">
|
||||
<value>Don't know how to delete video from 'Watch later' playlist?</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -297,4 +297,7 @@
|
||||
<data name="live.Text" xml:space="preserve">
|
||||
<value>🔴 LIVE</value>
|
||||
</data>
|
||||
<data name="continue" xml:space="preserve">
|
||||
<value>Continue from</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -141,7 +141,7 @@
|
||||
<data name="editorDelete.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Удалить комментарий</value>
|
||||
</data>
|
||||
<data name="editorSubmin.Content" xml:space="preserve">
|
||||
<data name="editorSubmit.Content" xml:space="preserve">
|
||||
<value>Отправить</value>
|
||||
</data>
|
||||
<data name="failedDelete" xml:space="preserve">
|
||||
|
||||
@@ -120,6 +120,18 @@
|
||||
<data name="addTo.Label" xml:space="preserve">
|
||||
<value>Добавить в</value>
|
||||
</data>
|
||||
<data name="appHistory.Header" xml:space="preserve">
|
||||
<value>Приложение</value>
|
||||
</data>
|
||||
<data name="clear.Content" xml:space="preserve">
|
||||
<value> Очистить историю</value>
|
||||
</data>
|
||||
<data name="hlBody.Text" xml:space="preserve">
|
||||
<value>К сожалению, пока мы не можем управлять историей просмотров. Если вам нужна история просмотров сайта, перейдите на вкладку "Сайт". Если вы хотите что-то удалить из истории, пожалуйста перейдите на веб-версию сайта</value>
|
||||
</data>
|
||||
<data name="hlHeader.Text" xml:space="preserve">
|
||||
<value>Не можете что-то найти?</value>
|
||||
</data>
|
||||
<data name="openWeb.Label" xml:space="preserve">
|
||||
<value>Открыть в браузере</value>
|
||||
</data>
|
||||
@@ -132,4 +144,13 @@
|
||||
<data name="videos" xml:space="preserve">
|
||||
<value>видео</value>
|
||||
</data>
|
||||
<data name="webHistory.Header" xml:space="preserve">
|
||||
<value>Сайт</value>
|
||||
</data>
|
||||
<data name="wlBody.Text" xml:space="preserve">
|
||||
<value>К сожалению, пока мы не можем удалять видео из этого плейлиста. Если вы хотите сделать это, вам придется перейти на веб-версию сайта</value>
|
||||
</data>
|
||||
<data name="wlHeader.Text" xml:space="preserve">
|
||||
<value>Не знаете как удалить видео из "Посмотреть позже"?</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -297,4 +297,7 @@
|
||||
<data name="live.Text" xml:space="preserve">
|
||||
<value>🔴 ПРЯМОЙ ЭФИР</value>
|
||||
</data>
|
||||
<data name="continue" xml:space="preserve">
|
||||
<value>Продолжить с</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user