diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 4b5e866..f8c449a 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -14,6 +14,7 @@
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 98c459d..ee74011 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -6,8 +6,4 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/main/java/cc/wordview/api/controller/ImageController.java b/src/main/java/cc/wordview/api/controller/ImageController.java
index 87c00a6..dce034e 100644
--- a/src/main/java/cc/wordview/api/controller/ImageController.java
+++ b/src/main/java/cc/wordview/api/controller/ImageController.java
@@ -19,64 +19,29 @@
import cc.wordview.api.Application;
import cc.wordview.api.exception.ImageNotFoundException;
-import cc.wordview.api.util.WordViewResourceResolver;
+import cc.wordview.api.runtime.ImageCache;
import jakarta.annotation.PostConstruct;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.stream.Stream;
+@SuppressWarnings("RedundantThrows")
@RestController
@CrossOrigin(origins = Application.CORS_ORIGIN)
@RequestMapping(path = Application.API_PATH + "/image")
public class ImageController {
- private static final Logger logger = LoggerFactory.getLogger(ImageController.class);
-
@Autowired
- private WordViewResourceResolver resourceResolver;
-
- private final Map images = new HashMap<>();
+ private ImageCache cache;
@GetMapping(produces = MediaType.IMAGE_PNG_VALUE)
public @ResponseBody byte[] getImage(@RequestParam String parent) throws ImageNotFoundException {
- byte[] image = images.get(parent);
-
- if (image == null) {
- throw new ImageNotFoundException("Unable to find a image with this parent");
- } else return image;
+ return cache.get(parent);
}
@PostConstruct
private void preloadImages() throws IOException {
- String imagesPath = resourceResolver.getImagesPath();
-
- try (Stream paths = Files.walk(Path.of(imagesPath))) {
- paths.filter(Files::isRegularFile)
- .forEach(file -> {
- try (InputStream stream = new FileInputStream(file.toString())) {
- String[] parts = file.toString().split("/");
- String imageName = parts[parts.length - 1].replace(".png", "");
-
- logger.info("Loading image \"{}.png\"", imageName);
-
- images.put(imageName, IOUtils.toByteArray(stream));
- } catch (Exception e) {
- logger.error("Failed to load image", e);
- }
- });
- } catch (IOException e) {
- logger.error("Failed to walk through directory: {}", imagesPath, e);
- }
+ cache.init();
}
}
diff --git a/src/main/java/cc/wordview/api/runtime/ArrayCacheManager.java b/src/main/java/cc/wordview/api/runtime/ArrayCacheManager.java
new file mode 100644
index 0000000..e624ecf
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/ArrayCacheManager.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+abstract public class ArrayCacheManager {
+ protected final ArrayList array = new ArrayList<>();
+
+ /**
+ * Populates the array with the values that should be cached.
+ * Ideally should be run in a @PostConstruct
+ */
+ abstract public void init() throws IOException;
+}
diff --git a/src/main/java/cc/wordview/api/runtime/HashMapCacheManager.java b/src/main/java/cc/wordview/api/runtime/HashMapCacheManager.java
new file mode 100644
index 0000000..11b96e5
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/HashMapCacheManager.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+abstract public class HashMapCacheManager {
+ protected final Map map = new HashMap<>();
+
+ /**
+ * Populates the map with the values that should be cached.
+ * Ideally should be run in a @PostConstruct
+ */
+ abstract public void init() throws IOException;
+
+ /**
+ * Retrieves the value from the map using the key
+ *
+ * @param key the key to the value
+ */
+ public T get(String key) {
+ return map.get(key);
+ }
+}
diff --git a/src/main/java/cc/wordview/api/runtime/ImageCache.java b/src/main/java/cc/wordview/api/runtime/ImageCache.java
new file mode 100644
index 0000000..cf7f2c7
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/ImageCache.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import cc.wordview.api.exception.ImageNotFoundException;
+import cc.wordview.api.util.WordViewResourceResolver;
+import lombok.SneakyThrows;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+@Component
+public class ImageCache extends HashMapCacheManager {
+ private static final Logger logger = LoggerFactory.getLogger(ImageCache.class);
+
+ @Autowired
+ private WordViewResourceResolver resourceResolver;
+
+ @Override
+ public void init() throws IOException {
+ String imagesPath = resourceResolver.getImagesPath();
+ Path filepath = Path.of(imagesPath);
+
+ try (Stream paths = Files.walk(filepath)) {
+ paths.filter(Files::isRegularFile)
+ .forEach(file -> {
+ try (InputStream stream = new FileInputStream(file.toString())) {
+ String[] parts = file.toString().split("/");
+ String imageName = parts[parts.length - 1].replace(".png", "");
+
+ map.put(imageName, IOUtils.toByteArray(stream));
+ } catch (Exception e) {
+ logger.error("Failed to load image", e);
+ }
+ });
+
+ logger.info("Preloaded {} images", map.size());
+ } catch (IOException e) {
+ logger.error("Failed to walk through directory: {}", imagesPath, e);
+ }
+ }
+
+ @SneakyThrows(ImageNotFoundException.class)
+ @Override
+ public byte[] get(String key) {
+ byte[] img = super.get(key);
+
+ if (img == null) {
+ throw new ImageNotFoundException("Unable to find a image with this parent");
+ } else return img;
+ }
+}
diff --git a/src/main/java/cc/wordview/api/runtime/LyricsCache.java b/src/main/java/cc/wordview/api/runtime/LyricsCache.java
new file mode 100644
index 0000000..87cc674
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/LyricsCache.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import cc.wordview.api.database.entity.VideoLyrics;
+import cc.wordview.api.service.specification.VideoLyricsServiceInterface;
+import cc.wordview.api.util.WordViewResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class LyricsCache extends HashMapCacheManager {
+ private static final Logger logger = LoggerFactory.getLogger(LyricsCache.class);
+
+ @Autowired
+ private WordViewResourceResolver resourceResolver;
+
+ @Autowired
+ private VideoLyricsServiceInterface videoLyricsService;
+
+ @Override
+ public void init() throws IOException {
+ String lyricsPath = resourceResolver.getLyricsPath();
+ Path filepath = Path.of(lyricsPath);
+
+ Map fileToContent = new HashMap<>();
+
+ try {
+ Files.walk(filepath)
+ .filter(Files::isRegularFile)
+ .forEach(file -> {
+ try {
+ fileToContent.put(file.getFileName().toString(), Files.readString(file));
+ } catch (IOException e) {
+ logger.error("Failed to read lyrics file, ignoring this file", e);
+ }
+ });
+
+
+ List videoLyrics = videoLyricsService.getAll();
+
+ for (var videoLyric : videoLyrics) {
+ String content = fileToContent.get(videoLyric.getLyricsFile() + ".vtt");
+ map.put(videoLyric.getVideoId(), content);
+ }
+
+ logger.info("Preloaded {} lyrics", map.size());
+ } catch (IOException e) {
+ logger.error("Failed to read the lyrics path", e);
+ }
+ }
+
+ public void put(String id, String lyrics) {
+ if (map.containsKey(id))
+ return;
+
+ logger.info("Adding {} to cache, there are now {} lyrics", id, map.size() + 1);
+ map.put(id, lyrics);
+ }
+}
diff --git a/src/main/java/cc/wordview/api/runtime/PhraseCache.java b/src/main/java/cc/wordview/api/runtime/PhraseCache.java
new file mode 100644
index 0000000..733b45d
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/PhraseCache.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import cc.wordview.api.exception.NoSuchEntryException;
+import cc.wordview.api.service.util.Phrase;
+import cc.wordview.api.service.util.SimplePhrase;
+import cc.wordview.api.util.ArrayUtil;
+import cc.wordview.api.util.FileHelper;
+import cc.wordview.api.util.WordViewResourceResolver;
+import com.google.gson.Gson;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class PhraseCache extends ArrayCacheManager {
+ private static final Logger logger = LoggerFactory.getLogger(PhraseCache.class);
+
+ @Autowired
+ private WordViewResourceResolver resourceResolver;
+
+ @Override
+ public void init() throws IOException {
+ String phrasesPath = resourceResolver.getPhrasesPath();
+ List phraseFiles = Files.list(Path.of(phrasesPath)).toList();
+
+ for (Path filePath : phraseFiles) {
+ String content = FileHelper.read(filePath.toFile());
+ array.add(new Gson().fromJson(content, Phrase.class));
+ }
+
+ logger.info("Preloaded {} phrases", array.size());
+ }
+
+ /**
+ * Fetches from the cache and returns a randomly picked SimplePhrase serialized as a JSON string
+ */
+ public String getRandomPhrase(String phraseLang, String wordsLang, String keyword) throws NoSuchEntryException {
+ ArrayList possibilities = new ArrayList<>();
+
+ for (Phrase phrase : array) {
+ List words = phrase.getWords().getFirst().get(wordsLang);
+ String phraseText = phrase.getPhrases().get(phraseLang);
+
+ if (phraseText == null) continue;
+ if (words == null) continue;
+ if (!words.contains(keyword)) continue;
+
+ possibilities.add(new SimplePhrase(phraseText, words));
+ }
+
+ if (possibilities.isEmpty())
+ throw new NoSuchEntryException("Could not find any phrases matching these parameters");
+
+ SimplePhrase picked = ArrayUtil.random(possibilities);
+
+ return new Gson().toJson(picked);
+ }
+
+ public ArrayList getMultiplePhrases(String phraseLang, String wordsLang, List keywords) throws NoSuchEntryException {
+ ArrayList phrases = new ArrayList<>();
+
+ for (String keyword : keywords) {
+ try {
+ String phrase = getRandomPhrase(phraseLang, wordsLang, keyword);
+ phrases.add(phrase);
+ } catch (NoSuchEntryException ignored) {
+ // skip as not finding one phrase shouldn't stop the flow
+ }
+ }
+
+ if (phrases.isEmpty())
+ throw new NoSuchEntryException("Couldn't find any phrases matching these keywords");
+
+ return phrases;
+ }
+}
diff --git a/src/main/java/cc/wordview/api/runtime/TranslationCache.java b/src/main/java/cc/wordview/api/runtime/TranslationCache.java
new file mode 100644
index 0000000..d1eda58
--- /dev/null
+++ b/src/main/java/cc/wordview/api/runtime/TranslationCache.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2025 Arthur Araujo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package cc.wordview.api.runtime;
+
+import cc.wordview.api.service.util.SimpleTranslation;
+import cc.wordview.api.service.util.Translation;
+import cc.wordview.api.util.FileHelper;
+import cc.wordview.api.util.WordViewResourceResolver;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class TranslationCache extends ArrayCacheManager {
+ private static final Logger logger = LoggerFactory.getLogger(TranslationCache.class);
+
+ @Autowired
+ private WordViewResourceResolver resourceResolver;
+
+ @Override
+ public void init() throws IOException {
+ String translationsPath = resourceResolver.getTranslationsPath();
+
+ List translationFiles = Files.list(Path.of(translationsPath)).toList();
+
+ for (Path filePath : translationFiles) {
+ String content = FileHelper.read(filePath.toFile());
+
+ Type listType = new TypeToken>() {}.getType();
+ List translationList = new Gson().fromJson(content, listType);
+
+ array.addAll(translationList);
+ }
+
+ logger.info("Preloaded {} translations", array.size());
+ }
+
+ public ArrayList getTranslations(String lang, List words) {
+ ArrayList reqTranslations = new ArrayList<>();
+
+ for (Translation translation : array) {
+ String translatedWord = null;
+
+ for (var aaa : translation.getTranslations()) {
+ var tword = aaa.get(lang);
+ if (tword != null) translatedWord = tword;
+ }
+
+ // it has no translations, skip
+ if (translatedWord == null) continue;
+
+ if (words.contains(translation.getParent())) {
+ reqTranslations.add(new SimpleTranslation(
+ translation.getParent(),
+ translatedWord
+ ));
+ }
+ }
+
+ return reqTranslations;
+ }
+}
diff --git a/src/main/java/cc/wordview/api/service/LessonService.java b/src/main/java/cc/wordview/api/service/LessonService.java
index d796ca3..c6d930c 100644
--- a/src/main/java/cc/wordview/api/service/LessonService.java
+++ b/src/main/java/cc/wordview/api/service/LessonService.java
@@ -21,27 +21,17 @@
import cc.wordview.api.database.entity.User;
import cc.wordview.api.exception.NoSuchEntryException;
import cc.wordview.api.repository.KnownWordsRepository;
+import cc.wordview.api.runtime.PhraseCache;
+import cc.wordview.api.runtime.TranslationCache;
import cc.wordview.api.service.specification.LessonServiceInterface;
-import cc.wordview.api.service.util.Phrase;
-import cc.wordview.api.service.util.SimplePhrase;
import cc.wordview.api.service.util.SimpleTranslation;
-import cc.wordview.api.service.util.Translation;
import cc.wordview.api.util.ArrayUtil;
-import cc.wordview.api.util.FileHelper;
-import cc.wordview.api.util.WordViewResourceResolver;
import cc.wordview.gengolex.Language;
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
import jakarta.annotation.PostConstruct;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
-import java.lang.reflect.Type;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -49,81 +39,28 @@
@Service
public class LessonService implements LessonServiceInterface {
- private static final Logger logger = LoggerFactory.getLogger(LessonService.class);
-
@Autowired
private KnownWordsRepository repository;
@Autowired
- private WordViewResourceResolver resourceResolver;
+ private PhraseCache phraseCache;
- private final ArrayList phrases = new ArrayList<>();
- private final ArrayList translations = new ArrayList<>();
+ @Autowired
+ private TranslationCache translationCache;
@Override
- public String getPhrase(String phraseLang, String wordsLang, String keyword) throws IOException, NoSuchEntryException {
- ArrayList availablePhrases = new ArrayList<>();
-
- for (Phrase phrase : phrases) {
- List words = phrase.getWords().getFirst().get(wordsLang);
- String phraseText = phrase.getPhrases().get(phraseLang);
-
- if (phraseText == null) continue;
- if (words == null) continue;
- if (!words.contains(keyword)) continue;
-
- availablePhrases.add(new SimplePhrase(phraseText, words));
- }
-
- if (availablePhrases.isEmpty())
- throw new NoSuchEntryException("Could not find any phrases matching these parameters");
-
- SimplePhrase chosen = ArrayUtil.random(availablePhrases);
-
- return new Gson().toJson(chosen);
+ public String getPhrase(String phraseLang, String wordsLang, String keyword) throws NoSuchEntryException {
+ return phraseCache.getRandomPhrase(phraseLang, wordsLang, keyword);
}
@Override
- public ArrayList getPhrases(String phraseLang, String wordsLang, List keywords) throws IOException, NoSuchEntryException {
- ArrayList phrases = new ArrayList<>();
-
- for (String keyword : keywords) {
- try {
- String phrase = getPhrase(phraseLang, wordsLang, keyword);
- phrases.add(phrase);
- } catch (NoSuchEntryException ignored) {}
- }
-
- if (phrases.isEmpty())
- throw new NoSuchEntryException("Couldn't find any phrases matching these keywords");
-
- return phrases;
+ public ArrayList getPhrases(String phraseLang, String wordsLang, List keywords) throws NoSuchEntryException {
+ return phraseCache.getMultiplePhrases(phraseLang, wordsLang, keywords);
}
@Override
public ArrayList getTranslations(String lang, List words) {
- ArrayList reqTranslations = new ArrayList<>();
-
- for (Translation translation : translations) {
- String translatedWord = null;
-
- for (var aaa : translation.getTranslations()) {
- var tword = aaa.get(lang);
- if (tword != null) translatedWord = tword;
- }
-
- // it has no translations, skip
- if (translatedWord == null) continue;
-
- if (words.contains(translation.getParent())) {
- reqTranslations.add(new SimpleTranslation(
- translation.getParent(),
- translatedWord
- ));
- }
- }
-
- return reqTranslations;
+ return translationCache.getTranslations(lang, words);
}
@Override
@@ -169,35 +106,8 @@ private void insertKnownWords(KnownWords entity) {
}
@PostConstruct
- private void preloadPhrases() throws IOException {
- String phrasesPath = resourceResolver.getPhrasesPath();
-
- List phraseFiles = Files.list(Path.of(phrasesPath)).toList();
-
- for (Path filePath : phraseFiles) {
- String content = FileHelper.read(filePath.toFile());
-
- logger.info("Loading phrase \"{}\"", filePath.getFileName().toString());
-
- phrases.add(new Gson().fromJson(content, Phrase.class));
- }
- }
-
- @PostConstruct
- private void preloadTranslations() throws IOException {
- String translationsPath = resourceResolver.getTranslationsPath();
-
- List translationFiles = Files.list(Path.of(translationsPath)).toList();
-
- for (Path filePath : translationFiles) {
- String content = FileHelper.read(filePath.toFile());
-
- logger.info("Loading translation \"{}\"", filePath.getFileName().toString());
-
- Type listType = new TypeToken>(){}.getType();
- List translationList = new Gson().fromJson(content, listType);
-
- translations.addAll(translationList);
- }
+ private void preload() throws IOException {
+ phraseCache.init();
+ translationCache.init();
}
}
diff --git a/src/main/java/cc/wordview/api/service/LyricsService.java b/src/main/java/cc/wordview/api/service/LyricsService.java
index 2ef88f4..5bb4c14 100644
--- a/src/main/java/cc/wordview/api/service/LyricsService.java
+++ b/src/main/java/cc/wordview/api/service/LyricsService.java
@@ -17,12 +17,9 @@
package cc.wordview.api.service;
-import cc.wordview.api.database.entity.VideoLyrics;
-import cc.wordview.api.exception.NoSuchEntryException;
+import cc.wordview.api.runtime.LyricsCache;
import cc.wordview.api.service.specification.LyricsServiceInterface;
-import cc.wordview.api.service.specification.VideoLyricsServiceInterface;
import cc.wordview.api.util.DownloaderImpl;
-import cc.wordview.api.util.WordViewResourceResolver;
import cc.wordview.wordfind.exception.LyricsNotFoundException;
import cc.wordview.wordfind.WordFind;
import jakarta.annotation.PostConstruct;
@@ -31,38 +28,25 @@
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Objects;
@Service
public class LyricsService implements LyricsServiceInterface {
- private static final Logger logger = LoggerFactory.getLogger(LyricsService.class);
-
private static StreamingService YTService;
private final WordFind client = new WordFind();
- private final Map lyrics = new HashMap<>();
-
@Autowired
- private WordViewResourceResolver resourceResolver;
-
- @Autowired
- private VideoLyricsServiceInterface videoLyricsService;
+ private LyricsCache cache;
@Override
public String getLyrics(String id, String trackName, String artistName, String langTag) throws IOException, LyricsNotFoundException {
- String lyrics = getLyricsWordView(id);
+ String lyrics = cache.get(id);
if (lyrics == null)
lyrics = getLyricsYT(id, langTag);
@@ -75,6 +59,7 @@ public String getLyrics(String id, String trackName, String artistName, String l
throw new LyricsNotFoundException("Unable to find lyrics for %s".formatted(trackName));
}
+ cache.put(id, lyrics);
return lyrics;
}
@@ -101,17 +86,6 @@ private String getLyricsYT(String id, String langTag) {
return null;
}
- private String getLyricsWordView(String id) {
- VideoLyrics videoLyrics = null;
-
- try {
- videoLyrics = videoLyricsService.getByVideoId(id);
- } catch (NoSuchEntryException ignored) {}
-
- if (videoLyrics == null) return null;
- else return lyrics.get(videoLyrics.getLyricsFile() + ".vtt");
- }
-
@PostConstruct
private void initNewPipe() throws ExtractionException {
DownloaderImpl.init(null);
@@ -122,22 +96,7 @@ private void initNewPipe() throws ExtractionException {
@PostConstruct
private void preloadLyrics() throws IOException {
- String lyricsPath = resourceResolver.getLyricsPath();
-
- try {
- Files.walk(Path.of(lyricsPath))
- .filter(Files::isRegularFile)
- .forEach(file -> {
- try {
- lyrics.put(file.getFileName().toString(), Files.readString(file));
- logger.info("Loading lyrics file \"{}\"", file.getFileName().toString());
- } catch (IOException e) {
- logger.error("Failed to read lyrics file", e);
- }
- });
- } catch (IOException e) {
- logger.error("Failed to read the lyrics directory", e);
- }
+ cache.init();
}
@Override
diff --git a/src/main/java/cc/wordview/api/service/VideoLyricsService.java b/src/main/java/cc/wordview/api/service/VideoLyricsService.java
index 5905118..fac82b9 100644
--- a/src/main/java/cc/wordview/api/service/VideoLyricsService.java
+++ b/src/main/java/cc/wordview/api/service/VideoLyricsService.java
@@ -25,6 +25,7 @@
import org.springframework.stereotype.Service;
import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import static org.antlr.v4.runtime.tree.xpath.XPath.findAll;
@@ -57,6 +58,11 @@ public ArrayList listLyricsIds() {
return ids;
}
+ @Override
+ public List getAll() {
+ return (List) repository.findAll();
+ }
+
@Override
public VideoLyrics getById(Long id) {
diff --git a/src/main/java/cc/wordview/api/service/specification/VideoLyricsServiceInterface.java b/src/main/java/cc/wordview/api/service/specification/VideoLyricsServiceInterface.java
index 142747c..becba34 100644
--- a/src/main/java/cc/wordview/api/service/specification/VideoLyricsServiceInterface.java
+++ b/src/main/java/cc/wordview/api/service/specification/VideoLyricsServiceInterface.java
@@ -21,8 +21,10 @@
import cc.wordview.api.exception.NoSuchEntryException;
import java.util.ArrayList;
+import java.util.List;
public interface VideoLyricsServiceInterface extends ServiceInterface {
VideoLyrics getByVideoId(String videoId) throws NoSuchEntryException;
ArrayList listLyricsIds();
+ List getAll();
}
diff --git a/src/test/java/cc/wordview/api/test/api/controller/LyricsControllerTest.java b/src/test/java/cc/wordview/api/test/api/controller/LyricsControllerTest.java
index 212496d..d97e9a0 100644
--- a/src/test/java/cc/wordview/api/test/api/controller/LyricsControllerTest.java
+++ b/src/test/java/cc/wordview/api/test/api/controller/LyricsControllerTest.java
@@ -36,7 +36,7 @@ void getLyrics() throws Exception {
}
@Test
- @Disabled("Does not make external request but don't seem to work on CI")
+// @Disabled("Does not make external request but don't seem to work on CI")
void getLyricsWordView() throws Exception {
req.get("/lyrics?id=ZnUEeXpxBJ0&lang=pt&trackName=aquarela&artistName=toquinho")
.andExpect(status().isOk())