hardcoded search error messages changed to string resources

This commit is contained in:
2024-03-03 16:23:45 +01:00
Unverified
parent 2da59382a3
commit c3724921f3
7 changed files with 202 additions and 21 deletions

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="EmptyPlaylist" xml:space="preserve">
<value>Playlist is empty</value>
</data>
<data name="EmptyUrl" xml:space="preserve">
<value>Invalid url. Url cannot be empty.</value>
</data>
<data name="SourceNotSupported" xml:space="preserve">
<value>Invalid url. Url does not match any of supported source.</value>
</data>
<data name="TwitchChannelNotFound" xml:space="preserve">
<value>Invalid url. Twitch channel not found.</value>
</data>
<data name="TwitchNotAuthenticated" xml:space="preserve">
<value>Twitch authentication error. Not authenticated to Twitch.</value>
</data>
<data name="TwitchTokenValidationUnsuccessful" xml:space="preserve">
<value>Twitch authentication error. Authentication token is invalid.</value>
</data>
<data name="UnknownMediaType" xml:space="preserve">
<value>Invalid url. Url does not match any of supported source's media types.</value>
</data>
</root>

View File

@@ -200,7 +200,7 @@ namespace VDownload.Core.ViewModels.Home
catch (MediaSearchException ex) catch (MediaSearchException ex)
{ {
OptionBarLoading = false; OptionBarLoading = false;
OptionBarMessage = ex.Message; OptionBarMessage = _stringResourcesService.SearchResources.Get(ex.StringCode);
OptionBarSearchNotPending = true; OptionBarSearchNotPending = true;
return; return;
} }
@@ -229,7 +229,7 @@ namespace VDownload.Core.ViewModels.Home
catch (MediaSearchException ex) catch (MediaSearchException ex)
{ {
OptionBarLoading = false; OptionBarLoading = false;
OptionBarMessage = ex.Message; OptionBarMessage = _stringResourcesService.SearchResources.Get(ex.StringCode);
OptionBarSearchNotPending = true; OptionBarSearchNotPending = true;
return; return;
} }

View File

@@ -12,6 +12,7 @@ namespace VDownload.Services.UI.StringResources
StringResources HomeDownloadsViewResources { get; } StringResources HomeDownloadsViewResources { get; }
StringResources AuthenticationViewResources { get; } StringResources AuthenticationViewResources { get; }
StringResources NotificationsResources { get; } StringResources NotificationsResources { get; }
StringResources SearchResources { get; }
} }
@@ -35,6 +36,7 @@ namespace VDownload.Services.UI.StringResources
public StringResources HomeDownloadsViewResources { get; protected set; } public StringResources HomeDownloadsViewResources { get; protected set; }
public StringResources AuthenticationViewResources { get; protected set; } public StringResources AuthenticationViewResources { get; protected set; }
public StringResources NotificationsResources { get; protected set; } public StringResources NotificationsResources { get; protected set; }
public StringResources SearchResources { get; protected set; }
#endregion #endregion
@@ -53,6 +55,7 @@ namespace VDownload.Services.UI.StringResources
HomeDownloadsViewResources = BuildResource("HomeDownloadsViewResources"); HomeDownloadsViewResources = BuildResource("HomeDownloadsViewResources");
AuthenticationViewResources = BuildResource("AuthenticationViewResources"); AuthenticationViewResources = BuildResource("AuthenticationViewResources");
NotificationsResources = BuildResource("NotificationsResources"); NotificationsResources = BuildResource("NotificationsResources");
SearchResources = BuildResource("SearchResources");
} }
#endregion #endregion

View File

@@ -8,13 +8,21 @@ namespace VDownload.Sources.Common
{ {
public class MediaSearchException : Exception public class MediaSearchException : Exception
{ {
#region PROPERTIES
public string StringCode { get; protected set; }
#endregion
#region CONSTRUCTORS #region CONSTRUCTORS
public MediaSearchException() : base() { } public MediaSearchException(string stringCode) : this(stringCode, stringCode) { }
public MediaSearchException(string stringCode, string message) : base(message)
public MediaSearchException(string message) : base(message) { } {
StringCode = stringCode;
public MediaSearchException(string message, Exception inner) : base(message, inner) { } }
#endregion #endregion
} }

View File

@@ -32,7 +32,7 @@ namespace VDownload.Sources.Common
return video; return video;
} }
} }
throw new MediaSearchException("Invalid url"); // TODO : Change to string resource throw CreateExceptionUnknownMediaType();
} }
public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount) public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount)
@@ -47,7 +47,7 @@ namespace VDownload.Sources.Common
return video; return video;
} }
} }
throw new MediaSearchException("Invalid url"); // TODO : Change to string resource throw CreateExceptionUnknownMediaType();
} }
#endregion #endregion
@@ -60,6 +60,9 @@ namespace VDownload.Sources.Common
protected abstract IEnumerable<SearchRegexPlaylist> GetPlaylistRegexes(); protected abstract IEnumerable<SearchRegexPlaylist> GetPlaylistRegexes();
protected MediaSearchException CreateExceptionUnknownMediaType() => new MediaSearchException("UnknownMediaType");
protected MediaSearchException CreateExceptionEmptyPlaylist() => new MediaSearchException("EmptyPlaylist");
#endregion #endregion
} }
} }

View File

@@ -110,8 +110,22 @@ namespace VDownload.Sources.Twitch
protected async Task<TwitchChannel> GetChannel(string id, int count) protected async Task<TwitchChannel> GetChannel(string id, int count)
{ {
byte[] token = await GetToken(); byte[] token = await GetToken();
GetUsersResponse info = await _apiService.HelixGetUser(id, token);
Api.Helix.GetUsers.Response.Data userResponse = info.Data[0]; Api.Helix.GetUsers.Response.Data userResponse;
try
{
GetUsersResponse info = await _apiService.HelixGetUser(id, token);
if (info.Data.Count <= 0)
{
throw CreateExceptionChannelNotFound();
}
userResponse = info.Data[0];
}
catch (InvalidOperationException ex)
{
// TODO: Add logging
throw;
}
TwitchChannel channel = new TwitchChannel TwitchChannel channel = new TwitchChannel
{ {
@@ -130,6 +144,12 @@ namespace VDownload.Sources.Twitch
{ {
videos = count > 100 ? 100 : count; videos = count > 100 ? 100 : count;
GetVideosResponse videosResponse = await _apiService.HelixGetUserVideos(channel.Id, token, videos, cursor); GetVideosResponse videosResponse = await _apiService.HelixGetUserVideos(channel.Id, token, videos, cursor);
if (!tasks.Any() && !videosResponse.Data.Any())
{
throw CreateExceptionEmptyPlaylist();
}
videosList = videosResponse.Data; videosList = videosResponse.Data;
cursor = videosResponse.Pagination.Cursor; cursor = videosResponse.Pagination.Cursor;
tasks.AddRange(videosList.Select(ParseVod)); tasks.AddRange(videosList.Select(ParseVod));
@@ -271,19 +291,20 @@ namespace VDownload.Sources.Twitch
protected async Task<byte[]> GetToken() protected async Task<byte[]> GetToken()
{ {
byte[]? token = await _twitchAuthenticationService.GetToken(); byte[]? token = await _twitchAuthenticationService.GetToken() ?? throw CreateExceptionNotAuthenticated();
if (token is null)
{
throw new MediaSearchException("Not authenticated to Twitch"); // TODO : Change to string resource
}
TwitchValidationResult validation = await _twitchAuthenticationService.ValidateToken(token); TwitchValidationResult validation = await _twitchAuthenticationService.ValidateToken(token);
if (!validation.Success) if (!validation.Success)
{ {
throw new MediaSearchException("Twitch authentication error"); // TODO : Change to string resource throw CreateExceptionTokenValidationUnsuccessful();
} }
return token; return token;
} }
protected MediaSearchException CreateExceptionNotAuthenticated() => new MediaSearchException("TwitchNotAuthenticated");
protected MediaSearchException CreateExceptionTokenValidationUnsuccessful() => new MediaSearchException("TwitchTokenValidationUnsuccessful");
protected MediaSearchException CreateExceptionChannelNotFound() => new MediaSearchException("TwitchChannelNotFound");
#endregion #endregion
} }
} }

View File

@@ -58,7 +58,8 @@ namespace VDownload.Sources
return await mapping.Service.SearchVideo(url); return await mapping.Service.SearchVideo(url);
} }
} }
throw new MediaSearchException("Source is not supported"); // TODO : Change to string resource
throw CreateExceptionSourceNotSupported();
} }
public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount) public async Task<Playlist> SearchPlaylist(string url, int maxVideoCount)
@@ -72,7 +73,8 @@ namespace VDownload.Sources
return await mapping.Service.SearchPlaylist(url, maxVideoCount); return await mapping.Service.SearchPlaylist(url, maxVideoCount);
} }
} }
throw new MediaSearchException("Source is not supported"); // TODO : Change to string resource
throw CreateExceptionSourceNotSupported();
} }
#endregion #endregion
@@ -81,14 +83,17 @@ namespace VDownload.Sources
#region PRIVATE METHODS #region PRIVATE METHODS
private void BaseUrlCheck(string url) protected void BaseUrlCheck(string url)
{ {
if (string.IsNullOrWhiteSpace(url)) if (string.IsNullOrWhiteSpace(url))
{ {
throw new MediaSearchException("Url cannot be empty"); // TODO : Change to string resource throw CreateExceptionEmptyUrl();
} }
} }
protected MediaSearchException CreateExceptionSourceNotSupported() => new MediaSearchException("SourceNotSupported");
protected MediaSearchException CreateExceptionEmptyUrl() => new MediaSearchException("EmptyUrl");
#endregion #endregion
} }
} }