支持Hi-Res无损音频格式,支持AV1编码视频格式

pull/612/head
leiurayer 2 years ago
parent 6a511ca590
commit e09b4bc0ee

@ -19,12 +19,20 @@ namespace DownKyi.Core.BiliApi.BiliUtils
new Quality { Name = "360P 流畅", Id = 16 }, new Quality { Name = "360P 流畅", Id = 16 },
}; };
private static readonly List<Quality> codecIds = new List<Quality>
{
new Quality { Name = "H.264/AVC", Id = 7 },
new Quality { Name = "H.265/HEVC", Id = 12 },
new Quality { Name = "AV1", Id = 13 },
};
private static readonly List<Quality> qualities = new List<Quality> private static readonly List<Quality> qualities = new List<Quality>
{ {
new Quality { Name = "64K", Id = 30216 }, new Quality { Name = "64K", Id = 30216 },
new Quality { Name = "132K", Id = 30232 }, new Quality { Name = "132K", Id = 30232 },
new Quality { Name = "192K", Id = 30280 }, new Quality { Name = "192K", Id = 30280 },
new Quality { Name = "Dolby Atmos", Id = 30250 }, new Quality { Name = "Dolby Atmos", Id = 30250 },
new Quality { Name = "Hi-Res无损", Id = 30251 },
}; };
/// <summary> /// <summary>
@ -39,6 +47,18 @@ namespace DownKyi.Core.BiliApi.BiliUtils
return new List<Quality>(resolutions); return new List<Quality>(resolutions);
} }
/// <summary>
/// 获取视频编码代码
/// </summary>
/// <returns></returns>
public static List<Quality> GetCodecIds()
{
// 使用深复制,
// 保证外部修改list后
// 不会影响其他调用处
return new List<Quality>(codecIds);
}
/// <summary> /// <summary>
/// 获取支持的视频音质 /// 获取支持的视频音质
/// </summary> /// </summary>

@ -18,5 +18,7 @@ namespace DownKyi.Core.BiliApi.VideoStream.Models
public List<PlayUrlDashVideo> Audio { get; set; } public List<PlayUrlDashVideo> Audio { get; set; }
[JsonProperty("dolby")] [JsonProperty("dolby")]
public PlayUrlDashDolby Dolby { get; set; } public PlayUrlDashDolby Dolby { get; set; }
[JsonProperty("flac")]
public PlayUrlDashFlac Flac { get; set; }
} }
} }

@ -0,0 +1,12 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
namespace DownKyi.Core.BiliApi.VideoStream.Models
{
public class PlayUrlDashFlac : BaseModel
{
[JsonProperty("audio")]
public PlayUrlDashVideo Audio { get; set; }
//bool display { get; set; }
}
}

@ -30,6 +30,7 @@ namespace DownKyi.Core.BiliApi.VideoStream.Models
// start_with_sap // start_with_sap
// SegmentBase // SegmentBase
// segment_base // segment_base
// codecid [JsonProperty("codecid")]
public int CodecId { get; set; }
} }
} }

@ -249,6 +249,7 @@
<Compile Include="BiliApi\VideoStream\Models\PlayUrl.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrl.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDash.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDash.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDashDolby.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDashDolby.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDashFlac.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDashVideo.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDashVideo.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDurl.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDurl.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlSupportFormat.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlSupportFormat.cs" />

@ -34,15 +34,19 @@ namespace DownKyi.Core.FFmpeg
/// <param name="destVideo"></param> /// <param name="destVideo"></param>
public static bool MergeVideo(string video1, string video2, string destVideo) public static bool MergeVideo(string video1, string video2, string destVideo)
{ {
string param = $"-y -i \"{video1}\" -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; string param = $"-y -i \"{video1}\" -i \"{video2}\" -strict -2 -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
if (video1 == null || !File.Exists(video1)) if (video1 == null || !File.Exists(video1))
{ {
param = $"-y -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; param = $"-y -i \"{video2}\" -strict -2 -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
} }
if (video2 == null || !File.Exists(video2)) if (video2 == null || !File.Exists(video2))
{ {
param = $"-y -i \"{video1}\" -acodec copy \"{destVideo}\""; param = $"-y -i \"{video1}\" -strict -2 -acodec copy \"{destVideo}\"";
} }
// 支持flac格式音频
//param += " -strict -2";
if (!File.Exists(video1) && !File.Exists(video2)) { return false; } if (!File.Exists(video1) && !File.Exists(video2)) { return false; }
// 如果存在 // 如果存在

@ -8,7 +8,7 @@ namespace DownKyi.Core.Settings.Models
/// </summary> /// </summary>
public class VideoSettings public class VideoSettings
{ {
public VideoCodecs VideoCodecs { get; set; } = VideoCodecs.NONE; // AVC or HEVC public int VideoCodecs { get; set; } = -1; // AVC or HEVC
public int Quality { get; set; } = -1; // 画质 public int Quality { get; set; } = -1; // 画质
public int AudioQuality { get; set; } = -1; // 音质 public int AudioQuality { get; set; } = -1; // 音质
public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.NONE; // 是否将flv转为mp4 public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.NONE; // 是否将flv转为mp4

@ -9,7 +9,7 @@ namespace DownKyi.Core.Settings
public partial class SettingsManager public partial class SettingsManager
{ {
// 设置优先下载的视频编码 // 设置优先下载的视频编码
private readonly VideoCodecs videoCodecs = VideoCodecs.AVC; private readonly int videoCodecs = 7;
// 设置优先下载画质 // 设置优先下载画质
private readonly int quality = 120; private readonly int quality = 120;
@ -58,10 +58,10 @@ namespace DownKyi.Core.Settings
/// 获取优先下载的视频编码 /// 获取优先下载的视频编码
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public VideoCodecs GetVideoCodecs() public int GetVideoCodecs()
{ {
appSettings = GetSettings(); appSettings = GetSettings();
if (appSettings.Video.VideoCodecs == VideoCodecs.NONE) if (appSettings.Video.VideoCodecs == -1)
{ {
// 第一次获取,先设置默认值 // 第一次获取,先设置默认值
SetVideoCodecs(videoCodecs); SetVideoCodecs(videoCodecs);
@ -75,7 +75,7 @@ namespace DownKyi.Core.Settings
/// </summary> /// </summary>
/// <param name="videoCodecs"></param> /// <param name="videoCodecs"></param>
/// <returns></returns> /// <returns></returns>
public bool SetVideoCodecs(VideoCodecs videoCodecs) public bool SetVideoCodecs(int videoCodecs)
{ {
appSettings.Video.VideoCodecs = videoCodecs; appSettings.Video.VideoCodecs = videoCodecs;
return SetSettings(); return SetSettings();

@ -1,4 +1,5 @@
using DownKyi.Core.BiliApi.VideoStream; using DownKyi.Core.BiliApi.BiliUtils;
using DownKyi.Core.BiliApi.VideoStream;
using DownKyi.Core.BiliApi.VideoStream.Models; using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Danmaku2Ass; using DownKyi.Core.Danmaku2Ass;
using DownKyi.Core.FFmpeg; using DownKyi.Core.FFmpeg;
@ -14,6 +15,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -82,6 +84,11 @@ namespace DownKyi.Services.Download
{ {
downloadAudio = downloading.PlayUrl.Dash.Dolby.Audio[0]; downloadAudio = downloading.PlayUrl.Dash.Dolby.Audio[0];
} }
// Hi-Res无损
if (downloading.AudioCodec.Id == 30251)
{
downloadAudio = downloading.PlayUrl.Dash.Flac.Audio;
}
} }
catch (Exception) { } catch (Exception) { }
@ -110,7 +117,8 @@ namespace DownKyi.Services.Download
PlayUrlDashVideo downloadVideo = null; PlayUrlDashVideo downloadVideo = null;
foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video) foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video)
{ {
if (video.Id == downloading.Resolution.Id && Utils.GetVideoCodecName(video.Codecs) == downloading.VideoCodecName) Quality codecs = Constant.GetCodecIds().FirstOrDefault(t => t.Id == video.CodecId);
if (video.Id == downloading.Resolution.Id && codecs.Name == downloading.VideoCodecName)
{ {
downloadVideo = video; downloadVideo = video;
break; break;

@ -30,7 +30,7 @@ namespace DownKyi.Services
// 获取设置 // 获取设置
UserInfoSettings userInfo = SettingsManager.GetInstance().GetUserInfo(); UserInfoSettings userInfo = SettingsManager.GetInstance().GetUserInfo();
int defaultQuality = SettingsManager.GetInstance().GetQuality(); int defaultQuality = SettingsManager.GetInstance().GetQuality();
VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
int defaultAudioQuality = SettingsManager.GetInstance().GetAudioQuality(); int defaultAudioQuality = SettingsManager.GetInstance().GetAudioQuality();
// 未登录时最高仅720P // 未登录时最高仅720P
@ -132,15 +132,16 @@ namespace DownKyi.Services
private static ObservableCollection<string> GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality) private static ObservableCollection<string> GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality)
{ {
List<string> audioQualityFormatList = new List<string>(); List<string> audioQualityFormatList = new List<string>();
List<Quality> audioQualities = Constant.GetAudioQualities();
if (playUrl.Dash.Audio != null && playUrl.Dash.Audio.Count > 0) if (playUrl.Dash.Audio != null && playUrl.Dash.Audio.Count > 0)
{ {
foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio) foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio)
{ {
// 音质id大于设置质时,跳过 // 音质id大于设置质时,跳过
if (audio.Id > defaultAudioQuality) { continue; } if (audio.Id > defaultAudioQuality) { continue; }
Quality audioQuality = Constant.GetAudioQualities().FirstOrDefault(t => { return t.Id == audio.Id; }); Quality audioQuality = audioQualities.FirstOrDefault(t => { return t.Id == audio.Id; });
if (audioQuality != null) if (audioQuality != null)
{ {
ListHelper.AddUnique(audioQualityFormatList, audioQuality.Name); ListHelper.AddUnique(audioQualityFormatList, audioQuality.Name);
@ -148,9 +149,20 @@ namespace DownKyi.Services
} }
} }
if (playUrl.Dash.Dolby.Audio != null && playUrl.Dash.Dolby.Audio.Count > 0) if (audioQualities[3].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Dolby != null)
{ {
ListHelper.AddUnique(audioQualityFormatList, Constant.GetAudioQualities().Last().Name); if (playUrl.Dash.Dolby.Audio != null && playUrl.Dash.Dolby.Audio.Count > 0)
{
ListHelper.AddUnique(audioQualityFormatList, audioQualities[3].Name);
}
}
if (audioQualities[4].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Flac != null)
{
if (playUrl.Dash.Flac.Audio != null)
{
ListHelper.AddUnique(audioQualityFormatList, audioQualities[4].Name);
}
} }
audioQualityFormatList.Sort(new StringLogicalComparer<string>()); audioQualityFormatList.Sort(new StringLogicalComparer<string>());
@ -167,9 +179,10 @@ namespace DownKyi.Services
/// <param name="userInfo"></param> /// <param name="userInfo"></param>
/// <param name="videoCodecs"></param> /// <param name="videoCodecs"></param>
/// <returns></returns> /// <returns></returns>
private static List<VideoQuality> GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs) private static List<VideoQuality> GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, int videoCodecs)
{ {
List<VideoQuality> videoQualityList = new List<VideoQuality>(); List<VideoQuality> videoQualityList = new List<VideoQuality>();
List<Quality> codeIds = Constant.GetCodecIds();
if (playUrl.Dash.Video == null) if (playUrl.Dash.Video == null)
{ {
@ -197,7 +210,8 @@ namespace DownKyi.Services
// 寻找是否已存在这个画质 // 寻找是否已存在这个画质
// 不存在则添加,存在则修改 // 不存在则添加,存在则修改
string codecName = GetVideoCodecName(video.Codecs); //string codecName = GetVideoCodecName(video.Codecs);
string codecName = codeIds.FirstOrDefault(t => t.Id == video.CodecId).Name;
VideoQuality videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id); VideoQuality videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
if (videoQualityExist == null) if (videoQualityExist == null)
{ {
@ -237,24 +251,29 @@ namespace DownKyi.Services
} }
// 设置选中的视频编码 // 设置选中的视频编码
switch (videoCodecs) //switch (videoCodecs)
//{
// case VideoCodecs.AVC:
// if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC"))
// {
// videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC";
// }
// break;
// case VideoCodecs.HEVC:
// if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC"))
// {
// videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
// }
// break;
// case VideoCodecs.NONE:
// break;
// default:
// break;
//}
string videoCodecsName = codeIds.FirstOrDefault(t => t.Id == videoCodecs).Name;
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains(videoCodecsName))
{ {
case VideoCodecs.AVC: videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = videoCodecsName;
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC"))
{
videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC";
}
break;
case VideoCodecs.HEVC:
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC"))
{
videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
}
break;
case VideoCodecs.NONE:
break;
default:
break;
} }
} }
@ -267,10 +286,10 @@ namespace DownKyi.Services
/// </summary> /// </summary>
/// <param name="origin"></param> /// <param name="origin"></param>
/// <returns></returns> /// <returns></returns>
internal static string GetVideoCodecName(string origin) //internal static string GetVideoCodecName(string origin)
{ //{
return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : ""; // return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : "";
} //}
} }
} }

@ -24,15 +24,15 @@ namespace DownKyi.ViewModels.Settings
#region 页面属性申明 #region 页面属性申明
private List<string> videoCodecs; private List<Quality> videoCodecs;
public List<string> VideoCodecs public List<Quality> VideoCodecs
{ {
get => videoCodecs; get => videoCodecs;
set => SetProperty(ref videoCodecs, value); set => SetProperty(ref videoCodecs, value);
} }
private string selectedVideoCodec; private Quality selectedVideoCodec;
public string SelectedVideoCodec public Quality SelectedVideoCodec
{ {
get => selectedVideoCodec; get => selectedVideoCodec;
set => SetProperty(ref selectedVideoCodec, value); set => SetProperty(ref selectedVideoCodec, value);
@ -186,18 +186,21 @@ namespace DownKyi.ViewModels.Settings
#region 属性初始化 #region 属性初始化
// 优先下载的视频编码 // 优先下载的视频编码
VideoCodecs = new List<string> VideoCodecs = Constant.GetCodecIds();
{ //VideoCodecs = new List<string>
"H.264/AVC", //{
"H.265/HEVC", // "H.264/AVC",
}; // "H.265/HEVC",
//};
// 优先下载画质 // 优先下载画质
VideoQualityList = Constant.GetResolutions(); VideoQualityList = Constant.GetResolutions();
// 优先下载音质 // 优先下载音质
AudioQualityList = Constant.GetAudioQualities(); AudioQualityList = Constant.GetAudioQualities();
AudioQualityList.RemoveAt(3); //AudioQualityList.RemoveAt(3);
AudioQualityList[3].Id = AudioQualityList[3].Id + 1000;
AudioQualityList[4].Id = AudioQualityList[4].Id + 1000;
// 文件命名格式 // 文件命名格式
SelectedFileName = new ObservableCollection<DisplayFileNamePart>(); SelectedFileName = new ObservableCollection<DisplayFileNamePart>();
@ -253,8 +256,9 @@ namespace DownKyi.ViewModels.Settings
isOnNavigatedTo = true; isOnNavigatedTo = true;
// 优先下载的视频编码 // 优先下载的视频编码
VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
SelectedVideoCodec = GetVideoCodecsString(videoCodecs); //SelectedVideoCodec = GetVideoCodecsString(videoCodecs);
SelectedVideoCodec = VideoCodecs.FirstOrDefault(t => { return t.Id == videoCodecs; });
// 优先下载画质 // 优先下载画质
int quality = SettingsManager.GetInstance().GetQuality(); int quality = SettingsManager.GetInstance().GetQuality();
@ -315,18 +319,20 @@ namespace DownKyi.ViewModels.Settings
#region 命令申明 #region 命令申明
// 优先下载的视频编码事件 // 优先下载的视频编码事件
private DelegateCommand<string> videoCodecsCommand; private DelegateCommand<object> videoCodecsCommand;
public DelegateCommand<string> VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand<string>(ExecuteVideoCodecsCommand)); public DelegateCommand<object> VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand<object>(ExecuteVideoCodecsCommand));
/// <summary> /// <summary>
/// 优先下载的视频编码事件 /// 优先下载的视频编码事件
/// </summary> /// </summary>
/// <param name="parameter"></param> /// <param name="parameter"></param>
private void ExecuteVideoCodecsCommand(string parameter) private void ExecuteVideoCodecsCommand(object parameter)
{ {
VideoCodecs videoCodecs = GetVideoCodecs(parameter); //VideoCodecs videoCodecs = GetVideoCodecs(parameter);
bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs); if (!(parameter is Quality videoCodecs)) { return; }
bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs.Id);
PublishTip(isSucceed); PublishTip(isSucceed);
} }
@ -674,49 +680,49 @@ namespace DownKyi.ViewModels.Settings
/// </summary> /// </summary>
/// <param name="videoCodecs"></param> /// <param name="videoCodecs"></param>
/// <returns></returns> /// <returns></returns>
private string GetVideoCodecsString(VideoCodecs videoCodecs) //private string GetVideoCodecsString(VideoCodecs videoCodecs)
{ //{
string codec; // string codec;
switch (videoCodecs) // switch (videoCodecs)
{ // {
case Core.Settings.VideoCodecs.NONE: // case Core.Settings.VideoCodecs.NONE:
codec = ""; // codec = "";
break; // break;
case Core.Settings.VideoCodecs.AVC: // case Core.Settings.VideoCodecs.AVC:
codec = "H.264/AVC"; // codec = "H.264/AVC";
break; // break;
case Core.Settings.VideoCodecs.HEVC: // case Core.Settings.VideoCodecs.HEVC:
codec = "H.265/HEVC"; // codec = "H.265/HEVC";
break; // break;
default: // default:
codec = ""; // codec = "";
break; // break;
} // }
return codec; // return codec;
} //}
/// <summary> /// <summary>
/// 返回VideoCodecs /// 返回VideoCodecs
/// </summary> /// </summary>
/// <param name="str"></param> /// <param name="str"></param>
/// <returns></returns> /// <returns></returns>
private VideoCodecs GetVideoCodecs(string str) //private VideoCodecs GetVideoCodecs(string str)
{ //{
VideoCodecs videoCodecs; // VideoCodecs videoCodecs;
switch (str) // switch (str)
{ // {
case "H.264/AVC": // case "H.264/AVC":
videoCodecs = Core.Settings.VideoCodecs.AVC; // videoCodecs = Core.Settings.VideoCodecs.AVC;
break; // break;
case "H.265/HEVC": // case "H.265/HEVC":
videoCodecs = Core.Settings.VideoCodecs.HEVC; // videoCodecs = Core.Settings.VideoCodecs.HEVC;
break; // break;
default: // default:
videoCodecs = Core.Settings.VideoCodecs.NONE; // videoCodecs = Core.Settings.VideoCodecs.NONE;
break; // break;
} // }
return videoCodecs; // return videoCodecs;
} //}
/// <summary> /// <summary>
/// 保存下载视频内容到设置 /// 保存下载视频内容到设置

@ -23,6 +23,7 @@
Name="nameVideoCodecs" Name="nameVideoCodecs"
Width="120" Width="120"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
DisplayMemberPath="Name"
ItemsSource="{Binding VideoCodecs}" ItemsSource="{Binding VideoCodecs}"
SelectedValue="{Binding SelectedVideoCodec}"> SelectedValue="{Binding SelectedVideoCodec}">
<i:Interaction.Triggers> <i:Interaction.Triggers>

@ -27,10 +27,10 @@
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{DynamicResource VideoFilePath}" /> <TextBlock VerticalAlignment="Center" Text="{DynamicResource VideoFilePath}" />
<TextBox <TextBox
Width="300" Width="600"
Height="50" Height="65"
Margin="0,10,10,10" Margin="0,10,10,10"
VerticalContentAlignment="Center" VerticalContentAlignment="Top"
IsReadOnly="True" IsReadOnly="True"
Text="{Binding VideoPathsStr, Mode=TwoWay}" /> Text="{Binding VideoPathsStr, Mode=TwoWay}" />

Loading…
Cancel
Save