Skip to content

Commit

Permalink
Properly lock the AniDBTitleHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
da3dsoul committed Mar 20, 2024
1 parent fca3cd6 commit 24e9bcb
Showing 1 changed file with 69 additions and 91 deletions.
160 changes: 69 additions & 91 deletions Shoko.Server/Providers/AniDB/Titles/AniDBTitleHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Xml.Serialization;
using Shoko.Plugin.Abstractions.DataModels;
using Shoko.Server.Server;
Expand All @@ -14,15 +16,14 @@ namespace Shoko.Server.Providers.AniDB.Titles;

public class AniDBTitleHelper
{
// ensure that it doesn't try to download at the same time
private static readonly object AccessLock = new();
private readonly ReaderWriterLockSlim _accessLock = new(LockRecursionPolicy.SupportsRecursion);

private static readonly string CacheFilePath = Path.Combine(Utils.ApplicationPath, "anime-titles.xml");
private readonly string CacheFilePath = Path.Combine(Utils.ApplicationPath, "anime-titles.xml");

private static readonly string CacheFilePathTemp =
private readonly string CacheFilePathTemp =
Path.Combine(Utils.ApplicationPath, "anime-titles.xml") + ".temp";

private static readonly string CacheFilePathBak =
private readonly string CacheFilePathBak =
Path.Combine(Utils.ApplicationPath, "anime-titles.xml") + ".bak";

private ResponseAniDBTitles _cache;
Expand All @@ -37,24 +38,23 @@ public AniDBTitleHelper(ISettingsProvider settingsProvider)
{
try
{
if (_cache == null)
if (_cache == null) CreateCache();


try
{
CreateCache();
_accessLock.EnterReadLock();
return _cache?.Animes.ToList() ?? new List<ResponseAniDBTitles.Anime>();
}

if (_cache != null)
finally
{
return _cache.Animes;
_accessLock.ExitReadLock();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (_cache == null)
{
CreateCache();
}

return new List<ResponseAniDBTitles.Anime>();
}
Expand All @@ -63,13 +63,18 @@ public ResponseAniDBTitles.Anime SearchAnimeID(int animeID)
{
try
{
if (_cache == null)
if (_cache == null) CreateCache();

try
{
CreateCache();
_accessLock.EnterReadLock();
return _cache?.Animes
.FirstOrDefault(a => a.AnimeID == animeID);
}
finally
{
_accessLock.ExitReadLock();
}

return _cache?.Animes
.FirstOrDefault(a => a.AnimeID == animeID);
}
catch (Exception e)
{
Expand All @@ -83,15 +88,14 @@ public ResponseAniDBTitles.Anime SearchAnimeID(int animeID)
{
try
{
if (_cache == null)
{
CreateCache();
}
if (_cache == null) CreateCache();

if (_cache != null)

try
{
_accessLock.EnterReadLock();
var languages = _settingsProvider.GetSettings().LanguagePreference;
return _cache.Animes
return _cache?.Animes
.AsParallel()
.Search(
query,
Expand All @@ -102,7 +106,11 @@ public ResponseAniDBTitles.Anime SearchAnimeID(int animeID)
.ToList(),
fuzzy
)
.Select(a => a.Result);
.Select(a => a.Result).ToList() ?? new List<ResponseAniDBTitles.Anime>();
}
finally
{
_accessLock.ExitReadLock();
}
}
catch (Exception e)
Expand All @@ -115,37 +123,24 @@ public ResponseAniDBTitles.Anime SearchAnimeID(int animeID)

private void CreateCache()
{
_accessLock.EnterWriteLock();
if (!File.Exists(CacheFilePath))
{
// first check if there's a temp file
if (File.Exists(CacheFilePathTemp))
{
File.Move(CacheFilePathTemp, CacheFilePath);
}
if (File.Exists(CacheFilePathTemp)) File.Move(CacheFilePathTemp, CacheFilePath);

if (!File.Exists(CacheFilePath))
{
lock (AccessLock)
{
DownloadCache();
}
}
if (!File.Exists(CacheFilePath)) DownloadCache();
}

if (!File.Exists(CacheFilePath))
{
_accessLock.ExitWriteLock();
return;
}

lock (AccessLock)
{
// If data is stale, then re-download
var lastWriteTime = File.GetLastWriteTime(CacheFilePath);
if (DateTime.Now - lastWriteTime > TimeSpan.FromHours(24))
{
DownloadCache();
}
}
// If data is stale, then re-download
var lastWriteTime = File.GetLastWriteTime(CacheFilePath);
if (DateTime.Now - lastWriteTime > TimeSpan.FromHours(24)) DownloadCache();

try
{
Expand All @@ -156,94 +151,77 @@ private void CreateCache()
Decompress();
LoadCache();
}
_accessLock.ExitWriteLock();
}

private void LoadCache()
{
// Load the file
using var stream = new FileStream(CacheFilePath, FileMode.Open);
var serializer = new XmlSerializer(typeof(ResponseAniDBTitles));
if (serializer.Deserialize(stream) is ResponseAniDBTitles rawData)
{
_cache = rawData;
}
if (serializer.Deserialize(stream) is ResponseAniDBTitles rawData) _cache = rawData;
}

private static void Decompress()
private void Decompress()
{
using var stream = new FileStream(CacheFilePath, FileMode.Open);
var gzip = new GZipStream(stream, CompressionMode.Decompress);
var textResponse = new StreamReader(gzip).ReadToEnd();
if (File.Exists(CacheFilePathTemp))
{
File.Delete(CacheFilePathTemp);
}
if (File.Exists(CacheFilePathTemp)) File.Delete(CacheFilePathTemp);

File.WriteAllText(CacheFilePathTemp, textResponse);

// backup the old one
if (File.Exists(CacheFilePath))
{
File.Move(CacheFilePath, CacheFilePathBak);
}
if (File.Exists(CacheFilePath)) File.Move(CacheFilePath, CacheFilePathBak);

// rename new one
File.Move(CacheFilePathTemp, CacheFilePath);

// remove old one
if (File.Exists(CacheFilePathBak))
{
File.Delete(CacheFilePathBak);
}
if (File.Exists(CacheFilePathBak)) File.Delete(CacheFilePathBak);
}

private static void DownloadCache()
private void DownloadCache()
{
try
{
if (File.Exists(CacheFilePathTemp))
{
File.Delete(CacheFilePathTemp);
}
if (File.Exists(CacheFilePathTemp)) File.Delete(CacheFilePathTemp);

// Ignore all certificate failures.
ServicePointManager.Expect100Continue = true;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

// Download the file
using (var client = new WebClient())
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0");
httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
httpClient.DefaultRequestHeaders.Add("Accept-Language", "de,en-US;q=0.7,en;q=0.3");
httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "gzip,deflate");

using var response = httpClient.GetAsync(Constants.AniDBTitlesURL).Result;
if (response.IsSuccessStatusCode)
{
client.Headers.Add(HttpRequestHeader.UserAgent,
"Mozilla / 5.0(Windows NT 10.0; Win64; x64; rv: 74.0) Gecko / 20100101 Firefox / 74.0");
client.Headers.Add("Accept",
"text / html,application / xhtml + xml,application / xml; q = 0.9,image / webp,*/*;q=0.8");
client.Headers.Add("Accept-Language", "de,en-US;q=0.7,en;q=0.3");
client.Headers.Add("Accept-Encoding", "gzip,deflate");

var stream = client.OpenRead(Constants.AniDBTitlesURL);
if (stream != null)
{
var gzip = new GZipStream(stream, CompressionMode.Decompress);
var textResponse = new StreamReader(gzip).ReadToEnd();
File.WriteAllText(CacheFilePathTemp, textResponse);
}
using var responseStream = response.Content.ReadAsStream();
using var gzipStream = new GZipStream(responseStream, CompressionMode.Decompress);
using var reader = new StreamReader(gzipStream);
var textResponse = reader.ReadToEnd();
File.WriteAllText(CacheFilePathTemp, textResponse);
}

// backup the old one
if (File.Exists(CacheFilePath))
else
{
File.Move(CacheFilePath, CacheFilePathBak);
Console.WriteLine($@"Failed to download file. Status code: {response.StatusCode}");
return;
}

// backup the old one
if (File.Exists(CacheFilePath)) File.Move(CacheFilePath, CacheFilePathBak);

// rename new one
File.Move(CacheFilePathTemp, CacheFilePath);

// remove old one
if (File.Exists(CacheFilePathBak))
{
File.Delete(CacheFilePathBak);
}
if (File.Exists(CacheFilePathBak)) File.Delete(CacheFilePathBak);
}
catch (Exception e)
{
Expand Down

0 comments on commit 24e9bcb

Please sign in to comment.