diff --git a/src/Common/SIPackages.Providers/IPackagesProvider.cs b/src/Common/SIPackages.Providers/IPackagesProvider.cs
index 3a8dfd0d..03b91bdb 100644
--- a/src/Common/SIPackages.Providers/IPackagesProvider.cs
+++ b/src/Common/SIPackages.Providers/IPackagesProvider.cs
@@ -8,13 +8,15 @@ public interface IPackagesProvider
///
/// Enumerates available packages names.
///
+ /// Packages culture.
/// Cancellation token.
- Task> GetPackagesAsync(CancellationToken cancellationToken = default);
+ Task> GetPackagesAsync(string culture, CancellationToken cancellationToken = default);
///
/// Gets package by name.
///
+ /// Packages culture.
/// Package name.
/// Cancellation token.
- Task GetPackageAsync(string name, CancellationToken cancellationToken = default);
+ Task GetPackageAsync(string culture, string name, CancellationToken cancellationToken = default);
}
diff --git a/src/Common/SIPackages.Providers/PackageHelper.cs b/src/Common/SIPackages.Providers/PackageHelper.cs
index 1735e3b2..75f0f540 100644
--- a/src/Common/SIPackages.Providers/PackageHelper.cs
+++ b/src/Common/SIPackages.Providers/PackageHelper.cs
@@ -26,7 +26,7 @@ public static Task GenerateRandomPackageAsync(
CancellationToken cancellationToken = default)
{
var doc = SIDocument.Create(name, author, stream);
- return GenerateCoreAsync(provider, roundsCount, themesCount, baseCost, doc, roundNameFormat, finalName, int.MaxValue, cancellationToken);
+ return GenerateCoreAsync(provider, roundsCount, themesCount, baseCost, doc, roundNameFormat, finalName, "", int.MaxValue, cancellationToken);
}
public static Task GenerateRandomPackageAsync(
@@ -36,6 +36,7 @@ public static Task GenerateRandomPackageAsync(
string author,
string roundNameFormat,
string finalName,
+ string culture,
int roundsCount = 3,
int themesCount = 6,
int baseCost = 100,
@@ -44,7 +45,7 @@ public static Task GenerateRandomPackageAsync(
{
var doc = SIDocument.Create(name, author, folder);
- return GenerateCoreAsync(provider, roundsCount, themesCount, baseCost, doc, roundNameFormat, finalName, maxPackageCount, cancellationToken);
+ return GenerateCoreAsync(provider, roundsCount, themesCount, baseCost, doc, roundNameFormat, finalName, culture, maxPackageCount, cancellationToken);
}
private static async Task GenerateCoreAsync(
@@ -55,10 +56,11 @@ private static async Task GenerateCoreAsync(
SIDocument doc,
string roundNameFormat,
string finalName,
+ string culture,
int maxPackageCount = int.MaxValue,
CancellationToken cancellationToken = default)
{
- var files = (await provider.GetPackagesAsync(cancellationToken)).ToList();
+ var files = (await provider.GetPackagesAsync(culture, cancellationToken)).ToList();
if (maxPackageCount < int.MaxValue)
{
@@ -86,6 +88,7 @@ private static async Task GenerateCoreAsync(
round => round.Type == RoundTypes.Standart && round.Themes.Count > 0,
packageComments,
baseCost,
+ culture,
cancellationToken))
{
j--;
@@ -116,6 +119,7 @@ private static async Task GenerateCoreAsync(
round => round.Type == RoundTypes.Final && round.Themes.Count > 0,
packageComments,
0,
+ culture,
cancellationToken))
{
j--;
@@ -141,10 +145,11 @@ private static async Task ExtractThemeAsync(
Func predicate,
StringBuilder packageComments,
int baseCost,
+ string culture,
CancellationToken cancellationToken = default)
{
var fIndex = Random.Shared.Next(files.Count);
- var doc2 = await provider.GetPackageAsync(files[fIndex], cancellationToken) ?? throw new PackageNotFoundException(files[fIndex]);
+ var doc2 = await provider.GetPackageAsync(culture, files[fIndex], cancellationToken) ?? throw new PackageNotFoundException(files[fIndex]);
using (doc2)
{
@@ -250,6 +255,27 @@ private static async Task InheritContentAsync(
}
}
}
+
+ foreach (var atom in question.Scenario)
+ {
+ if (atom.Type == AtomTypes.Text || atom.Type == AtomTypes.Oral)
+ {
+ continue;
+ }
+
+ var link = doc2.GetLink(atom);
+
+ if (link.GetStream != null)
+ {
+ var collection = doc.TryGetCollection(atom.Type);
+
+ if (collection != null)
+ {
+ using var stream = link.GetStream().Stream;
+ await collection.AddFileAsync(link.Uri, stream, cancellationToken);
+ }
+ }
+ }
}
private static void InheritAuthors(SIDocument doc2, Round round, Theme theme)
diff --git a/src/Common/SIPackages.Providers/SIStoragePackageProvider.cs b/src/Common/SIPackages.Providers/SIStoragePackageProvider.cs
index 242bd53f..5f6523f1 100644
--- a/src/Common/SIPackages.Providers/SIStoragePackageProvider.cs
+++ b/src/Common/SIPackages.Providers/SIStoragePackageProvider.cs
@@ -12,26 +12,55 @@ public sealed class SIStoragePackageProvider : IPackagesProvider, IDisposable
private static readonly HttpClient HttpClient = new() { DefaultRequestVersion = HttpVersion.Version20 };
private readonly ISIStorageServiceClient _siStorageServiceClient;
- private readonly Dictionary _packageCache = new();
+ private readonly Dictionary> _packageCache = new();
+ private Dictionary? _languageCache = null;
private readonly SemaphoreSlim _packageSemaphore = new(1, 1);
public SIStoragePackageProvider(ISIStorageServiceClient siStorageServiceClient) =>
_siStorageServiceClient = siStorageServiceClient;
- public async Task> GetPackagesAsync(CancellationToken cancellationToken = default)
+ public async Task> GetPackagesAsync(string culture, CancellationToken cancellationToken = default)
{
- if (_packageCache.Count == 0)
+ if (_languageCache == null)
{
await _packageSemaphore.WaitAsync(cancellationToken);
try
{
- if (_packageCache.Count == 0)
+ if (_languageCache == null)
{
+ var languages = await _siStorageServiceClient.Facets.GetLanguagesAsync(cancellationToken);
+ _languageCache = languages.ToDictionary(l => l.Code, l => l.Id);
+ }
+ }
+ finally
+ {
+ _packageSemaphore.Release();
+ }
+ }
+
+ if (!_languageCache.TryGetValue(culture, out var languageId) || culture == null)
+ {
+ if (!_languageCache.TryGetValue("en-US", out languageId))
+ {
+ languageId = -1;
+ }
+ }
+
+ if (!_packageCache.TryGetValue(languageId, out var localizedCache))
+ {
+ await _packageSemaphore.WaitAsync(cancellationToken);
+
+ try
+ {
+ if (!_packageCache.TryGetValue(languageId, out localizedCache))
+ {
+ _packageCache[languageId] = localizedCache = new Dictionary();
+
var packages = await _siStorageServiceClient.Packages.GetPackagesAsync(
- new PackageFilters { TagIds = new[] { -1 } },
- new PackageSelectionParameters { Count = 1000 },
- cancellationToken);
+ new PackageFilters { LanguageId = languageId, TagIds = new[] { -1 } },
+ new PackageSelectionParameters { Count = 1000 },
+ cancellationToken);
foreach (var package in packages.Packages)
{
@@ -40,7 +69,7 @@ public async Task> GetPackagesAsync(CancellationToken cancel
continue;
}
- _packageCache[package.Id.ToString()] = new PackageEntry { Uri = package.DirectContentUri };
+ localizedCache[package.Id.ToString()] = new PackageEntry { Uri = package.DirectContentUri };
}
}
}
@@ -50,32 +79,65 @@ public async Task> GetPackagesAsync(CancellationToken cancel
}
}
- return _packageCache.Keys;
+ return localizedCache.Keys;
}
- public async Task GetPackageAsync(string name, CancellationToken cancellationToken = default)
+ public async Task GetPackageAsync(string culture, string name, CancellationToken cancellationToken = default)
{
- if (!_packageCache.TryGetValue(name, out var info))
+ ArgumentNullException.ThrowIfNull(name, nameof(name));
+
+ int languageId;
+
+ if (_languageCache == null || culture == null)
+ {
+ languageId = -1;
+ }
+ else if (!_languageCache.TryGetValue(culture, out languageId))
+ {
+ if (!_languageCache.TryGetValue("en-US", out languageId))
+ {
+ languageId = -1;
+ }
+ }
+
+ if (!_packageCache.TryGetValue(languageId, out var localizedCache))
{
throw new PackageNotFoundException(name);
}
- if (info.LocalPath == null)
+ if (!localizedCache.TryGetValue(name, out var info))
{
- var fileName = Path.GetTempFileName();
+ throw new PackageNotFoundException(name);
+ }
- using var response = await HttpClient.GetAsync(info.Uri, cancellationToken);
+ if (info.LocalPath == null)
+ {
+ await _packageSemaphore.WaitAsync(cancellationToken);
- if (!response.IsSuccessStatusCode)
+ try
{
- throw new Exception(
- $"Error while accessing \"{info.Uri}\": {await response.Content.ReadAsStringAsync(cancellationToken)}!");
- }
+ if (info.LocalPath == null)
+ {
+ var fileName = Path.GetTempFileName();
+
+ using var response = await HttpClient.GetAsync(info.Uri, cancellationToken);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new Exception(
+ $"Error while accessing \"{info.Uri}\": {await response.Content.ReadAsStringAsync(cancellationToken)}!");
+ }
- using var fs = File.Create(fileName);
- await response.Content.CopyToAsync(fs, cancellationToken);
+ using var fs = File.Create(fileName);
+ await response.Content.CopyToAsync(fs, cancellationToken);
- info.LocalPath = fileName;
+ info.LocalPath = fileName;
+ }
+ }
+ finally
+ {
+ _packageSemaphore.Release();
+ }
}
return SIDocument.Load(File.OpenRead(info.LocalPath));
@@ -85,18 +147,21 @@ public void Dispose()
{
var exceptionsList = new List();
- foreach (var package in _packageCache)
+ foreach (var localizedCache in _packageCache.Values)
{
- try
+ foreach (var package in localizedCache.Values)
{
- if (package.Value.LocalPath != null)
+ try
{
- File.Delete(package.Value.LocalPath);
+ if (package.LocalPath != null)
+ {
+ File.Delete(package.LocalPath);
+ }
+ }
+ catch (Exception exc)
+ {
+ exceptionsList.Add(exc);
}
- }
- catch (Exception exc)
- {
- exceptionsList.Add(exc);
}
}
@@ -106,7 +171,7 @@ public void Dispose()
}
}
- private record struct PackageEntry
+ private record PackageEntry
{
public Uri Uri { get; set; }
diff --git a/src/SICore/SICore/Clients/Game/GameLogic.cs b/src/SICore/SICore/Clients/Game/GameLogic.cs
index cf9fbfab..e9be1c3c 100644
--- a/src/SICore/SICore/Clients/Game/GameLogic.cs
+++ b/src/SICore/SICore/Clients/Game/GameLogic.cs
@@ -553,7 +553,17 @@ private bool ShareMedia(ContentItem contentItem, bool isBackground = false)
return false;
}
- _gameActions.SendMessageWithArgs(Messages.Content, contentItem.Placement, 0, contentItem.Type, globalUri);
+ // For backward compatibility; remove later
+ // {
+ var contentType2 = contentItem.Type;
+
+ if (contentType2 == AtomTypes.AudioNew)
+ {
+ contentType2 = AtomTypes.Audio;
+ }
+ // }
+
+ _gameActions.SendMessageWithArgs(Messages.Content, contentItem.Placement, 0, contentType2, globalUri);
// TODO: remove after complete switching to Content message
// {
diff --git a/src/SICore/SICore/Clients/Game/QuestionPlayHandler.cs b/src/SICore/SICore/Clients/Game/QuestionPlayHandler.cs
index ed55a484..1e9158e0 100644
--- a/src/SICore/SICore/Clients/Game/QuestionPlayHandler.cs
+++ b/src/SICore/SICore/Clients/Game/QuestionPlayHandler.cs
@@ -238,7 +238,7 @@ public void OnQuestionContentItem(ContentItem contentItem)
break;
case ContentPlacements.Background:
- if (contentItem.Type == ContentTypes.Audio)
+ if (contentItem.Type == ContentTypes.Audio || contentItem.Type == AtomTypes.Audio)
{
GameLogic.OnContentBackgroundAudio(contentItem);
}
diff --git a/src/SICore/SICore/PackageProvider.cs b/src/SICore/SICore/PackageProvider.cs
index cb29bd4e..d6e8e08c 100644
--- a/src/SICore/SICore/PackageProvider.cs
+++ b/src/SICore/SICore/PackageProvider.cs
@@ -15,12 +15,12 @@ public PackageProvider(string folder)
_folder = folder;
}
- public Task> GetPackagesAsync(CancellationToken cancellationToken = default)
+ public Task> GetPackagesAsync(string culture, CancellationToken cancellationToken = default)
{
var dir = new DirectoryInfo(_folder);
return Task.FromResult(dir.EnumerateFiles("*.siq").Select(file => file.Name));
}
- public Task GetPackageAsync(string name, CancellationToken cancellationToken = default) =>
+ public Task GetPackageAsync(string culture, string name, CancellationToken cancellationToken = default) =>
Task.FromResult(SIDocument.Load(File.OpenRead(Path.Combine(_folder, name))));
}