mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-22 03:02:35 +01:00
Working on removing obsolete WebClient
This commit is contained in:
parent
e98179fbae
commit
959e105924
43
build.bat
43
build.bat
@ -176,27 +176,28 @@ IF NOT EXIST "temp_zip\Tesseract302" MD "temp_zip\Tesseract302"
|
||||
IF NOT EXIST "temp_zip\Icons" MD "temp_zip\Icons"
|
||||
|
||||
ECHO.
|
||||
COPY /Y /V "..\..\..\..\LICENSE.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\Changelog.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\preview.mkv" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx86.dll" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx64.dll" "temp_zip\"
|
||||
COPY /Y /V "libse.dll" "temp_zip\"
|
||||
COPY /Y /V "zlib.net.dll" "temp_zip\"
|
||||
COPY /Y /V "NHunspell.dll" "temp_zip\"
|
||||
COPY /Y /V "UtfUnknown.dll" "temp_zip\"
|
||||
COPY /Y /V "Vosk.dll" "temp_zip\"
|
||||
COPY /Y /V "NCalc.dll" "temp_zip\"
|
||||
COPY /Y /V "..\..\DLLs\Interop.QuartzTypeLib.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Extensions.dll" "temp_zip\"
|
||||
COPY /Y /V "Newtonsoft.Json.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Primitives.dll" "temp_zip\"
|
||||
COPY /Y /V "SubtitleEdit.exe" "temp_zip\"
|
||||
COPY /Y /V "Languages\*.xml" "temp_zip\Languages\"
|
||||
COPY /Y /V "..\..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
|
||||
COPY /Y /V "..\..\..\..\Ocr\*.*" "temp_zip\Ocr\"
|
||||
XCOPY /Y /V "..\..\..\..\Tesseract302\*.*" "temp_zip\Tesseract302\" /S
|
||||
COPY /Y /V "..\..\..\..\Icons\*.ico" "temp_zip\Icons\"
|
||||
COPY /Y /V "..\..\..\..\LICENSE.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\Changelog.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\preview.mkv" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx86.dll" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx64.dll" "temp_zip\"
|
||||
COPY /Y /V "libse.dll" "temp_zip\"
|
||||
COPY /Y /V "zlib.net.dll" "temp_zip\"
|
||||
COPY /Y /V "NHunspell.dll" "temp_zip\"
|
||||
COPY /Y /V "UtfUnknown.dll" "temp_zip\"
|
||||
COPY /Y /V "Vosk.dll" "temp_zip\"
|
||||
COPY /Y /V "NCalc.dll" "temp_zip\"
|
||||
COPY /Y /V "..\..\DLLs\Interop.QuartzTypeLib.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Extensions.dll" "temp_zip\"
|
||||
COPY /Y /V "Newtonsoft.Json.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Primitives.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.dll" "temp_zip\"
|
||||
COPY /Y /V "SubtitleEdit.exe" "temp_zip\"
|
||||
COPY /Y /V "Languages\*.xml" "temp_zip\Languages\"
|
||||
COPY /Y /V "..\..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
|
||||
COPY /Y /V "..\..\..\..\Ocr\*.*" "temp_zip\Ocr\"
|
||||
XCOPY /Y /V "..\..\..\..\Tesseract302\*.*" "temp_zip\Tesseract302\" /S
|
||||
COPY /Y /V "..\..\..\..\Icons\*.ico" "temp_zip\Icons\"
|
||||
|
||||
PUSHD "temp_zip"
|
||||
START "" /B /WAIT "%SEVENZIP%" a -tzip -mx=9 "SubtitleEdit-%VERSION%.zip" * >NUL
|
||||
|
@ -164,27 +164,28 @@ IF NOT EXIST "temp_zip\Tesseract302" MD "temp_zip\Tesseract302"
|
||||
IF NOT EXIST "temp_zip\Icons" MD "temp_zip\Icons"
|
||||
|
||||
ECHO.
|
||||
COPY /Y /V "..\..\..\..\LICENSE.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\Changelog.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\preview.mkv" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx86.dll" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx64.dll" "temp_zip\"
|
||||
COPY /Y /V "libse.dll" "temp_zip\"
|
||||
COPY /Y /V "zlib.net.dll" "temp_zip\"
|
||||
COPY /Y /V "NHunspell.dll" "temp_zip\"
|
||||
COPY /Y /V "UtfUnknown.dll" "temp_zip\"
|
||||
COPY /Y /V "Vosk.dll" "temp_zip\"
|
||||
COPY /Y /V "NCalc.dll" "temp_zip\"
|
||||
COPY /Y /V "..\..\DLLs\Interop.QuartzTypeLib.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Extensions.dll" "temp_zip\"
|
||||
COPY /Y /V "Newtonsoft.Json.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Primitives.dll" "temp_zip\"
|
||||
COPY /Y /V "SubtitleEdit.exe" "temp_zip\"
|
||||
COPY /Y /V "Languages\*.xml" "temp_zip\Languages\"
|
||||
COPY /Y /V "..\..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
|
||||
COPY /Y /V "..\..\..\..\Ocr\*.*" "temp_zip\Ocr\"
|
||||
XCOPY /Y /V "..\..\..\..\Tesseract302\*.*" "temp_zip\Tesseract302\" /S
|
||||
COPY /Y /V "..\..\..\..\Icons\*.ico" "temp_zip\Icons\"
|
||||
COPY /Y /V "..\..\..\..\LICENSE.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\Changelog.txt" "temp_zip\"
|
||||
COPY /Y /V "..\..\..\..\preview.mkv" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx86.dll" "temp_zip\"
|
||||
COPY /Y /V "Hunspellx64.dll" "temp_zip\"
|
||||
COPY /Y /V "libse.dll" "temp_zip\"
|
||||
COPY /Y /V "zlib.net.dll" "temp_zip\"
|
||||
COPY /Y /V "NHunspell.dll" "temp_zip\"
|
||||
COPY /Y /V "UtfUnknown.dll" "temp_zip\"
|
||||
COPY /Y /V "Vosk.dll" "temp_zip\"
|
||||
COPY /Y /V "NCalc.dll" "temp_zip\"
|
||||
COPY /Y /V "..\..\DLLs\Interop.QuartzTypeLib.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Extensions.dll" "temp_zip\"
|
||||
COPY /Y /V "Newtonsoft.Json.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.Primitives.dll" "temp_zip\"
|
||||
COPY /Y /V "System.Net.Http.dll" "temp_zip\"
|
||||
COPY /Y /V "SubtitleEdit.exe" "temp_zip\"
|
||||
COPY /Y /V "Languages\*.xml" "temp_zip\Languages\"
|
||||
COPY /Y /V "..\..\..\..\Dictionaries\*.*" "temp_zip\Dictionaries\"
|
||||
COPY /Y /V "..\..\..\..\Ocr\*.*" "temp_zip\Ocr\"
|
||||
XCOPY /Y /V "..\..\..\..\Tesseract302\*.*" "temp_zip\Tesseract302\" /S
|
||||
COPY /Y /V "..\..\..\..\Icons\*.ico" "temp_zip\Icons\"
|
||||
|
||||
PUSHD "temp_zip"
|
||||
START "" /B /WAIT "%SEVENZIP%" a -tzip -mx=9 "SubtitleEditBeta.zip" * >NUL
|
||||
|
@ -312,6 +312,7 @@ Source: ..\src\ui\DLLs\Interop.QuartzTypeLib.dll; DestDir: {app};
|
||||
Source: {#bindir}\Newtonsoft.Json.dll; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: {#bindir}\System.Net.Http.Extensions.dll; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: {#bindir}\System.Net.Http.Primitives.dll; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: {#bindir}\System.Net.Http.dll; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: ..\Changelog.txt; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: ..\LICENSE.txt; DestDir: {app}; Flags: ignoreversion; Components: main
|
||||
Source: Icons\uninstall.ico; DestDir: {app}\Icons; Flags: ignoreversion; Components: main
|
||||
@ -351,6 +352,7 @@ Type: files; Name: {app}\Interop.QuartzTypeLib.dll; Check: IsU
|
||||
Type: files; Name: {app}\Newtonsoft.Json.dll; Check: IsUpgrade()
|
||||
Type: files; Name: {app}\System.Net.Http.Extensions.dll; Check: IsUpgrade()
|
||||
Type: files; Name: {app}\System.Net.Http.Primitives.dll; Check: IsUpgrade()
|
||||
Type: files; Name: {app}\System.Net.Http.dll; Check: IsUpgrade()
|
||||
|
||||
; Remove old files from the {app} dir
|
||||
Type: files; Name: {app}\Dictionaries\da_names.xml; Check: IsUpgrade()
|
||||
|
93
src/libse/Common/HttpClientHelper.cs
Normal file
93
src/libse/Common/HttpClientHelper.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Core.Common
|
||||
{
|
||||
public static class HttpClientHelper
|
||||
{
|
||||
public static HttpClient MakeHttpClient()
|
||||
{
|
||||
return new HttpClient(HttpClientHelper.GetHttpClientHandler(Configuration.Settings.Proxy));
|
||||
}
|
||||
|
||||
public static HttpClientHandler GetHttpClientHandler(ProxySettings proxySettings)
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
|
||||
if (!string.IsNullOrEmpty(proxySettings.ProxyAddress))
|
||||
{
|
||||
handler.Proxy = new WebProxy(proxySettings.ProxyAddress);
|
||||
handler.UseProxy = true;
|
||||
}
|
||||
|
||||
if (proxySettings.UseDefaultCredentials)
|
||||
{
|
||||
handler.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
|
||||
handler.Credentials = CredentialCache.DefaultNetworkCredentials;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(proxySettings.UserName) && !string.IsNullOrEmpty(proxySettings.ProxyAddress))
|
||||
{
|
||||
var credentialCache = new CredentialCache { { new Uri(proxySettings.ProxyAddress), proxySettings.AuthType, new NetworkCredential(proxySettings.UserName, proxySettings.Password) } };
|
||||
handler.Credentials = credentialCache;
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<float> progress = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using (var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken))
|
||||
{
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
|
||||
using (var downloadStream = await response.Content.ReadAsStreamAsync())
|
||||
{
|
||||
if (progress == null || !contentLength.HasValue)
|
||||
{
|
||||
await downloadStream.CopyToAsync(destination);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert absolute progress (bytes downloaded) into relative progress (0% - 100%)
|
||||
var relativeProgress = new Progress<long>(totalBytes => progress.Report((float)totalBytes / contentLength.Value));
|
||||
// Use extension method to report progress while downloading
|
||||
await CopyToAsync(downloadStream, destination, 81920, relativeProgress, cancellationToken);
|
||||
progress.Report(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CopyToAsync(Stream source, Stream destination, int bufferSize, IProgress<long> progress = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (destination == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(destination));
|
||||
}
|
||||
|
||||
if (bufferSize < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||||
}
|
||||
|
||||
var buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report(totalBytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -836,6 +836,8 @@ $HorzAlign = Center
|
||||
public class ProxySettings
|
||||
{
|
||||
public string ProxyAddress { get; set; }
|
||||
public string AuthType { get; set; }
|
||||
public bool UseDefaultCredentials { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Domain { get; set; }
|
||||
|
@ -56,6 +56,7 @@ SE 3.6.7
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="UTF.Unknown" Version="2.5.1" />
|
||||
<PackageReference Include="zlib.net-mutliplatform" Version="1.0.6" />
|
||||
</ItemGroup>
|
||||
|
@ -4,7 +4,7 @@ using Nikse.SubtitleEdit.Logic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Forms
|
||||
@ -12,6 +12,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
public sealed partial class AudioToTextModelDownload : Form
|
||||
{
|
||||
public bool AutoClose { get; internal set; }
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public AudioToTextModelDownload()
|
||||
{
|
||||
@ -24,7 +25,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
UiUtil.FixLargeFonts(this, buttonDownload);
|
||||
|
||||
var selectedIndex = 0;
|
||||
foreach (var downloadModel in DownloadModel.VoskModels.OrderBy(p=>p.LanguageName))
|
||||
foreach (var downloadModel in DownloadModel.VoskModels.OrderBy(p => p.LanguageName))
|
||||
{
|
||||
comboBoxModels.Items.Add(downloadModel);
|
||||
if (selectedIndex == 0 && downloadModel.TwoLetterLanguageCode == "en")
|
||||
@ -35,8 +36,8 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
comboBoxModels.SelectedIndex = selectedIndex;
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
|
||||
textBoxError.Visible = false;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void AudioToTextDownload_KeyDown(object sender, KeyEventArgs e)
|
||||
@ -55,63 +56,69 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
}
|
||||
|
||||
var downloadModel = (DownloadModel)comboBoxModels.Items[comboBoxModels.SelectedIndex];
|
||||
var url = downloadModel.Url;
|
||||
try
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait;
|
||||
buttonDownload.Enabled = false;
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
var url = downloadModel.Url;
|
||||
var wc = new WebClient { Proxy = Utilities.GetProxy() };
|
||||
|
||||
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
|
||||
wc.DownloadProgressChanged += (o, args) =>
|
||||
var httpClient = HttpClientHelper.MakeHttpClient();
|
||||
using (var downloadStream = new MemoryStream())
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + args.ProgressPercentage + "%";
|
||||
};
|
||||
wc.DownloadDataAsync(new Uri(url));
|
||||
var downloadTask = httpClient.DownloadAsync(url, downloadStream, new Progress<float>((progress) =>
|
||||
{
|
||||
var pct = (int)Math.Round(progress * 100.0, MidpointRounding.AwayFromZero);
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + pct + "%";
|
||||
}), _cancellationTokenSource.Token);
|
||||
|
||||
while (!downloadTask.IsCompleted && !downloadTask.IsCanceled)
|
||||
{
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
if (downloadTask.IsCanceled)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
labelPleaseWait.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteDownload(downloadStream);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonDownload.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
MessageBox.Show($"Unable to download {url}!" + Environment.NewLine + Environment.NewLine +
|
||||
exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
|
||||
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
|
||||
private void CompleteDownload(MemoryStream downloadStream)
|
||||
{
|
||||
var folder = Path.Combine(Configuration.DataDirectory, "Vosk");
|
||||
|
||||
if (e.Error != null)
|
||||
if (downloadStream.Length == 0)
|
||||
{
|
||||
Width = 630;
|
||||
Height = 350;
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
var errorMsg = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadFailed, "Vosk model");
|
||||
textBoxError.Visible = true;
|
||||
var downloadModel = (DownloadModel)comboBoxModels.Items[comboBoxModels.SelectedIndex];
|
||||
var dirName = Path.GetFileNameWithoutExtension(downloadModel.Url);
|
||||
textBoxError.Text = $"{errorMsg}{Environment.NewLine}{Environment.NewLine}You could manually download and unpack{Environment.NewLine}{downloadModel.Url}{Environment.NewLine}to{Environment.NewLine}{folder}{Path.DirectorySeparatorChar}{dirName}" + Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
e.Error;
|
||||
Cursor = Cursors.Default;
|
||||
return;
|
||||
throw new Exception("No content downloaded - missing file or no internet connection!");
|
||||
}
|
||||
|
||||
var folder = Path.Combine(Configuration.DataDirectory, "Vosk");
|
||||
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
using (var ms = new MemoryStream(e.Result))
|
||||
using (var zip = ZipExtractor.Open(ms))
|
||||
using (var zip = ZipExtractor.Open(downloadStream))
|
||||
{
|
||||
var dir = zip.ReadCentralDir();
|
||||
foreach (var entry in dir)
|
||||
|
@ -1,9 +1,8 @@
|
||||
using Nikse.SubtitleEdit.Core.Common;
|
||||
using Nikse.SubtitleEdit.Logic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Forms
|
||||
@ -12,6 +11,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
{
|
||||
public string FFmpegPath { get; internal set; }
|
||||
public bool AutoClose { get; internal set; }
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public DownloadFfmpeg()
|
||||
{
|
||||
@ -22,6 +22,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
buttonOK.Text = LanguageSettings.Current.General.Ok;
|
||||
buttonCancel.Text = LanguageSettings.Current.General.Cancel;
|
||||
UiUtil.FixLargeFonts(this, buttonOK);
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void DownloadFfmpeg_KeyDown(object sender, KeyEventArgs e)
|
||||
@ -39,62 +40,76 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
|
||||
private void DownloadFfmpeg_Shown(object sender, EventArgs e)
|
||||
{
|
||||
var url = "https://github.com/SubtitleEdit/support-files/raw/master/ffpmeg/ffmpeg-" + IntPtr.Size * 8 + ".zip";
|
||||
|
||||
try
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait;
|
||||
buttonOK.Enabled = false;
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
string url = "https://github.com/SubtitleEdit/support-files/raw/master/ffpmeg/ffmpeg-" + IntPtr.Size * 8 + ".zip";
|
||||
var wc = new WebClient { Proxy = Utilities.GetProxy() };
|
||||
|
||||
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
|
||||
wc.DownloadProgressChanged += (o, args) =>
|
||||
var httpClient = HttpClientHelper.MakeHttpClient();
|
||||
using (var downloadStream = new MemoryStream())
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + args.ProgressPercentage + "%";
|
||||
};
|
||||
wc.DownloadDataAsync(new Uri(url));
|
||||
var downloadTask = httpClient.DownloadAsync(url, downloadStream, new Progress<float>((progress) =>
|
||||
{
|
||||
var pct = (int)Math.Round(progress * 100.0, MidpointRounding.AwayFromZero);
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + pct + "%";
|
||||
}), _cancellationTokenSource.Token);
|
||||
|
||||
while (!downloadTask.IsCompleted && !downloadTask.IsCanceled)
|
||||
{
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
if (downloadTask.IsCanceled)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
labelPleaseWait.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteDownload(downloadStream);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
MessageBox.Show($"Unable to download {url}!" + Environment.NewLine + Environment.NewLine +
|
||||
exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
|
||||
private void CompleteDownload(Stream downloadStream)
|
||||
{
|
||||
if (e.Error != null)
|
||||
if (downloadStream.Length == 0)
|
||||
{
|
||||
labelPleaseWait.Text = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadFailed, "ffmpeg");
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
return;
|
||||
throw new Exception("No content downloaded - missing file or no internet connection!");
|
||||
}
|
||||
|
||||
string folder = Path.Combine(Configuration.DataDirectory, "ffmpeg");
|
||||
var folder = Path.Combine(Configuration.DataDirectory, "ffmpeg");
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
using (var ms = new MemoryStream(e.Result))
|
||||
using (ZipExtractor zip = ZipExtractor.Open(ms))
|
||||
using (var zip = ZipExtractor.Open(downloadStream))
|
||||
{
|
||||
List<ZipExtractor.ZipFileEntry> dir = zip.ReadCentralDir();
|
||||
foreach (ZipExtractor.ZipFileEntry entry in dir)
|
||||
var dir = zip.ReadCentralDir();
|
||||
foreach (var entry in dir)
|
||||
{
|
||||
string fileName = Path.GetFileName(entry.FilenameInZip);
|
||||
var fileName = Path.GetFileName(entry.FilenameInZip);
|
||||
if (fileName != null)
|
||||
{
|
||||
string path = Path.Combine(folder, fileName);
|
||||
var path = Path.Combine(folder, fileName);
|
||||
if (fileName.EndsWith("ffmpeg.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
FFmpegPath = path;
|
||||
|
4
src/ui/Forms/DownloadVosk.Designer.cs
generated
4
src/ui/Forms/DownloadVosk.Designer.cs
generated
@ -51,7 +51,7 @@
|
||||
this.buttonOK.Name = "buttonOK";
|
||||
this.buttonOK.Size = new System.Drawing.Size(75, 23);
|
||||
this.buttonOK.TabIndex = 15;
|
||||
this.buttonOK.Text = "&OK";
|
||||
this.buttonOK.Text = "7";
|
||||
this.buttonOK.UseVisualStyleBackColor = true;
|
||||
this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
|
||||
//
|
||||
@ -68,7 +68,7 @@
|
||||
this.buttonCancel.UseVisualStyleBackColor = true;
|
||||
this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click);
|
||||
//
|
||||
// textBox1
|
||||
// textBoxError
|
||||
//
|
||||
this.textBoxError.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
|
@ -3,7 +3,7 @@ using Nikse.SubtitleEdit.Logic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Forms
|
||||
@ -11,6 +11,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
public sealed partial class DownloadVosk : Form
|
||||
{
|
||||
public bool AutoClose { get; internal set; }
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
private static readonly string[] VoskNewSha512Hashes =
|
||||
{
|
||||
@ -28,6 +29,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
buttonCancel.Text = LanguageSettings.Current.General.Cancel;
|
||||
UiUtil.FixLargeFonts(this, buttonOK);
|
||||
textBoxError.Visible = false;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void DownloadVosk_KeyDown(object sender, KeyEventArgs e)
|
||||
@ -45,6 +47,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
|
||||
@ -59,50 +62,59 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
|
||||
var wc = new WebClient { Proxy = Utilities.GetProxy() };
|
||||
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
|
||||
wc.DownloadProgressChanged += (o, args) =>
|
||||
var httpClient = HttpClientHelper.MakeHttpClient();
|
||||
using (var downloadStream = new MemoryStream())
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + args.ProgressPercentage + "%";
|
||||
};
|
||||
wc.DownloadDataAsync(new Uri(VoskUrl));
|
||||
var downloadTask = httpClient.DownloadAsync(VoskUrl, downloadStream, new Progress<float>((progress) =>
|
||||
{
|
||||
var pct = (int)Math.Round(progress * 100.0, MidpointRounding.AwayFromZero);
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + pct + "%";
|
||||
}), _cancellationTokenSource.Token);
|
||||
|
||||
while (!downloadTask.IsCompleted && !downloadTask.IsCanceled)
|
||||
{
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
if (downloadTask.IsCanceled)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
labelPleaseWait.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteDownload(downloadStream);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
MessageBox.Show($"Unable to download {VoskUrl}!" + Environment.NewLine + Environment.NewLine +
|
||||
exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
|
||||
private void CompleteDownload(MemoryStream downloadStream)
|
||||
{
|
||||
var folder = Path.Combine(Configuration.DataDirectory, "Vosk");
|
||||
|
||||
if (e.Error != null)
|
||||
if (downloadStream.Length == 0)
|
||||
{
|
||||
Width = 630;
|
||||
Height = 350;
|
||||
labelPleaseWait.Text = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadFailed, "Vosk");
|
||||
textBoxError.Visible = true;
|
||||
textBoxError.Text = $"You could manually download and unpack{Environment.NewLine}{VoskUrl}{Environment.NewLine}to{Environment.NewLine}{folder}" + Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
e.Error;
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
return;
|
||||
throw new Exception("No content downloaded - missing file or no internet connection!");
|
||||
}
|
||||
|
||||
var hash = Utilities.GetSha512Hash(e.Result);
|
||||
var folder = Path.Combine(Configuration.DataDirectory, "Vosk");
|
||||
|
||||
var hash = Utilities.GetSha512Hash(downloadStream.ToArray());
|
||||
if (!VoskNewSha512Hashes.Contains(hash))
|
||||
{
|
||||
MessageBox.Show("Vosk SHA-512 hash does not match!");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var ms = new MemoryStream(e.Result))
|
||||
using (var zip = ZipExtractor.Open(ms))
|
||||
downloadStream.Position = 0;
|
||||
using (var zip = ZipExtractor.Open(downloadStream))
|
||||
{
|
||||
var dir = zip.ReadCentralDir();
|
||||
foreach (var entry in dir)
|
||||
|
@ -2,7 +2,7 @@
|
||||
using Nikse.SubtitleEdit.Logic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Forms
|
||||
@ -12,6 +12,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
public const string Url = "https://github.com/yt-dlp/yt-dlp/releases/download/2022.05.18/yt-dlp.exe";
|
||||
public const string Sha512Hash = "f8f55be89b8b4b5c9703bb594367ad10c245dd1e1de9182a8a3d8ae06220aeabe200ed9b797d6f176a5d10f99ff3bbb48176c4e52cd9f4cc3c1fac451df42dde";
|
||||
public bool AutoClose { get; internal set; }
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public DownloadYouTubeDl()
|
||||
{
|
||||
@ -22,6 +23,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
buttonOK.Text = LanguageSettings.Current.General.Ok;
|
||||
buttonCancel.Text = LanguageSettings.Current.General.Cancel;
|
||||
UiUtil.FixLargeFonts(this, buttonOK);
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void DownloadFfmpeg_KeyDown(object sender, KeyEventArgs e)
|
||||
@ -39,6 +41,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
|
||||
@ -50,48 +53,63 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
buttonOK.Enabled = false;
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
var wc = new WebClient { Proxy = Utilities.GetProxy() };
|
||||
|
||||
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
|
||||
wc.DownloadProgressChanged += (o, args) =>
|
||||
var httpClient = HttpClientHelper.MakeHttpClient();
|
||||
using (var downloadStream = new MemoryStream())
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + args.ProgressPercentage + "%";
|
||||
};
|
||||
wc.DownloadDataAsync(new Uri(Url));
|
||||
var downloadTask = httpClient.DownloadAsync(Url, downloadStream, new Progress<float>((progress) =>
|
||||
{
|
||||
var pct = (int)Math.Round(progress * 100.0, MidpointRounding.AwayFromZero);
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + pct + "%";
|
||||
}), _cancellationTokenSource.Token);
|
||||
|
||||
while (!downloadTask.IsCompleted && !downloadTask.IsCanceled)
|
||||
{
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
if (downloadTask.IsCanceled)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
labelPleaseWait.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteDownload(downloadStream);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
MessageBox.Show($"Unable to download {Url}!" + Environment.NewLine + Environment.NewLine +
|
||||
exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
|
||||
private void CompleteDownload(MemoryStream downloadStream)
|
||||
{
|
||||
if (e.Error != null)
|
||||
if (downloadStream.Length == 0)
|
||||
{
|
||||
labelPleaseWait.Text = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadFailed, "youtube-dl");
|
||||
buttonOK.Enabled = true;
|
||||
Cursor = Cursors.Default;
|
||||
return;
|
||||
throw new Exception("No content downloaded - missing file or no internet connection!");
|
||||
}
|
||||
|
||||
string folder = Path.Combine(Configuration.DataDirectory);
|
||||
var folder = Path.Combine(Configuration.DataDirectory);
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
var hash = Utilities.GetSha512Hash(e.Result);
|
||||
var bytes = downloadStream.ToArray();
|
||||
var hash = Utilities.GetSha512Hash(bytes);
|
||||
if (hash != Sha512Hash)
|
||||
{
|
||||
MessageBox.Show("yt-dlp.exe SHA-512 hash does not match!");
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllBytes(Path.Combine(folder, "yt-dlp.exe"), e.Result);
|
||||
File.WriteAllBytes(Path.Combine(folder, "yt-dlp.exe"), bytes);
|
||||
|
||||
Cursor = Cursors.Default;
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
@ -103,7 +121,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
}
|
||||
|
||||
buttonOK.Enabled = true;
|
||||
labelPleaseWait.Text = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadOk, "youtube-dl");
|
||||
labelPleaseWait.Text = string.Format(LanguageSettings.Current.SettingsFfmpeg.XDownloadOk, "yt-dlp.exe");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ using Nikse.SubtitleEdit.Logic.VideoPlayers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Nikse.SubtitleEdit.Forms.Options
|
||||
@ -13,6 +13,7 @@ namespace Nikse.SubtitleEdit.Forms.Options
|
||||
{
|
||||
private readonly bool _justDownload;
|
||||
private string _downloadUrl;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public SettingsMpv(bool justDownload)
|
||||
{
|
||||
@ -31,27 +32,63 @@ namespace Nikse.SubtitleEdit.Forms.Options
|
||||
}
|
||||
|
||||
UiUtil.FixLargeFonts(this, buttonOK);
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
|
||||
private void ButtonDownloadClick(object sender, EventArgs e)
|
||||
{
|
||||
if (e.Error != null)
|
||||
_downloadUrl = "https://github.com/SubtitleEdit/support-files/blob/master/mpv/libmpv2-" + IntPtr.Size * 8 + ".zip?raw=true";
|
||||
try
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait;
|
||||
buttonOK.Enabled = false;
|
||||
buttonDownload.Enabled = false;
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
var httpClient = HttpClientHelper.MakeHttpClient();
|
||||
using (var downloadStream = new MemoryStream())
|
||||
{
|
||||
var downloadTask = httpClient.DownloadAsync(_downloadUrl, downloadStream, new Progress<float>((progress) =>
|
||||
{
|
||||
var pct = (int)Math.Round(progress * 100.0, MidpointRounding.AwayFromZero);
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + pct + "%";
|
||||
}), _cancellationTokenSource.Token);
|
||||
|
||||
while (!downloadTask.IsCompleted && !downloadTask.IsCanceled)
|
||||
{
|
||||
Application.DoEvents();
|
||||
}
|
||||
|
||||
if (downloadTask.IsCanceled)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
labelPleaseWait.Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteDownload(downloadStream);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MessageBox.Show(LanguageSettings.Current.SettingsMpv.DownloadMpvFailed + Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
$"Manual action: Download \"{_downloadUrl}\" and unpack to \"{Configuration.DataDirectory}\"." + Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
e.Error.Message);
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonOK.Enabled = true;
|
||||
buttonDownload.Enabled = !Configuration.IsRunningOnLinux;
|
||||
Cursor = Cursors.Default;
|
||||
return;
|
||||
MessageBox.Show($"Unable to download {_downloadUrl}!" + Environment.NewLine + Environment.NewLine +
|
||||
exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
}
|
||||
|
||||
private void CompleteDownload(MemoryStream downloadStream)
|
||||
{
|
||||
if (downloadStream.Length == 0)
|
||||
{
|
||||
throw new Exception("No content downloaded - missing file or no internet connection!");
|
||||
}
|
||||
|
||||
string dictionaryFolder = Configuration.DataDirectory;
|
||||
using (var ms = new MemoryStream(e.Result))
|
||||
using (ZipExtractor zip = ZipExtractor.Open(ms))
|
||||
var dictionaryFolder = Configuration.DataDirectory;
|
||||
using (ZipExtractor zip = ZipExtractor.Open(downloadStream))
|
||||
{
|
||||
List<ZipExtractor.ZipFileEntry> dir = zip.ReadCentralDir();
|
||||
foreach (ZipExtractor.ZipFileEntry entry in dir)
|
||||
@ -83,35 +120,6 @@ namespace Nikse.SubtitleEdit.Forms.Options
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void ButtonDownloadClick(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait;
|
||||
buttonOK.Enabled = false;
|
||||
buttonDownload.Enabled = false;
|
||||
Refresh();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
_downloadUrl = "https://github.com/SubtitleEdit/support-files/blob/master/mpv/libmpv2-" + IntPtr.Size * 8 + ".zip?raw=true";
|
||||
var wc = new WebClient { Proxy = Utilities.GetProxy() };
|
||||
|
||||
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
|
||||
wc.DownloadProgressChanged += (o, args) =>
|
||||
{
|
||||
labelPleaseWait.Text = LanguageSettings.Current.General.PleaseWait + " " + args.ProgressPercentage + "%";
|
||||
};
|
||||
wc.DownloadDataAsync(new Uri(_downloadUrl));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
labelPleaseWait.Text = string.Empty;
|
||||
buttonOK.Enabled = true;
|
||||
buttonDownload.Enabled = !Configuration.IsRunningOnLinux;
|
||||
Cursor = Cursors.Default;
|
||||
MessageBox.Show(exception.Message + Environment.NewLine + Environment.NewLine + exception.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
private void buttonOK_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
@ -119,6 +127,7 @@ namespace Nikse.SubtitleEdit.Forms.Options
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
DialogResult = DialogResult.Cancel;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user