From 256d2f76670eb94d2419f558c2bf8c708617257d Mon Sep 17 00:00:00 2001 From: Louis Vallat Date: Wed, 28 Oct 2020 22:44:51 +0100 Subject: [PATCH] Listing by title now works Signed-off-by: Louis Vallat --- .../xyz/vallat/louis/commands/Command.java | 66 +++++++++++++++++++ .../{ListLang.java => ListLangMovieImdb.java} | 15 ++--- .../louis/commands/ListLangMovieTitle.java | 39 +++++++++++ .../vallat/louis/commands/SearchTitle.java | 6 -- .../vallat/louis/discord/DiscordManager.java | 3 +- .../xyz/vallat/louis/omdb/OMDBClient.java | 22 ++++--- .../xyz/vallat/louis/omdb/objects/Movie.java | 14 ++-- 7 files changed, 133 insertions(+), 32 deletions(-) rename src/main/java/xyz/vallat/louis/commands/{ListLang.java => ListLangMovieImdb.java} (87%) create mode 100644 src/main/java/xyz/vallat/louis/commands/ListLangMovieTitle.java diff --git a/src/main/java/xyz/vallat/louis/commands/Command.java b/src/main/java/xyz/vallat/louis/commands/Command.java index 4060d3b..af5fc53 100644 --- a/src/main/java/xyz/vallat/louis/commands/Command.java +++ b/src/main/java/xyz/vallat/louis/commands/Command.java @@ -1,9 +1,29 @@ package xyz.vallat.louis.commands; +import com.github.wtekiela.opensub4j.response.ListResponse; +import com.github.wtekiela.opensub4j.response.ResponseStatus; +import com.github.wtekiela.opensub4j.response.SubtitleInfo; import discord4j.core.event.domain.message.MessageCreateEvent; +import discord4j.core.spec.EmbedCreateSpec; +import discord4j.rest.util.Color; +import org.apache.xmlrpc.XmlRpcException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; +import xyz.vallat.louis.omdb.OMDBClient; +import xyz.vallat.louis.omdb.objects.Movie; +import xyz.vallat.louis.subtitles.OpenSubtitles; + +import java.io.IOException; +import java.util.Comparator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; public abstract class Command { + private static final Logger logger = LoggerFactory.getLogger(ListLangMovieTitle.class.getCanonicalName()); protected final String name; protected final String description; @@ -19,6 +39,11 @@ public abstract class Command { this.maxArgs = maxArgs; } + public static Predicate distinctByKey(Function keyExtractor) { + Map map = new ConcurrentHashMap<>(); + return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } + public abstract Mono execute(MessageCreateEvent event); public String getUsage() { @@ -36,4 +61,45 @@ public abstract class Command { public int getMaxArgs() { return maxArgs; } + + protected Mono notEnoughArguments(MessageCreateEvent event) { + return event.getMessage().getChannel() + .flatMap(channel -> channel.createMessage("Error, you're missing some arguments.\n" + getUsage())) + .then(); + } + + protected void createEmbedListLang(String arg, EmbedCreateSpec embed, boolean isId) { + try { + Movie movie = OMDBClient.getMovie(arg, isId); + ListResponse subtitles = + OpenSubtitles.searchSubtitles("", + String.valueOf(movie.getNumericImdbId())); + if (subtitles.getStatus().equals(ResponseStatus.OK)) { + embed.setColor(Color.BISMARK); + embed.setDescription("You requested a list of the languages available for a movie with this " + + (isId ? "IMDB Identifier" : "title") + " '" + arg + "'. " + subtitles.getData().size() + + " subtitles were found."); + embed.addField("Top 20 distinct languages (based on downloads)", subtitles.getData().stream() + .sorted(Comparator.comparingInt(SubtitleInfo::getDownloadsNo)) + .filter(distinctByKey(SubtitleInfo::getLanguage)) + .map(subtitleInfo -> subtitleInfo.getSubtitleFileId() + + " - " + subtitleInfo.getLanguage()) + .limit(20) + .collect(Collectors.joining("\n")), false); + } else if (subtitles.getStatus().equals(ResponseStatus.INVALID_IMDB_ID)) { + embed.setColor(Color.ORANGE).setDescription("It looks like this IMDB Identifier either isn't valid " + + "or is not in the database."); + } else { + logger.error("Cannot get subtitle list. Code returned: {}", subtitles.getStatus()); + embed.setDescription("An error occurred. " + + "Try again later or contact my administrator."); + } + } catch (IOException | XmlRpcException e) { + logger.error("A network error happened: {}", e.getMessage()); + logger.warn("It may be due to a website being down. If it's not" + + "the case, please fix this issue quickly."); + embed.setDescription("An error occurred. " + + "Try again later or contact my administrator."); + } + } } diff --git a/src/main/java/xyz/vallat/louis/commands/ListLang.java b/src/main/java/xyz/vallat/louis/commands/ListLangMovieImdb.java similarity index 87% rename from src/main/java/xyz/vallat/louis/commands/ListLang.java rename to src/main/java/xyz/vallat/louis/commands/ListLangMovieImdb.java index dd13d9b..c710a57 100644 --- a/src/main/java/xyz/vallat/louis/commands/ListLang.java +++ b/src/main/java/xyz/vallat/louis/commands/ListLangMovieImdb.java @@ -12,19 +12,18 @@ import org.apache.xmlrpc.XmlRpcException; import reactor.core.publisher.Mono; import xyz.vallat.louis.subtitles.OpenSubtitles; -public class ListLang extends Command { +public class ListLangMovieImdb extends Command { private static final ReactionEmoji WAITING = ReactionEmoji.unicode("⌛"); - public ListLang(String name) { - super(name, "List all languages attached to a film.", name + " IMDBid", 1, 2); + public ListLangMovieImdb(String name) { + super(name, "List all languages attached to a film using its IMDB Identifier.", name + " IMDB_ID", 1, 2); } @Override public Mono execute(MessageCreateEvent event) { String arg = event.getMessage().getContent().substring(name.length() + 1); - if (arg.replace(" ", "").length() == 0) - return notEnoughArguments(event); + if (arg.isBlank()) return notEnoughArguments(event); return event.getMessage().getChannel() .flatMap(channel -> { if (OpenSubtitles.isLoggedIn()) { @@ -38,12 +37,6 @@ public class ListLang extends Command { .then(); } - private Mono notEnoughArguments(MessageCreateEvent event) { - return event.getMessage().getChannel() - .flatMap(channel -> channel.createMessage("Error.\n" + getUsage())) - .then(); - } - // TODO: ADD MOVIE TITLE private Mono searchAndShowFilms(String arg, MessageChannel channel, MessageChannel messageChannel) { try { diff --git a/src/main/java/xyz/vallat/louis/commands/ListLangMovieTitle.java b/src/main/java/xyz/vallat/louis/commands/ListLangMovieTitle.java new file mode 100644 index 0000000..5d9e578 --- /dev/null +++ b/src/main/java/xyz/vallat/louis/commands/ListLangMovieTitle.java @@ -0,0 +1,39 @@ +package xyz.vallat.louis.commands; + +import discord4j.core.event.domain.message.MessageCreateEvent; +import discord4j.core.object.reaction.ReactionEmoji; +import discord4j.rest.util.Color; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import xyz.vallat.louis.subtitles.OpenSubtitles; + +public class ListLangMovieTitle extends Command { + + private static final Logger logger = LoggerFactory.getLogger(ListLangMovieTitle.class.getCanonicalName()); + private static final ReactionEmoji WAITING = ReactionEmoji.unicode("⌛"); + + public ListLangMovieTitle(String name) { + super(name, "List all languages attached to a film title.", name + " title", 1, 2); + } + + @Override + public Mono execute(MessageCreateEvent event) { + String arg = event.getMessage().getContent().substring(name.length() + 1); + if (arg.isBlank()) return notEnoughArguments(event); + logger.debug("Executing command with argument '{}'.", arg); + return event.getMessage().getChannel() + .flatMap(channel -> event.getMessage().addReaction(WAITING).then(event.getMessage().getChannel() + .flatMap(messageChannel -> messageChannel.createEmbed(embed -> { + embed.setTitle("Subtitle languages").setColor(Color.RED); + if (OpenSubtitles.isLoggedIn()) { + createEmbedListLang(arg, embed, false); + } else { + logger.error("Not logged in on OpenSubtitles!"); + logger.warn("It may be due to a website being down. If it's not" + + "the case, please fix this issue quickly."); + embed.setDescription("I cannot search for subtitle languages right now. Sorry."); + } + })))).then(event.getMessage().removeSelfReaction(WAITING)).then(); + } +} diff --git a/src/main/java/xyz/vallat/louis/commands/SearchTitle.java b/src/main/java/xyz/vallat/louis/commands/SearchTitle.java index 9a9d9c7..56b7d44 100644 --- a/src/main/java/xyz/vallat/louis/commands/SearchTitle.java +++ b/src/main/java/xyz/vallat/louis/commands/SearchTitle.java @@ -60,10 +60,4 @@ public class SearchTitle extends Command { return channel.createMessage("It seems like OpenSubtitles had a stroke."); } } - - private Mono notEnoughArguments(MessageCreateEvent event) { - return event.getMessage().getChannel() - .flatMap(channel -> channel.createMessage("Error.\n" + getUsage())) - .then(); - } } diff --git a/src/main/java/xyz/vallat/louis/discord/DiscordManager.java b/src/main/java/xyz/vallat/louis/discord/DiscordManager.java index 6a3f7b6..f7e4316 100644 --- a/src/main/java/xyz/vallat/louis/discord/DiscordManager.java +++ b/src/main/java/xyz/vallat/louis/discord/DiscordManager.java @@ -26,7 +26,8 @@ public final class DiscordManager { static { commands.put("ping", new Ping(PREFIX + "ping")); commands.put("version", new Version(PREFIX + "version")); - commands.put("listLang", new ListLang(PREFIX + "listLang")); + commands.put("listLangMovieImdb", new ListLangMovieImdb(PREFIX + "listLangMovieImdb")); + commands.put("listLangMovie", new ListLangMovieTitle(PREFIX + "listLangMovie")); commands.put("download", new Download(PREFIX + "download")); } diff --git a/src/main/java/xyz/vallat/louis/omdb/OMDBClient.java b/src/main/java/xyz/vallat/louis/omdb/OMDBClient.java index cca277d..edb49a7 100644 --- a/src/main/java/xyz/vallat/louis/omdb/OMDBClient.java +++ b/src/main/java/xyz/vallat/louis/omdb/OMDBClient.java @@ -60,7 +60,7 @@ public final class OMDBClient { return movies; } - public static Movie getMovie(String value, boolean isId) throws IOException, InterruptedException { + public static Movie getMovie(String value, boolean isId) throws IOException { logger.debug("Getting movie by title '{}'.", value); Movie movie = FilmManager.getMovieFromTitleOrImdbId(value, isId); if (movie != null) { @@ -72,15 +72,19 @@ public final class OMDBClient { .uri(URI.create(ENDPOINT + "?apikey=" + API_KEY + "&type=movie" + "&" + (isId ? "i" : "t") + "=" + URLEncoder.encode(value, StandardCharsets.UTF_8))) .build(); - HttpResponse response = getHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 200) { - JsonObject jsonMovie = JsonParser.parseString(response.body()).getAsJsonObject(); - if (jsonMovie.get("Response").getAsBoolean()) { - movie = getMovieFromJson(jsonMovie); + try { + HttpResponse response = getHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() == 200) { + JsonObject jsonMovie = JsonParser.parseString(response.body()).getAsJsonObject(); + if (jsonMovie.get("Response").getAsBoolean()) { + movie = getMovieFromJson(jsonMovie); + } else + logger.error("OMDB API returned an error: {}", jsonMovie.get("Error").getAsString()); } else - logger.error("OMDB API returned an error: {}", jsonMovie.get("Error").getAsString()); - } else - logger.error("Querying the results gave this code: {}.", response.statusCode()); + logger.error("Querying the results gave this code: {}.", response.statusCode()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } return movie; } diff --git a/src/main/java/xyz/vallat/louis/omdb/objects/Movie.java b/src/main/java/xyz/vallat/louis/omdb/objects/Movie.java index 3f570a2..23ec274 100644 --- a/src/main/java/xyz/vallat/louis/omdb/objects/Movie.java +++ b/src/main/java/xyz/vallat/louis/omdb/objects/Movie.java @@ -17,11 +17,6 @@ public class Movie { this.poster = poster; } - public Movie setYear(int year) { - this.year = year; - return this; - } - public int getId() { return this.id; } @@ -31,6 +26,10 @@ public class Movie { return this; } + public int getNumericImdbId() { + return Integer.parseInt(this.imdbID.substring(2)); + } + public String getTitle() { return title; } @@ -39,6 +38,11 @@ public class Movie { return year; } + public Movie setYear(int year) { + this.year = year; + return this; + } + public String getImdbID() { return imdbID; }