diff --git a/FoxTube/Assets/Data/Patchnotes.xml b/FoxTube/Assets/Data/Patchnotes.xml
index b9ee5f3..9c72cdf 100644
--- a/FoxTube/Assets/Data/Patchnotes.xml
+++ b/FoxTube/Assets/Data/Patchnotes.xml
@@ -6,7 +6,7 @@
- Fixed fails when trying to retrieve history, WL or recommended
- Fixed ads appearance
- Fixed ads watermarks on video when it was opened through notification
-- Fixed videos loading
+- Optimized and enchanced video playback
- Fixed special characters appearing in toast notifications
- History page re-design
- Added app history management (doesn't affect web site's history)
@@ -17,12 +17,14 @@
- Added incognito mode (available in video card context menu)
- Search suggestions now run smoother
- FoxTube pro price is now displayed in menu
+- Fixed crashes on opening links which don't contain http(s) prefix
+- Fixed backward navigation with minimized video
### Что нового:
- Исправлена проблема получения истории, "Посмотреть позже" и рекомендаций
- Исправлен внешний вид рекламы
- Исправлено появление водяных занков рекламы на видео при открытии через уведомления
-- Исправлена загрузка видео
+- Оптимизирован и улучшен просмотр видео
- Исправлено появление особых символов в уведомлениях
- Редизайн страницы истории
- Добавлено управление историей просмотра приложения (не влияет на историю просмотров на сайте)
@@ -33,6 +35,8 @@
- Добавлен режим инкогнито (доступен в контекстном меню видео карточки)
- Подсказки при поиске работают плавнее
- Теперь на кнопке отключения рекламы отображается текущая цена
+- Исправлены вылеты при попытке открыть ссылку не содержащую http(s) префикс
+- Исправлена обратная навигация при уменьшенном видео
diff --git a/FoxTube/Classes/ManifestGenerator.cs b/FoxTube/Classes/ManifestGenerator.cs
new file mode 100644
index 0000000..53f5641
--- /dev/null
+++ b/FoxTube/Classes/ManifestGenerator.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Threading.Tasks;
+using YoutubeExplode.Models.MediaStreams;
+using Windows.Storage;
+using Google.Apis.YouTube.v3.Data;
+using System.Xml;
+using System.IO;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace FoxTube.Controls.Player
+{
+ public static class ManifestGenerator
+ {
+ static StorageFolder roaming = ApplicationData.Current.RoamingFolder;
+ public static async Task GetManifest(Video meta, VideoStreamInfo requestedQuality, MediaStreamInfoSet list)
+ {
+ StorageFile manifest = await roaming.CreateFileAsync("manifest.mpd", CreationCollisionOption.ReplaceExisting);
+
+ XmlDocument doc = new XmlDocument();
+
+ XmlElement mpd = doc.CreateElement("MPD");
+ mpd.SetAttribute("mediaPresentationDuration", meta.ContentDetails.Duration);
+
+ XmlElement period = doc.CreateElement("Period");
+ period.SetAttribute("duration", meta.ContentDetails.Duration);
+ period.SetAttribute("start", "PT0S");
+
+ XmlElement videoSet = doc.CreateElement("AdaptationSet");
+ XmlElement videoMeta = doc.CreateElement("ContentComponent");
+ videoMeta.SetAttribute("contentType", "video");
+ videoSet.AppendChild(videoMeta);
+
+ AppendVideoSet(doc, videoSet, list.Video);
+
+ XmlElement audioSet = doc.CreateElement("AdaptationSet");
+ XmlElement audioMeta = doc.CreateElement("ContentComponent");
+ audioMeta.SetAttribute("contentType", "audio");
+ audioSet.AppendChild(audioMeta);
+
+ XmlElement audio = doc.CreateElement("Representation");
+ audio.SetAttribute("bandwidth", "100000");
+ audio.SetAttribute("id", (list.Video.Count + 1).ToString());
+ audio.SetAttribute("mimeType", $"audio/{list.Audio.First().Container.GetFileExtension()}");
+
+ XmlElement audioUrl = doc.CreateElement("BaseURL");
+ audioUrl.InnerText = list.Audio.First().Url;
+
+ audio.AppendChild(audioUrl);
+ audioSet.AppendChild(audio);
+
+ doc.AppendChild(mpd);
+ mpd.AppendChild(period);
+ period.AppendChild(videoSet);
+ period.AppendChild(audioSet);
+
+ doc.Save(await manifest.OpenStreamForWriteAsync());
+
+ return "ms-appdata:///roaming/manifest.mpd".ToUri();
+ }
+
+ private static void AppendVideoSet(XmlDocument doc, XmlElement root, IReadOnlyList list)
+ {
+ for (int k = 0; k < list.Count; k++)
+ {
+ XmlElement representation = doc.CreateElement("Representation");
+ representation.SetAttribute("bandwidth", GetBandwidth(list[k].VideoQuality));
+ representation.SetAttribute("height", list[k].Resolution.Height.ToString());
+ representation.SetAttribute("width", list[k].Resolution.Width.ToString());
+ representation.SetAttribute("id", (k + 1).ToString());
+ representation.SetAttribute("mimeType", $"video/{list[k].Container.GetFileExtension()}");
+
+ XmlElement baseUrl = doc.CreateElement("BaseURL");
+ baseUrl.InnerText = list[k].Url;
+ representation.AppendChild(baseUrl);
+
+ XmlElement segmentBase = doc.CreateElement("SegmentBase");
+ representation.AppendChild(segmentBase);
+
+ XmlElement initialization = doc.CreateElement("Initialization");
+ initialization.SetAttribute("range", "0-1000");
+ segmentBase.AppendChild(initialization);
+
+ root.AppendChild(representation);
+ }
+ }
+
+ private static string GetBandwidth(VideoQuality quality)
+ {
+ switch(quality)
+ {
+ case VideoQuality.High4320:
+ return $"16763040";
+ case VideoQuality.High3072:
+ return $"11920384";
+ case VideoQuality.High2880:
+ return $"11175360";
+ case VideoQuality.High2160:
+ return $"8381520";
+ case VideoQuality.High1440:
+ return $"5587680";
+ case VideoQuality.High1080:
+ return $"4190760";
+ case VideoQuality.High720:
+ return $"2073921";
+ case VideoQuality.Medium480:
+ return $"869460";
+ case VideoQuality.Medium360:
+ return $"686521";
+ case VideoQuality.Low240:
+ return $"264835";
+ case VideoQuality.Low144:
+ default:
+ return $"100000";
+ }
+ }
+ }
+}
diff --git a/FoxTube/Classes/Methods.cs b/FoxTube/Classes/Methods.cs
index 559aa29..32a5349 100644
--- a/FoxTube/Classes/Methods.cs
+++ b/FoxTube/Classes/Methods.cs
@@ -214,8 +214,12 @@ namespace FoxTube
{
if (link.IsMatch(item))
{
+ string str = item;
+ if (!str.Contains("http"))
+ str = str.Insert(0, "http://");
+
Hyperlink hl = new Hyperlink();
- hl.Click += (s, arg) => ProcessLink(item);
+ hl.Click += (s, arg) => ProcessLink(str);
hl.Inlines.Add(new Run { Text = item });
block.Inlines.Add(hl);
}
diff --git a/FoxTube/Classes/VideoProcessor.cs b/FoxTube/Classes/VideoProcessor.cs
deleted file mode 100644
index c5a976e..0000000
--- a/FoxTube/Classes/VideoProcessor.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using YoutubeExplode.Models.MediaStreams;
-using Windows.Media.Editing;
-using Windows.Media.Core;
-using Windows.Storage;
-using Windows.UI.Xaml.Controls;
-using System.Net;
-using YoutubeExplode;
-
-namespace FoxTube.Classes
-{
- public class VideoProcessor
- {
- public MediaElement Player { get; set; }
- MediaComposition composition = new MediaComposition();
-
- StorageFolder roaming = ApplicationData.Current.RoamingFolder;
- StorageFile audioCache;
- StorageFile videoCache;
-
- MediaStream videoStream;
- MediaStream audioStream;
-
- YoutubeClient client = new YoutubeClient(SecretsVault.HttpClient);
-
- public VideoProcessor()
- {
- //Player.CurrentStateChanged += Player_CurrentStateChanged;
- }
-
- public async Task GetStream(VideoStreamInfo video, AudioStreamInfo audio)
- {
- audioCache = await roaming.CreateFileAsync("audioCache.mp4", CreationCollisionOption.ReplaceExisting);
- videoCache = await roaming.CreateFileAsync("videoCache.mp4", CreationCollisionOption.ReplaceExisting);
-
- videoStream = await client.GetMediaStreamAsync(video);
- audioStream = await client.GetMediaStreamAsync(audio);
-
- /*Stream write = await videoCache.OpenStreamForWriteAsync();
- write.
- write.WriteAsync()*/
-
- videoStream.BeginRead(new byte[300 * video.Bitrate], (int)((int)Player.Position.TotalSeconds * video.Bitrate), (int)((int)Player.Position.Add(TimeSpan.FromMinutes(5)).TotalSeconds * video.Bitrate - (int)Player.Position.TotalSeconds * video.Bitrate), null, null);
-
- composition.BackgroundAudioTracks.Add(await BackgroundAudioTrack.CreateFromFileAsync(audioCache));
- composition.Clips.Add(await MediaClip.CreateFromFileAsync(videoCache));
-
- return composition.GenerateMediaStreamSource();
- }
-
- public async void Close()
- {
- await audioCache.DeleteAsync(StorageDeleteOption.PermanentDelete);
- await videoCache.DeleteAsync(StorageDeleteOption.PermanentDelete);
- }
-
- private void Player_CurrentStateChanged(object sender, Windows.UI.Xaml.RoutedEventArgs e)
- {
- throw new NotImplementedException();
- }
-
-
- }
-}
diff --git a/FoxTube/Controls/LiveCaptions.xaml.cs b/FoxTube/Controls/LiveCaptions.xaml.cs
index 108213c..734d2cb 100644
--- a/FoxTube/Controls/LiveCaptions.xaml.cs
+++ b/FoxTube/Controls/LiveCaptions.xaml.cs
@@ -1,4 +1,5 @@
using System;
+using Windows.Media.Playback;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using YoutubeExplode.Models.ClosedCaptions;
@@ -16,7 +17,7 @@ namespace FoxTube.Controls
set => text.FontSize = value;
}
- public MediaElement Player { get; set; }
+ public MediaPlayer Player { get; set; }
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(10) };
ClosedCaption currentCaption = null;
@@ -31,7 +32,7 @@ namespace FoxTube.Controls
private void UpdateCaption(object sender, object e)
{
- currentCaption = track.Captions.Find(i => i.Offset <= Player.Position && i.Offset + i.Duration > Player.Position);
+ currentCaption = track.Captions.Find(i => i.Offset <= Player.PlaybackSession.Position && i.Offset + i.Duration > Player.PlaybackSession.Position);
if (currentCaption != null)
text.Text = currentCaption.Text;
diff --git a/FoxTube/Controls/Player/PlayerControls.cs b/FoxTube/Controls/Player/PlayerControls.cs
index 5d249d5..47ca80e 100644
--- a/FoxTube/Controls/Player/PlayerControls.cs
+++ b/FoxTube/Controls/Player/PlayerControls.cs
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Windows.ApplicationModel.Resources;
+using Windows.Media.Core;
+using Windows.Media.Playback;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
@@ -29,7 +31,7 @@ namespace FoxTube
public event QualityChangedEventHandler QualityChanged;
- public MediaElement Player;
+ public MediaPlayer Player;
public PlayerAdvert Advert;
public IReadOnlyList ClosedCaptions { get; set; }
@@ -347,7 +349,7 @@ namespace FoxTube
(GetTemplateChild("cc") as Button).Visibility = Visibility.Collapsed;
(GetTemplateChild("quality") as Button).Visibility = Visibility.Collapsed;
- Player.Source = url.ToUri();
+ Player.Source = MediaSource.CreateFromUri(url.ToUri());
}
}
}
diff --git a/FoxTube/Controls/Player/VideoPlayer.xaml b/FoxTube/Controls/Player/VideoPlayer.xaml
index fd0fc73..d0849ca 100644
--- a/FoxTube/Controls/Player/VideoPlayer.xaml
+++ b/FoxTube/Controls/Player/VideoPlayer.xaml
@@ -11,13 +11,13 @@
RequestedTheme="Dark">
-
-
+
+
-
-
+
+
diff --git a/FoxTube/Controls/Player/VideoPlayer.xaml.cs b/FoxTube/Controls/Player/VideoPlayer.xaml.cs
index 7a823a9..63c2ea0 100644
--- a/FoxTube/Controls/Player/VideoPlayer.xaml.cs
+++ b/FoxTube/Controls/Player/VideoPlayer.xaml.cs
@@ -11,6 +11,14 @@ using YoutubeExplode;
using System.IO;
using FoxTube.Classes;
using Windows.Media.Core;
+using System.Linq;
+using Windows.Media.Playback;
+using System.Threading.Tasks;
+using Windows.Media.Editing;
+using Windows.Storage.Pickers;
+using Windows.Storage;
+using Windows.Media.MediaProperties;
+using FoxTube.Controls.Player;
namespace FoxTube
{
@@ -23,17 +31,17 @@ namespace FoxTube
public event Event NextClicked;
public event ObjectEventHandler MiniMode;
+
public PlayerControls Controls => videoSource.TransportControls as PlayerControls;
+ public MediaPlayer Player { get; } = new MediaPlayer();
public TimeSpan Position
{
- get { return videoSource.Position; }
- set { videoSource.Position = value; }
+ get { return videoSource.MediaPlayer.PlaybackSession.Position; }
+ set { videoSource.MediaPlayer.PlaybackSession.Position = value; }
}
- VideoProcessor processor;
-
- TimeSpan timecodeBackup;
- bool needUpdateTimecode = false;
+ //TimeSpan timecodeBackup;
+ //bool needUpdateTimecode = false;
SystemMediaTransportControls systemControls;
@@ -58,11 +66,12 @@ namespace FoxTube
if (item.Snippet.LiveBroadcastContent == "none")
{
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) });
+ // TODO: make ads live again
+ /*if (Methods.GetDuration(item.ContentDetails.Duration).TotalMinutes > 5)
+ Player.PlaybackSession.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) });
+ 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));
@@ -87,22 +96,23 @@ namespace FoxTube
private void Controls_LiveRequested(object sender, RoutedEventArgs e)
{
- videoSource.Position = videoSource.NaturalDuration.TimeSpan;
+ Position = Player.PlaybackSession.NaturalDuration;
}
public void InitializeContols()
{
- processor = new VideoProcessor
- {
- Player = videoSource
- };
- videoSource.Volume = SettingsStorage.Volume;
+ videoSource.SetMediaPlayer(Player);
+ Player.Volume = SettingsStorage.Volume;
+
+ Player.CurrentStateChanged += VideoSource_CurrentStateChanged;
+ Player.MediaOpened += VideoSource_MediaOpened;
+ Player.VolumeChanged += VideoSource_VolumeChanged;
Controls.CloseRequested += Controls_CloseRequested;
Controls.NextRequested += (s, e) => NextClicked?.Invoke();
Controls.QualityChanged += Controls_QualityChanged;
Controls.MiniModeChanged += Controls_MiniModeChanged;
- Controls.Player = videoSource;
+ Controls.Player = Player;
#region System Media Transport Controls
systemControls = SystemMediaTransportControls.GetForCurrentView();
@@ -135,16 +145,23 @@ namespace FoxTube
private async void Controls_QualityChanged(object sender, MediaStreamInfo requestedQuality, MediaStreamInfoSet list)
{
- videoSource.Pause();
+ Player.Pause();
- timecodeBackup = videoSource.Position;
+ Player.Source = MediaSource.CreateFromUri(await ManifestGenerator.GetManifest(item, requestedQuality as VideoStreamInfo, list));
+
+ //await ManifestGenerator.GetManifest(item, requestedQuality as VideoStreamInfo, list);
+ /*FileOpenPicker picker = new FileOpenPicker();
+ picker.FileTypeFilter.Add(".mpd");*/
+
+ //Player.Source = MediaSource.CreateFromUri("https://foxsharp.000webhostapp.com/dash_sample.mpd".ToUri());
+
+ /*timecodeBackup = videoSource.Position;
needUpdateTimecode = true;
if (requestedQuality is MuxedStreamInfo)
videoSource.Source = requestedQuality.Url.ToUri();
else
- videoSource.SetPlaybackSource(MediaSource.CreateFromStream((await new YoutubeClient().GetMediaStreamAsync(requestedQuality)).AsRandomAccessStream(), "video"));
- //videoSource.SetMediaStreamSource(await processor.GetStream(requestedQuality as VideoStreamInfo, list.Audio.First()));
+ processor.Start(requestedQuality as VideoStreamInfo, list);*/
}
public void Controls_CloseRequested(object sender, RoutedEventArgs e)
@@ -154,12 +171,10 @@ namespace FoxTube
if (!incognito)
{
- history.LeftOn = videoSource.Position;
+ history.LeftOn = Player.PlaybackSession.Position;
HistorySet.Update(history);
}
- videoSource.Stop();
-
Methods.MainPage.CloseVideo();
}
@@ -170,10 +185,10 @@ namespace FoxTube
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
- videoSource.Play();
+ Player.Play();
break;
case SystemMediaTransportControlsButton.Pause:
- videoSource.Pause();
+ Player.Pause();
break;
case SystemMediaTransportControlsButton.Next:
NextClicked?.Invoke();
@@ -184,40 +199,42 @@ namespace FoxTube
public void Pause()
{
- videoSource.Pause();
+ Player.Pause();
}
- private void VideoSource_CurrentStateChanged(object sender, RoutedEventArgs e)
+ private void VideoSource_CurrentStateChanged(MediaPlayer sender, object e)
{
- switch(videoSource.CurrentState)
+ switch (Player.PlaybackSession.PlaybackState)
{
- case Windows.UI.Xaml.Media.MediaElementState.Buffering:
- case Windows.UI.Xaml.Media.MediaElementState.Paused:
+ case MediaPlaybackState.Buffering:
+ case MediaPlaybackState.None:
+ case MediaPlaybackState.Opening:
+ case MediaPlaybackState.Paused:
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
- if(!incognito)
+ if (!incognito)
{
- history.LeftOn = videoSource.Position;
+ history.LeftOn = Player.PlaybackSession.Position;
HistorySet.Update(history);
}
break;
- case Windows.UI.Xaml.Media.MediaElementState.Playing:
+ case MediaPlaybackState.Playing:
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
}
}
- private void VideoSource_MediaOpened(object sender, RoutedEventArgs e)
+ private void VideoSource_MediaOpened(MediaPlayer sender, object e)
{
- if (!needUpdateTimecode)
+ /*if (!needUpdateTimecode)
return;
videoSource.Position = timecodeBackup;
- needUpdateTimecode = false;
+ needUpdateTimecode = false;*/
}
- private void VideoSource_VolumeChanged(object sender, RoutedEventArgs e)
+ private void VideoSource_VolumeChanged(MediaPlayer sender, object e)
{
- SettingsStorage.Volume = videoSource.Volume;
+ SettingsStorage.Volume = Player.Volume;
}
private void VideoSource_MarkerReached(object sender, Windows.UI.Xaml.Media.TimelineMarkerRoutedEventArgs e)
diff --git a/FoxTube/FoxTube.csproj b/FoxTube/FoxTube.csproj
index 71a18b3..154879a 100644
--- a/FoxTube/FoxTube.csproj
+++ b/FoxTube/FoxTube.csproj
@@ -107,7 +107,7 @@
-
+
Advert.xaml
diff --git a/FoxTube/Pages/MainPage.xaml b/FoxTube/Pages/MainPage.xaml
index 2c69d66..42e31fd 100644
--- a/FoxTube/Pages/MainPage.xaml
+++ b/FoxTube/Pages/MainPage.xaml
@@ -120,47 +120,12 @@
-
-
-
diff --git a/FoxTube/Pages/MainPage.xaml.cs b/FoxTube/Pages/MainPage.xaml.cs
index 1fa2b8d..90ecc1b 100644
--- a/FoxTube/Pages/MainPage.xaml.cs
+++ b/FoxTube/Pages/MainPage.xaml.cs
@@ -591,7 +591,7 @@ namespace FoxTube
private void Nav_BackRequested(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewBackRequestedEventArgs args)
{
- if (videoPlaceholder.Content != null)
+ if (videoPlaceholder.Content != null && videoPlaceholder.Width == double.NaN)
{
if ((videoPlaceholder.Content as VideoPage).LoadingPage.State != LoadingState.Loaded)
CloseVideo();