Merge pull request 'improvements' (#25) from improvements into main
Reviewed-on: #25
This commit is contained in:
commit
7dbb019345
@ -14,11 +14,11 @@ public interface MangaDexClient {
|
||||
MangaDexMangaDTO getManga(@PathVariable UUID id);
|
||||
|
||||
@GetMapping("/manga/{id}/feed")
|
||||
MangaDexMangaFeedDTO getMangaFeed(@PathVariable UUID id);
|
||||
MangaDexMangaFeedDTO getMangaFeed(@PathVariable UUID id, @RequestParam("contentRating[]") List<String> contentRating);
|
||||
|
||||
@GetMapping("/manga/{id}/feed")
|
||||
MangaDexMangaFeedDTO getMangaFeed(
|
||||
@PathVariable UUID id, @RequestParam int limit, @RequestParam int offset);
|
||||
@PathVariable UUID id, @RequestParam int limit, @RequestParam int offset, @RequestParam("contentRating[]") List<String> contentRating);
|
||||
|
||||
@GetMapping("/at-home/server/{chapterId}")
|
||||
MangaChapterDataDTO getMangaChapter(@PathVariable UUID chapterId);
|
||||
|
||||
@ -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) {}
|
||||
|
||||
@ -4,4 +4,4 @@ import com.magamochi.mangamochi.model.enumeration.MangaStatus;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record ContentProviderMangaInfoResponseDTO(
|
||||
@NotBlank String title, @NotBlank String url, String imgUrl, MangaStatus status) {}
|
||||
@NotBlank String title, @NotBlank String url, MangaStatus status) {}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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 ->
|
||||
|
||||
@ -15,6 +15,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
@Log4j2
|
||||
@Service(ContentProviders.MANGA_DEX)
|
||||
@RequiredArgsConstructor
|
||||
@ -27,13 +29,12 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro
|
||||
public List<ContentProviderMangaChapterResponseDTO> getAvailableChapters(MangaProvider provider) {
|
||||
try {
|
||||
mangaDexRateLimiter.acquire();
|
||||
var response = mangaDexClient.getMangaFeed(UUID.fromString(provider.getUrl()));
|
||||
var response = mangaDexClient.getMangaFeed(UUID.fromString(provider.getUrl()), List.of("safe", "suggestive", "erotica", "pornographic"));
|
||||
|
||||
var mangas = new ArrayList<>(response.data());
|
||||
var totalPages = (int) Math.ceil((double) response.total() / 500);
|
||||
|
||||
try {
|
||||
|
||||
IntStream.range(1, totalPages)
|
||||
.parallel()
|
||||
.forEach(
|
||||
@ -41,7 +42,7 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro
|
||||
mangaDexRateLimiter.acquire();
|
||||
|
||||
var pagedResponse =
|
||||
mangaDexClient.getMangaFeed(UUID.fromString(provider.getUrl()), 500, i * 500);
|
||||
mangaDexClient.getMangaFeed(UUID.fromString(provider.getUrl()), 500, i * 500, List.of("safe", "suggestive", "erotica", "pornographic"));
|
||||
|
||||
mangas.addAll(pagedResponse.data());
|
||||
});
|
||||
@ -49,16 +50,22 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro
|
||||
log.warn(e.getMessage());
|
||||
}
|
||||
|
||||
// TODO this is filtering only pt-br chapters for now, we may want to make this configurable
|
||||
// NOTE: this is getting only pt-br and en chapters for now, we may want to make this configurable
|
||||
// later
|
||||
var languagesToImport = Map.of("pt-br", "pt-BR", "en", "en-US");
|
||||
|
||||
return mangas.stream()
|
||||
.filter(
|
||||
c ->
|
||||
c.type().equals("chapter")
|
||||
&& c.attributes().isUnavailable().equals(Boolean.FALSE)
|
||||
&& c.attributes().translatedLanguage().equals("pt-br"))
|
||||
&& languagesToImport.containsKey(c.attributes().translatedLanguage()))
|
||||
.sorted(
|
||||
(o1, o2) -> {
|
||||
if (isNull(o1.attributes().chapter()) || isNull(o2.attributes().chapter())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
Float chapter1 = Float.parseFloat(o1.attributes().chapter());
|
||||
Float chapter2 = Float.parseFloat(o2.attributes().chapter());
|
||||
@ -73,7 +80,7 @@ public class MangaDexProvider implements ContentProvider, ManualImportContentPro
|
||||
c.attributes().chapter() + " - " + c.attributes().title(),
|
||||
c.id().toString(),
|
||||
c.attributes().chapter(),
|
||||
c.attributes().translatedLanguage()))
|
||||
languagesToImport.get(c.attributes().translatedLanguage())))
|
||||
.toList();
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
|
||||
@ -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) {
|
||||
@ -121,12 +121,6 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv
|
||||
var contentContainer =
|
||||
linkElement.getElementsByClass("manga-card-content").getFirst();
|
||||
|
||||
var imageUrl =
|
||||
imageContainer
|
||||
.getElementsByClass("attachment-manga-cover")
|
||||
.getFirst()
|
||||
.attr("data-lazy-src");
|
||||
|
||||
var title = contentContainer.getElementsByTag("h3").text();
|
||||
var url = linkElement.attr("href");
|
||||
var status =
|
||||
@ -140,7 +134,7 @@ public class MangaLivreBlogProvider implements ContentProvider, PagedContentProv
|
||||
default -> MangaStatus.UNKNOWN;
|
||||
};
|
||||
|
||||
return new ContentProviderMangaInfoResponseDTO(title, url, imageUrl, status);
|
||||
return new ContentProviderMangaInfoResponseDTO(title, url, status);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
@ -116,7 +116,7 @@ public class MangaLivreProvider implements ContentProvider, PagedContentProvider
|
||||
}
|
||||
|
||||
return new ContentProviderMangaInfoResponseDTO(
|
||||
title.trim(), url.trim(), null, MangaStatus.UNKNOWN);
|
||||
title.trim(), url.trim(), MangaStatus.UNKNOWN);
|
||||
})
|
||||
.toList();
|
||||
} catch (NoSuchElementException e) {
|
||||
|
||||
@ -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) {
|
||||
@ -138,8 +141,7 @@ public class PinkRosaScanProvider implements ContentProvider, PagedContentProvid
|
||||
var textElement = linkElement.getElementsByTag("h3");
|
||||
var title = textElement.text().trim();
|
||||
|
||||
return new ContentProviderMangaInfoResponseDTO(
|
||||
title, url, null, MangaStatus.UNKNOWN);
|
||||
return new ContentProviderMangaInfoResponseDTO(title, url, MangaStatus.UNKNOWN);
|
||||
})
|
||||
.toList();
|
||||
} catch (NoSuchElementException e) {
|
||||
|
||||
40
src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql
Normal file
40
src/main/resources/db/migration/V0019__CHAPTER_LANGUAGE.sql
Normal 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;
|
||||
Loading…
x
Reference in New Issue
Block a user