feat(language): add Language entity and update manga chapter language handling

This commit is contained in:
Rodrigo Verdiani 2025-12-31 12:51:36 -03:00
parent 1bb04e5d75
commit 9e44f031d2
13 changed files with 130 additions and 10 deletions

View File

@ -3,4 +3,7 @@ package com.magamochi.mangamochi.model.dto;
import jakarta.validation.constraints.NotBlank;
public record ContentProviderMangaChapterResponseDTO(
@NotBlank String chapterTitle, @NotBlank String chapterUrl, String chapter, String language) {}
@NotBlank String chapterTitle,
@NotBlank String chapterUrl,
String chapter,
String languageCode) {}

View File

@ -0,0 +1,17 @@
package com.magamochi.mangamochi.model.dto;
import static java.util.Objects.isNull;
import com.magamochi.mangamochi.model.entity.Language;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
public record LanguageDTO(@NotNull Long id, @NotBlank String code, @NotBlank String name) {
public static LanguageDTO from(Language language) {
if (isNull(language)) {
return null;
}
return new LanguageDTO(language.getId(), language.getCode(), language.getName());
}
}

View File

@ -8,12 +8,14 @@ public record MangaChapterDTO(
@NotNull Long id,
@NotBlank String title,
@NotNull Boolean downloaded,
@NotNull Boolean isRead) {
@NotNull Boolean isRead,
LanguageDTO language) {
public static MangaChapterDTO from(MangaChapter mangaChapter) {
return new MangaChapterDTO(
mangaChapter.getId(),
mangaChapter.getTitle(),
mangaChapter.getDownloaded(),
mangaChapter.getRead());
mangaChapter.getRead(),
LanguageDTO.from(mangaChapter.getLanguage()));
}
}

View File

@ -0,0 +1,21 @@
package com.magamochi.mangamochi.model.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "languages")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Language {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String code;
private String name;
}

View File

@ -38,7 +38,9 @@ public class MangaChapter {
@OneToMany(mappedBy = "mangaChapter")
private List<MangaChapterImage> mangaChapterImages;
private String language;
private Integer chapterNumber;
@ManyToOne
@JoinColumn(name = "language_id")
private Language language;
}

View File

@ -0,0 +1,9 @@
package com.magamochi.mangamochi.model.repository;
import com.magamochi.mangamochi.model.entity.Language;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface LanguageRepository extends JpaRepository<Language, Long> {
Optional<Language> findByCodeIgnoreCase(String code);
}

View File

@ -0,0 +1,19 @@
package com.magamochi.mangamochi.service;
import com.magamochi.mangamochi.exception.NotFoundException;
import com.magamochi.mangamochi.model.entity.Language;
import com.magamochi.mangamochi.model.repository.LanguageRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LanguageService {
public final LanguageRepository languageRepository;
public Language getOrThrow(String code) {
return languageRepository
.findByCodeIgnoreCase(code)
.orElseThrow(() -> new NotFoundException("Language with code " + code + " not found"));
}
}

View File

@ -32,6 +32,7 @@ public class MangaImportService {
private final ProviderService providerService;
private final MangaCreationService mangaCreationService;
private final ImageService imageService;
private final LanguageService languageService;
private final GenreRepository genreRepository;
private final MangaGenreRepository mangaGenreRepository;
@ -76,7 +77,7 @@ public class MangaImportService {
removeFileExtension(file.getOriginalFilename()),
"manual_" + file.getOriginalFilename(),
file.getOriginalFilename(),
"pt-br"));
"en-US"));
List<MangaChapterImage> allChapterImages = new ArrayList<>();
try (InputStream is = file.getInputStream();
@ -237,7 +238,9 @@ public class MangaImportService {
mangaChapter.setMangaProvider(mangaProvider);
mangaChapter.setTitle(chapter.chapterTitle());
mangaChapter.setUrl(chapter.chapterUrl());
mangaChapter.setLanguage(chapter.language());
var language = languageService.getOrThrow(chapter.languageCode());
mangaChapter.setLanguage(language);
if (nonNull(chapter.chapter())) {
try {

View File

@ -33,6 +33,7 @@ public class BatoProvider implements ContentProvider, ManualImportContentProvide
// Direct selector for chapter links
var chapterLinks = document.select("div.scrollable-panel a[href*=/title/]");
// TODO: fix chapter and language code
return chapterLinks.stream()
.map(
chapterLink ->

View File

@ -51,7 +51,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv
linkElement.getElementsByClass("chapter-number").getFirst();
return new ContentProviderMangaChapterResponseDTO(
chapterNumberElement.text(), linkElement.attr("href"), null, null);
chapterNumberElement.text(), linkElement.attr("href"), null, "pt-BR");
})
.toList();
} catch (IOException | NoSuchElementException e) {

View File

@ -50,7 +50,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider
var title = linkElement.text();
return new ContentProviderMangaChapterResponseDTO(
title.trim(), url.trim(), null, null);
title.trim(), url.trim(), null, "pt-BR");
})
.toList();
} catch (NoSuchElementException e) {

View File

@ -58,7 +58,10 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid
.getElementsByClass("text-sm truncate")
.getFirst();
return new ContentProviderMangaChapterResponseDTO(
chapterTitleElement.text().trim(), chapterItemElement.attr("href"), null, null);
chapterTitleElement.text().trim(),
chapterItemElement.attr("href"),
null,
"pt-BR");
})
.toList();
} catch (NoSuchElementException e) {

View File

@ -0,0 +1,40 @@
CREATE TABLE IF NOT EXISTS languages
(
id SERIAL PRIMARY KEY,
code VARCHAR(12) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL
);
INSERT INTO languages (code, name)
VALUES ('en-US', 'English'),
('es', 'Spanish'),
('ja-JP', 'Japanese'),
('pt-BR', 'Portuguese (Brazil)')
ON CONFLICT DO NOTHING;
ALTER TABLE manga_chapters
ADD COLUMN IF NOT EXISTS language_id BIGINT REFERENCES languages (id);
UPDATE manga_chapters
SET language = NULL;
UPDATE manga_chapters mc
SET language_id = (SELECT id FROM languages WHERE code = 'pt-BR')
FROM manga_provider mp
JOIN providers p ON mp.provider_id = p.id
WHERE mc.manga_provider_id = mp.id
AND mc.language IS NULL
AND p.name ILIKE ANY
(ARRAY ['Manga Livre Blog', 'Pink Rosa Scan', 'Manga Livre.to', 'Manga Livre', 'MangaDex', 'Bato', 'Taimu']);
UPDATE manga_chapters mc
SET language_id = (SELECT id FROM languages WHERE code = 'en-US')
FROM manga_provider mp
JOIN providers p ON mp.provider_id = p.id
WHERE mc.manga_provider_id = mp.id
AND mc.language IS NULL
AND p.name ILIKE ANY
(ARRAY ['Manual Import']);
ALTER TABLE manga_chapters
DROP COLUMN IF EXISTS language;