✨ Add movie descriptions to new movies section
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 7 Mar 2024 19:53:31 +0000 (20:53 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 7 Mar 2024 19:53:31 +0000 (20:53 +0100)
src/main/java/net/pterodactylus/rhynodge/filters/webpages/savoy/Movie.java
src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractor.kt
src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieState.kt
src/test/java/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieExtractorTest.java
src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/savoy/MovieStateTest.kt

index 5e831d8..8d9f1f6 100644 (file)
@@ -23,15 +23,23 @@ public class Movie {
        private final String imageUrl;
 
        @JsonProperty
+       private final String description;
+
+       @JsonProperty
        private final List<Performance> performances = new ArrayList<>();
 
        public Movie() {
-               this("", "");
+               this("", "", "");
        }
 
        public Movie(String name, String imageUrl) {
+               this(name, imageUrl, "");
+       }
+
+       public Movie(String name, String imageUrl, String description) {
                this.name = name;
                this.imageUrl = imageUrl;
+               this.description = description;
        }
 
        public String getName() {
@@ -42,6 +50,10 @@ public class Movie {
                return imageUrl;
        }
 
+       public String getDescription() {
+               return description;
+       }
+
        public List<Performance> getPerformances() {
                return performances;
        }
@@ -52,12 +64,12 @@ public class Movie {
 
        @Override
        public String toString() {
-               return format("%s (%s, %s)", name, imageUrl, performances.stream().map(link -> String.format("%s: %s", link.getTime(), link.getLink())).collect(Collectors.joining(", ")));
+               return format("%s (%s, %s, %s)", name, imageUrl, description, performances.stream().map(link -> String.format("%s: %s", link.getTime(), link.getLink())).collect(Collectors.joining(", ")));
        }
 
        @Override
        public int hashCode() {
-               return Objects.hash(name, imageUrl, performances);
+               return Objects.hash(name, imageUrl, description, performances);
        }
 
        @Override
@@ -65,7 +77,7 @@ public class Movie {
                if (this == o) return true;
                if (o == null || getClass() != o.getClass()) return false;
                Movie movie = (Movie) o;
-               return Objects.equals(name, movie.name) && Objects.equals(imageUrl, movie.imageUrl) && Objects.equals(performances, movie.performances);
+               return Objects.equals(name, movie.name) && Objects.equals(imageUrl, movie.imageUrl) && Objects.equals(description, movie.description) && Objects.equals(performances, movie.performances);
        }
 
 }
index a87d639..39c3ab8 100644 (file)
@@ -25,7 +25,7 @@ class MovieExtractor {
                .replace("&l;", "<")
                .replace("&g;", ">")
 
-       private fun JsonNode.extractMovie() = Movie(get("name").asText(), get("poster").get("original").asText().fixImageUrl()).apply {
+       private fun JsonNode.extractMovie() = Movie(get("name").asText(), get("poster").get("original").asText().fixImageUrl(), get("description_long").asText()).apply {
                this@extractMovie.get("performances")
                        .map { performance ->
                                val begin = LocalDateTime.parse(performance.get("begin").asText(), dateFormat)
index 0d01b20..fa8a9c0 100644 (file)
@@ -37,10 +37,11 @@ class MovieState(@JsonProperty val movies: Collection<Movie>, val newMovies: Col
                                        +"html { font-family: 'Recursive Sans Linear Static', Roboto, serif; }"
                                        +"section.new-movies > .label { font-family: Impact, sans-serif; background-color: black; padding: 0.5ex; font-size: 200%; color: #ffffee; font-weight: bold; }"
                                        +"section.new-movies ul { padding: 0; margin: 0; }"
-                                       +"section.new-movies li.movie { list-style: none; width: 250px; height: 353px; display: inline-block; position: relative; margin: 1ex 1ex 1ex 0ex; }"
-                                       +"section.new-movies li.movie img { width: 100%; height: 100%; display: block; position: absolute; z-index: -1; }"
-                                       +"section.new-movies li.movie .text { color: white; text-shadow: 2px 2px black; font-weight: bold; font-size: 150%; display: flex; flex-direction: column; justify-content: end; width: 100%; height: 100%; }"
+                                       +"section.new-movies li.movie { list-style: none; display: grid; margin: 1ex 1ex 1ex 0ex; grid-template-rows: 353px; grid-template-columns: 250px auto; }"
+                                       +"section.new-movies li.movie img { grid-area: 1/1/2/2 }"
+                                       +"section.new-movies li.movie .text { color: white; text-shadow: 2px 2px black; font-weight: bold; font-size: 150%; display: flex; flex-direction: column; justify-content: end; grid-area: 1/1/2/2; }"
                                        +"section.new-movies li.movie .text .name { padding: 1ex; font-size: 120%; }"
+                                       +"section.new-movies li.movie .description { padding: 1ex; }"
 
                                        +"section.daily-programmes ol { padding: 0; }"
                                        +"section.daily-programmes li { list-style: none; }"
@@ -71,6 +72,9 @@ class MovieState(@JsonProperty val movies: Collection<Movie>, val newMovies: Col
                                                                                +(movie.name)
                                                                        }
                                                                }
+                                                               div("description") {
+                                                                       +movie.description
+                                                               }
                                                        }
                                                }
                                        }
index 63c642a..2b5988e 100644 (file)
@@ -19,7 +19,7 @@ import java.util.Optional;
 import static java.time.LocalDateTime.of;
 import static java.util.Optional.empty;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.*;
 import static org.jsoup.Jsoup.parse;
 
 /**
@@ -47,6 +47,7 @@ public class MovieExtractorTest {
        public void moviesAreLocated() throws IOException {
                assertThat(movies, containsInAnyOrder(
                                movie("All of Us Strangers", "https://cdn.premiumkino.de/movie/2809/db0e5c3ed02ba7fe4afa3b12aabb611f.jpg",
+                                               allOf(startsWith("Sich neu verlieben, durch eine Zeitschleife"), endsWith("Quelle: disney-content.de")),
                                                performance(of(2024, 2, 9, 16, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240209/1615/HkKdhlHMvtfSMy1fqYYtYuVdgGIKtnT7i7ddY5jzRfY~"),
                                                performance(of(2024, 2, 10, 22, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240210/2215/znWqm8FQUNSbrODY_A0jw8Au2nW6uSqbFE7Co8UgQv0~"),
                                                performance(of(2024, 2, 11, 20, 15), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240211/2015/I642oTHBKpy7sz2RULHIQK6cykSCPi57_c0TApiKbUk~"),
@@ -55,6 +56,7 @@ public class MovieExtractorTest {
                                                performance(of(2024, 2, 14, 14, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/all-of-us-strangers/20240214/1430/VZhgxh4PZutWWlLPEAe9dfOUJkB3sLnXzWoduICJYTk~")
                                ),
                                movie("Dune", "https://cdn.premiumkino.de/movie/3261/bcc70cb1cc4559731b6fd547d0f5cee1.jpg",
+                                               allOf(startsWith("&quot;Dune&quot; erzählt die packende Geschichte"), endsWith("Quelle: mediapass.warnerbros.com")),
                                                performance(of(2024, 2, 9, 19, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240209/1900/o9jDuan4yyxaW7-Jg3hCJpiAM4CLZei8J2IX-O5-hA0~"),
                                                performance(of(2024, 2, 10, 15, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240210/1530/jXbZnYD8R5djVnj3Ojjcsc9qdSJ0JBMVhn7PeP88HyY~"),
                                                performance(of(2024, 2, 11, 13, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240211/1330/aJmlM8wOaGl_jtuWDvWG9TF7RR1zRpNrSZeArCBhn90~"),
@@ -62,6 +64,7 @@ public class MovieExtractorTest {
                                                performance(of(2024, 2, 13, 17, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/dune/20240213/1700/QCphOtH-WrZ2tRbENwaxXPskrN2gH3I8dHR0Y5L3l0Y~")
                                ),
                                movie("SAVOY Sneak-Preview", "https://cdn.premiumkino.de/movie/5617/33dc4c74b4cca5cf4585b4cadfc38ec4.png",
+                                               allOf(startsWith("Wir zeigen unsere OV-Sneak freitagabends"), endsWith("Logenpreis 1,- Euro Ermäßigung. ")),
                                                performance(of(2024, 2, 9, 22, 30), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240209/2230/GtYBcOR_Jy7a8xDxwwPHI0wfY_v_Ep2P6rV0w4wJ7SM~"),
                                                performance(of(2024, 2, 16, 22, 0), "2D OV", "https://savoy.premiumkino.de/vorstellung/sneak-preview/20240216/2200/ZRC7iir9Hu8nIpH1PsiA_UDvckcj7yGqgMEYXHAs9Qw~")
                                ),
@@ -150,6 +153,11 @@ public class MovieExtractorTest {
 
        @SafeVarargs
        private Matcher<Movie> movie(String name, String imageUrl, Triple<LocalDateTime, String, String>... presentationTimesTypesAndLinks) {
+               return movie(name, imageUrl, anything(), presentationTimesTypesAndLinks);
+       }
+
+       @SafeVarargs
+       private Matcher<Movie> movie(String name, String imageUrl, Matcher<? super String> descriptionMatcher, Triple<LocalDateTime, String, String>... presentationTimesTypesAndLinks) {
                return new TypeSafeDiagnosingMatcher<Movie>() {
                        @Override
                        protected boolean matchesSafely(Movie movie, Description mismatchDescription) {
@@ -161,6 +169,10 @@ public class MovieExtractorTest {
                                        mismatchDescription.appendText("image URL is ").appendValue(movie.getImageUrl());
                                        return false;
                                }
+                               if (!descriptionMatcher.matches(movie.getDescription())) {
+                                       descriptionMatcher.describeMismatch(movie.getDescription(), mismatchDescription);
+                                       return false;
+                               }
                                List<Performance> performances = new ArrayList<>(movie.getPerformances());
                                if (performances.size() != presentationTimesTypesAndLinks.length) {
                                        mismatchDescription.appendText("has ").appendValue(performances.size()).appendText(" presentations");
@@ -190,6 +202,7 @@ public class MovieExtractorTest {
                        @Override
                        public void describeTo(Description description) {
                                description.appendText("movie with name ").appendValue(name);
+                               description.appendText(", description ").appendDescriptionOf(descriptionMatcher);
                                description.appendText(" and ").appendValue(presentationTimesTypesAndLinks.length).appendText(" presentations");
                        }
                };
index 857bd03..77ff2d3 100644 (file)
@@ -9,6 +9,7 @@ import org.hamcrest.Matchers.empty
 import org.hamcrest.Matchers.equalTo
 import org.hamcrest.Matchers.not
 import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
 import org.jsoup.nodes.TextNode
 import org.junit.Test
 import java.time.LocalDateTime
@@ -19,9 +20,9 @@ class MovieStateTest {
        fun `summary contains date of earliest movie`() {
                val movieState = MovieState(
                        setOf(
-                               Movie("1", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 2, 12, 18, 45), "", "")) },
-                               Movie("2", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 3, 12, 15, 30), "", "")) },
-                               Movie("3", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 2, 11, 21, 15), "", "")) }
+                               Movie("1", "", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 2, 12, 18, 45), "", "")) },
+                               Movie("2", "", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 3, 12, 15, 30), "", "")) },
+                               Movie("3", "", "").apply { addPerformance(Performance(LocalDateTime.of(2024, 2, 11, 21, 15), "", "")) }
                        ), emptySet()
                )
                assertThat(movieState.output(Reaction("", null, null, null)).summary(), containsString("2024-02-11"))
@@ -34,7 +35,7 @@ class MovieStateTest {
 
        @Test
        fun `movie state with a new movie is triggered`() {
-               assertThat(MovieState(emptySet(), setOf(Movie("1", ""))).triggered(), equalTo(true))
+               assertThat(MovieState(emptySet(), setOf(Movie("1", "", ""))).triggered(), equalTo(true))
        }
 
        @Test
@@ -47,15 +48,23 @@ class MovieStateTest {
 
        @Test
        fun `html output does contain a section for new movie if there are new movies`() {
-               val movieState = MovieState(emptySet(), setOf(Movie("1", ""), Movie("2", "")))
+               val movieState = MovieState(emptySet(), setOf(Movie("1", "", ""), Movie("2", "", "")))
                val output = movieState.output(Reaction("", null, null, null))
                val document = Jsoup.parse(output.text("text/html"))
                assertThat(document.select("section.new-movies"), not(empty()))
        }
 
        @Test
+       fun `new movies section contains description of movies`() {
+               val movieState = MovieState(emptySet(), setOf(Movie("1", "", "Movie 1 is cool."), Movie("2", "", "Movie 2 is bad.")))
+               val output = movieState.output(Reaction("", null, null, null))
+               val document = Jsoup.parse(output.text("text/html"))
+               assertThat(document.select("section.new-movies .description").map(Element::text), contains("Movie 1 is cool.", "Movie 2 is bad."))
+       }
+
+       @Test
        fun `new movies section contains the titles of all new movies`() {
-               val movieState = MovieState(emptySet(), setOf(Movie("New Movie", ""), Movie("Even Newer Movie", "")))
+               val movieState = MovieState(emptySet(), setOf(Movie("New Movie", "", ""), Movie("Even Newer Movie", "", "")))
                val output = movieState.output(Reaction("", null, null, null))
                val document = Jsoup.parse(output.text("text/html"))
                assertThat(document.select("section.new-movies li.movie .name").textNodes().map(TextNode::text), containsInAnyOrder("New Movie", "Even Newer Movie"))
@@ -73,10 +82,10 @@ class MovieStateTest {
        fun `html output contains a section for each day with a movie`() {
                val movieState = MovieState(
                        setOf(
-                               movie("Movie 1", "", "20240212-1845", "20240213-1330", "20240214-1815"),
-                               movie("Movie 2", "", "20240212-2030", "20240213-1745", "20240214-1430"),
-                               movie("Movie 3", "", "20240213-2015", "20240214-1730"),
-                               movie("Movie 4", "", "20240216-1000"),
+                               movie("Movie 1", "", "", "20240212-1845", "20240213-1330", "20240214-1815"),
+                               movie("Movie 2", "", "", "20240212-2030", "20240213-1745", "20240214-1430"),
+                               movie("Movie 3", "", "", "20240213-2015", "20240214-1730"),
+                               movie("Movie 4", "", "", "20240216-1000"),
                        ), emptySet()
                )
                val output = movieState.output(Reaction("", null, null, null))
@@ -89,13 +98,13 @@ class MovieStateTest {
        fun `html output contains the correct movies within each day`() {
                val movieState = MovieState(
                        setOf(
-                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "20240212-1845", "20240213-1330", "20240214-1815"),
-                               movie("Movie 2", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1_w300.jpg", "20240212-2030", "20240213-1745", "20240214-1430"),
-                               movie("Movie 3", "https://cdn.premiumkino.de/movie/7300/14d1b21dee51a82a7b096ca282bf01c8_w300.png", "20240213-2015", "20240214-1730"),
-                               movie("Movie 4", "https://cdn.premiumkino.de/movie/6080/cef2b33483d2898ddb472c955c58ea20_w300.jpg", "20240216-1000"),
+                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "", "20240212-1845", "20240213-1330", "20240214-1815"),
+                               movie("Movie 2", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1_w300.jpg", "", "20240212-2030", "20240213-1745", "20240214-1430"),
+                               movie("Movie 3", "https://cdn.premiumkino.de/movie/7300/14d1b21dee51a82a7b096ca282bf01c8_w300.png", "", "20240213-2015", "20240214-1730"),
+                               movie("Movie 4", "https://cdn.premiumkino.de/movie/6080/cef2b33483d2898ddb472c955c58ea20_w300.jpg", "", "20240216-1000"),
                        ), setOf(
-                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "20240212-1845", "20240213-1330", "20240214-1815"),
-                               movie("Movie 2", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1_w300.jpg", "20240212-1845", "20240213-1330", "20240214-1815")
+                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "", "20240212-1845", "20240213-1330", "20240214-1815"),
+                               movie("Movie 2", "https://cdn.premiumkino.de/movie/1066/aba09af737677ff6a15676ae588098b1_w300.jpg", "", "20240212-1845", "20240213-1330", "20240214-1815")
                        )
                )
                val output = movieState.output(Reaction("", null, null, null))
@@ -112,7 +121,7 @@ class MovieStateTest {
        fun `html output contains the correct release type for movies`() {
                val movieState = MovieState(
                        setOf(
-                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "20240212-1845" to "2D OV", "20240213-1330" to "2D", "20240214-1815" to "2D OmeU"),
+                               movie("Movie 1", "https://cdn.premiumkino.de/movie/3047/81c49774d7828a898ae1d525ffd135af_w300.jpg", "", "20240212-1845" to "2D OV", "20240213-1330" to "2D", "20240214-1815" to "2D OmeU"),
                        ), setOf()
                )
                val output = movieState.output(Reaction("", null, null, null))
@@ -136,10 +145,10 @@ private fun dateTime(dateTimeString: String) = LocalDateTime.of(
        dateTimeString.substring(11..12).toInt(),
 )
 
-private fun movie(name: String, imageUrl: String, vararg times: String) = Movie(name, imageUrl).apply {
+private fun movie(name: String, imageUrl: String, description: String = "", vararg times: String) = Movie(name, imageUrl, description).apply {
        times.map(::dateTime).map { Performance(it, "", "https://link/$it") }.forEach(this::addPerformance)
 }
 
-private fun movie(name: String, imageUrl: String, vararg timesAndTypes: Pair<String, String>) = Movie(name, imageUrl).apply {
+private fun movie(name: String, imageUrl: String, description: String = "", vararg timesAndTypes: Pair<String, String>) = Movie(name, imageUrl, description).apply {
        timesAndTypes.map { Performance(it.first.let(::dateTime), it.second, "https://link/${it.first}") }.forEach(this::addPerformance)
 }