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())