From 59f1d52da77bde6297079ff52beda6855667cbee Mon Sep 17 00:00:00 2001 From: jhouse Date: Mon, 27 Jan 2020 15:23:48 +0900 Subject: [PATCH] Reworking configuration --- .../com/jasonhhouse/gaps/GapsService.java | 2 +- .../java/com/jasonhhouse/gaps/PlexQuery.java | 8 +- .../java/com/jasonhhouse/gaps/PlexSearch.java | 56 +---- .../java/com/jasonhhouse/gaps/PlexServer.java | 23 +- Dockerfile.no-ssl | 4 - .../controller/ConfigurationController.java | 77 ++++++ ...Controller.java => LibraryController.java} | 35 +-- .../PlexConfigurationController.java | 4 +- .../controller/PlexLibrariesController.java | 8 +- .../controller/PlexMovieListController.java | 3 +- .../gaps/service/GapsSearchService.java | 57 ++--- .../gaps/service/GapsServiceImpl.java | 29 +-- .../jasonhhouse/gaps/service/IoService.java | 232 ++++++++++-------- .../gaps/service/PlexQueryImpl.java | 19 +- .../validator/PlexLibrariesValidator.java | 58 ----- ...alidator.java => PlexServerValidator.java} | 9 +- .../main/resources/static/js/ownedMovies.js | 4 +- .../resources/templates/configuration.html | 137 +++++++++++ .../main/resources/templates/emptyState.html | 5 +- .../src/main/resources/templates/error.html | 6 +- .../main/resources/templates/error/404.html | 6 +- .../src/main/resources/templates/index.html | 24 +- .../{ownedMovies.html => libraries.html} | 28 ++- .../templates/plexConfiguration.html | 5 +- .../resources/templates/plexLibraries.html | 5 +- .../resources/templates/plexMovieList.html | 5 +- .../main/resources/templates/recommended.html | 5 +- GapsWeb/src/main/resources/templates/rss.html | 50 +++- .../com/jasonhhouse/gaps/GapsServiceTest.java | 7 +- 29 files changed, 570 insertions(+), 341 deletions(-) create mode 100644 GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/ConfigurationController.java rename GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/{OwnedMovieController.java => LibraryController.java} (71%) delete mode 100644 GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexLibrariesValidator.java rename GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/{PlexPropertiesValidator.java => PlexServerValidator.java} (91%) create mode 100644 GapsWeb/src/main/resources/templates/configuration.html rename GapsWeb/src/main/resources/templates/{ownedMovies.html => libraries.html} (74%) diff --git a/Core/src/main/java/com/jasonhhouse/gaps/GapsService.java b/Core/src/main/java/com/jasonhhouse/gaps/GapsService.java index 336116a..7d3d939 100644 --- a/Core/src/main/java/com/jasonhhouse/gaps/GapsService.java +++ b/Core/src/main/java/com/jasonhhouse/gaps/GapsService.java @@ -26,7 +26,7 @@ public interface GapsService { /** * Updates PlexLibrary's to add them if not added and set them selected or unselected if added * - * @param selectedLibraries The libraries to update + * @param selectedLibraries The libraries to update. they must come in as a single string added together of the plex server ID and the library key. */ void updateLibrarySelections(@NotNull List selectedLibraries); diff --git a/Core/src/main/java/com/jasonhhouse/gaps/PlexQuery.java b/Core/src/main/java/com/jasonhhouse/gaps/PlexQuery.java index dadc38e..b2f7abf 100644 --- a/Core/src/main/java/com/jasonhhouse/gaps/PlexQuery.java +++ b/Core/src/main/java/com/jasonhhouse/gaps/PlexQuery.java @@ -19,16 +19,16 @@ import org.jetbrains.annotations.NotNull; public interface PlexQuery { /** - * @param plexSearch Needs to have the IP Address, port, and plex token to connect + * @param plexServer Needs to have the IP Address, port, and plex token to connect * @return The list of libraries that are of type movie for that Plex server */ - @NotNull List getLibraries(@NotNull PlexSearch plexSearch); + @NotNull List getLibraries(@NotNull PlexServer plexServer); /** * Find the plex server name, key, and libraries based on the given PlexSearch parameters * - * @param plexSearch the search parameters + * @param plexServer the search parameters * @return A PlexServer instance */ - @NotNull PlexServer getPlexServer(@NotNull PlexSearch plexSearch); + @NotNull PlexServer queryPlexServer(@NotNull PlexServer plexServer); } diff --git a/Core/src/main/java/com/jasonhhouse/gaps/PlexSearch.java b/Core/src/main/java/com/jasonhhouse/gaps/PlexSearch.java index 5857cc6..448da70 100644 --- a/Core/src/main/java/com/jasonhhouse/gaps/PlexSearch.java +++ b/Core/src/main/java/com/jasonhhouse/gaps/PlexSearch.java @@ -10,36 +10,27 @@ package com.jasonhhouse.gaps; +import java.util.ArrayList; +import java.util.List; + public final class PlexSearch { public static final String MOVIE_DB_API_KEY = "movieDbApiKey"; - public static final String PLEX_TOKEN = "plexToken"; - - public static final String ADDRESS = "address"; - - public static final String PORT = "port"; - private String movieDbApiKey; - private String plexToken; - - private String address; - - private Integer port; - - private PlexServer plexServer; + private final List plexServers; public PlexSearch() { - + plexServers = new ArrayList<>(); } - public void setPlexServer(PlexServer plexServer) { - this.plexServer = plexServer; + public void addPlexServer(PlexServer plexServer) { + this.plexServers.add(plexServer); } - public PlexServer getPlexServer() { - return plexServer; + public List getPlexServers() { + return plexServers; } public String getMovieDbApiKey() { @@ -50,38 +41,11 @@ public final class PlexSearch { this.movieDbApiKey = movieDbApiKey; } - public String getPlexToken() { - return plexToken; - } - - public void setPlexToken(String plexToken) { - this.plexToken = plexToken; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - @Override public String toString() { return "PlexSearch{" + "movieDbApiKey='" + movieDbApiKey + '\'' + - ", plexToken='" + plexToken + '\'' + - ", address='" + address + '\'' + - ", port=" + port + - ", plexServer=" + plexServer + + ", plexServers=" + plexServers + '}'; } } diff --git a/Core/src/main/java/com/jasonhhouse/gaps/PlexServer.java b/Core/src/main/java/com/jasonhhouse/gaps/PlexServer.java index a57660f..10bd6b5 100644 --- a/Core/src/main/java/com/jasonhhouse/gaps/PlexServer.java +++ b/Core/src/main/java/com/jasonhhouse/gaps/PlexServer.java @@ -9,11 +9,17 @@ public class PlexServer { private final String friendlyName; private final String machineIdentifier; + private final String plexToken; + private final String address; + private final Integer port; private final SetUniqueList plexLibraries; - public PlexServer(String friendlyName, String machineIdentifier) { + public PlexServer(String friendlyName, String machineIdentifier, String plexToken, String address, Integer port) { this.friendlyName = friendlyName; this.machineIdentifier = machineIdentifier; + this.plexToken = plexToken; + this.address = address; + this.port = port; plexLibraries = SetUniqueList.setUniqueList(new ArrayList<>()); } @@ -29,6 +35,18 @@ public class PlexServer { return plexLibraries; } + public String getPlexToken() { + return plexToken; + } + + public String getAddress() { + return address; + } + + public Integer getPort() { + return port; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -47,6 +65,9 @@ public class PlexServer { return "PlexServer{" + "friendlyName='" + friendlyName + '\'' + ", machineIdentifier='" + machineIdentifier + '\'' + + ", plexToken='" + plexToken + '\'' + + ", address='" + address + '\'' + + ", port=" + port + ", plexLibraries=" + plexLibraries + '}'; } diff --git a/Dockerfile.no-ssl b/Dockerfile.no-ssl index ac35a06..4e60f05 100644 --- a/Dockerfile.no-ssl +++ b/Dockerfile.no-ssl @@ -4,8 +4,6 @@ MAINTAINER jh5975@gmail.com EXPOSE 32400 -EXPOSE 8000 - RUN mkdir -p /usr/data COPY movieIds.json /tmp @@ -16,6 +14,4 @@ WORKDIR /usr/app COPY GapsWeb/target/GapsWeb-0.2.1.jar /usr/app/ -ENV JAVA_TOOL_OPTIONS -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n - ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=no-ssl", "GapsWeb-0.2.1.jar"] \ No newline at end of file diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/ConfigurationController.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/ConfigurationController.java new file mode 100644 index 0000000..1767e5b --- /dev/null +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/ConfigurationController.java @@ -0,0 +1,77 @@ +package com.jasonhhouse.gaps.controller; + +import com.jasonhhouse.gaps.GapsService; +import com.jasonhhouse.gaps.PlexSearch; +import com.jasonhhouse.gaps.PlexServer; +import com.jasonhhouse.gaps.service.BindingErrorsService; +import com.jasonhhouse.gaps.service.IoService; +import com.jasonhhouse.gaps.service.PlexQueryImpl; +import java.io.IOException; +import java.util.List; +import javax.validation.Valid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.ModelAndView; + +@Controller +@RequestMapping(value = "/configuration") +public class ConfigurationController { + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationController.class); + + private final BindingErrorsService bindingErrorsService; + private final PlexQueryImpl plexQuery; + private final GapsService gapsService; + private final IoService ioService; + + public ConfigurationController(BindingErrorsService bindingErrorsService, PlexQueryImpl plexQuery, GapsService gapsService, IoService ioService) { + this.bindingErrorsService = bindingErrorsService; + this.plexQuery = plexQuery; + this.gapsService = gapsService; + this.ioService = ioService; + } + + @RequestMapping(method = RequestMethod.GET, + produces = MediaType.TEXT_HTML_VALUE) + public ModelAndView getConfiguration() { + LOGGER.info("getConfiguration()"); + + try { + PlexSearch plexSearch = ioService.readProperties(); + gapsService.updatePlexSearch(plexSearch); + + List plexServers = ioService.readPlexConfiguration(); + plexSearch.getPlexServers().addAll(plexServers); + } catch (IOException e) { + LOGGER.warn("Failed to read gaps properties.", e); + } + + ModelAndView modelAndView = new ModelAndView("configuration"); + modelAndView.addObject("plexSearch", gapsService.getPlexSearch()); + return modelAndView; + } + + @RequestMapping(value = "/plex", + method = RequestMethod.POST, + consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @ResponseStatus(value = HttpStatus.OK) + public void postPlexServer(@Valid final PlexServer plexServer, BindingResult bindingResult) { + LOGGER.info("postPlexLibraries( " + plexServer + " )"); + +/* + if (bindingErrorsService.hasBindingErrors(bindingResult)) { + LOGGER.error("Error binding PlexServer object: " + plexServer); + } +*/ + final PlexServer plexServerInformation = plexQuery.queryPlexServer(plexServer); + plexServerInformation.getPlexLibraries().addAll(plexQuery.getLibraries(plexServerInformation)); + gapsService.getPlexSearch().addPlexServer(plexServerInformation); + ioService.writePlexConfiguration(plexServerInformation); + } +} diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/OwnedMovieController.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/LibraryController.java similarity index 71% rename from GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/OwnedMovieController.java rename to GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/LibraryController.java index 3eb3730..1ae009d 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/OwnedMovieController.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/LibraryController.java @@ -2,14 +2,18 @@ package com.jasonhhouse.gaps.controller; import com.jasonhhouse.gaps.GapsService; import com.jasonhhouse.gaps.Movie; +import com.jasonhhouse.gaps.PlexLibrary; import com.jasonhhouse.gaps.PlexSearch; +import com.jasonhhouse.gaps.PlexServer; import com.jasonhhouse.gaps.service.BindingErrorsService; import com.jasonhhouse.gaps.service.IoService; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,37 +24,36 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; @RestController -public class OwnedMovieController { +public class LibraryController { - private static final Logger LOGGER = LoggerFactory.getLogger(OwnedMovieController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LibraryController.class); private final BindingErrorsService bindingErrorsService; private final IoService ioService; private final GapsService gapsService; @Autowired - public OwnedMovieController(BindingErrorsService bindingErrorsService, IoService ioService, GapsService gapsService) { + public LibraryController(BindingErrorsService bindingErrorsService, IoService ioService, GapsService gapsService) { this.bindingErrorsService = bindingErrorsService; this.ioService = ioService; this.gapsService = gapsService; } @RequestMapping(method = RequestMethod.GET, - path = "/ownedMovies") - public ModelAndView getOwnedMovies() { - LOGGER.info("getOwnedMovies()"); + path = "/libraries") + public ModelAndView getLibraries() { + LOGGER.info("getLibraries()"); - if (!ioService.doesOwnedMoviesFileExist()) { + if (!ioService.doOwnedMoviesFilesExist(gapsService.getPlexSearch().getPlexServers())) { //Show empty page return new ModelAndView("emptyState"); } else { //ToDo //Need to write a way to get all movies or break up the UI per library (Maybe just show the library the movie came from in the table) - List ownedMovies = Collections.emptyList(); - //List ownedMovies = ioService.getOwnedMovies(); - LOGGER.info("ownedMovies.size():" + ownedMovies.size()); + Map ownedMovies = ioService.readAllOwnedMovies(gapsService.getPlexSearch().getPlexServers()); + //LOGGER.info("ownedMovies.size():" + ownedMovies.size()); - if (CollectionUtils.isEmpty(ownedMovies)) { + if (MapUtils.isEmpty(ownedMovies)) { LOGGER.error("Could not parse Owned Movie JSON"); return bindingErrorsService.getErrorPage(); } @@ -62,15 +65,15 @@ public class OwnedMovieController { LOGGER.warn("Failed to read gaps properties.", e); } - ModelAndView modelAndView = new ModelAndView("ownedMovies"); - modelAndView.addObject("ownedMovies", ownedMovies); - modelAndView.addObject("urls", buildUrls(ownedMovies)); + ModelAndView modelAndView = new ModelAndView("libraries"); + modelAndView.addObject("movies", ownedMovies); + /*modelAndView.addObject("urls", buildUrls(ownedMovies));*/ modelAndView.addObject("plexSearch", gapsService.getPlexSearch()); return modelAndView; } } - private List buildUrls(List movies) { + /* private List buildUrls(List movies) { LOGGER.info("buildUrls( " + movies + " ) "); List urls = new ArrayList<>(); for (Movie movie : movies) { @@ -88,5 +91,5 @@ public class OwnedMovieController { } return urls; - } + }*/ } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexConfigurationController.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexConfigurationController.java index 880be42..f80f5f2 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexConfigurationController.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexConfigurationController.java @@ -47,7 +47,7 @@ public class PlexConfigurationController { this.gapsService = gapsService; } - @RequestMapping(method = RequestMethod.POST, + /*@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.TEXT_HTML_VALUE) public ModelAndView postPlexConfiguration(@Valid PlexSearch plexSearch, BindingResult bindingResult) { @@ -72,7 +72,7 @@ public class PlexConfigurationController { ModelAndView modelAndView = new ModelAndView("plexConfiguration"); modelAndView.addObject("plexSearch", gapsService.getPlexSearch()); return modelAndView; - } + }*/ @RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE) diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexLibrariesController.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexLibrariesController.java index 4ffd8f0..df1ba2a 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexLibrariesController.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexLibrariesController.java @@ -18,7 +18,7 @@ import com.jasonhhouse.gaps.PlexSearchFormatter; import com.jasonhhouse.gaps.PlexServer; import com.jasonhhouse.gaps.service.BindingErrorsService; import com.jasonhhouse.gaps.service.IoService; -import com.jasonhhouse.gaps.validator.PlexPropertiesValidator; +import com.jasonhhouse.gaps.validator.PlexServerValidator; import java.io.IOException; import java.util.List; import javax.validation.Valid; @@ -52,7 +52,7 @@ public class PlexLibrariesController { this.gapsService = gapsService; } - @RequestMapping(method = RequestMethod.POST, + /* @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.TEXT_HTML_VALUE) public ModelAndView postPlexLibraries(@Valid PlexSearch plexSearch, BindingResult bindingResult) { @@ -78,7 +78,7 @@ public class PlexLibrariesController { ModelAndView modelAndView = new ModelAndView("plexLibraries"); modelAndView.addObject("plexSearch", gapsService.getPlexSearch()); return modelAndView; - } + }*/ @RequestMapping(method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE) @@ -93,6 +93,6 @@ public class PlexLibrariesController { public void initBinder(WebDataBinder binder) { LOGGER.info("initBinder()"); binder.addCustomFormatter(new PlexSearchFormatter(), "plexSearch"); - binder.setValidator(new PlexPropertiesValidator()); + binder.setValidator(new PlexServerValidator()); } } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexMovieListController.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexMovieListController.java index 8eed089..82895e0 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexMovieListController.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/controller/PlexMovieListController.java @@ -13,7 +13,6 @@ package com.jasonhhouse.gaps.controller; import com.jasonhhouse.gaps.GapsService; import com.jasonhhouse.gaps.PlexSearchFormatter; import com.jasonhhouse.gaps.service.BindingErrorsService; -import com.jasonhhouse.gaps.validator.PlexLibrariesValidator; import java.util.ArrayList; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; @@ -70,6 +69,6 @@ public class PlexMovieListController { public void initBinder(WebDataBinder binder) { LOGGER.info("initBinder()"); binder.addCustomFormatter(new PlexSearchFormatter(), "plexSearch"); - binder.setValidator(new PlexLibrariesValidator()); + //binder.setValidator(new PlexLibrariesValidator()); } } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsSearchService.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsSearchService.java index e809fcf..040fc88 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsSearchService.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsSearchService.java @@ -152,16 +152,17 @@ public class GapsSearchService implements GapsSearch { gapsService .getPlexSearch() - .getPlexServer() - .getPlexLibraries() - .stream() - .filter(PlexLibrary::getSelected) - .forEach(plexLibrary -> { - everyMovie - .forEach(movie -> { - previousMovies.put(new MoviePair(movie.getName(), movie.getYear()), movie); - }); - }); + .getPlexServers() + .forEach(plexServer -> plexServer + .getPlexLibraries() + .stream() + .filter(PlexLibrary::getSelected) + .forEach(plexLibrary -> { + everyMovie + .forEach(movie -> { + previousMovies.put(new MoviePair(movie.getName(), movie.getYear()), movie); + }); + })); findAllPlexMovies(previousMovies); @@ -185,20 +186,19 @@ public class GapsSearchService implements GapsSearch { cancelSearch.set(true); } - ioService.writeToFile(recommended); - //Always write to log ioService.writeRecommendedToFile(recommended); ioService.writeMovieIdsToFile(new TreeSet<>(everyMovie)); gapsService.getPlexSearch() - .getPlexServer() - .getPlexLibraries() - .stream() - .filter(PlexLibrary::getSelected) - .forEach(plexLibrary -> { - ioService.writeOwnedMoviesToFile(gapsService.getPlexSearch().getPlexServer(), plexLibrary.getKey(), ownedMovies); - }); + .getPlexServers() + .forEach(plexServer -> plexServer + .getPlexLibraries() + .stream() + .filter(PlexLibrary::getSelected) + .forEach(plexLibrary -> { + ioService.writeOwnedMoviesToFile(plexServer, plexLibrary.getKey(), ownedMovies); + })); template.convertAndSend("/finishedSearching", true); @@ -379,7 +379,6 @@ public class GapsSearchService implements GapsSearch { .readTimeout(180, TimeUnit.SECONDS) .build(); - List urls = generatePlexUrls(); if (CollectionUtils.isEmpty(urls)) { @@ -914,14 +913,16 @@ public class GapsSearchService implements GapsSearch { private List generatePlexUrls() { LOGGER.info("generatePlexUrls()"); - LOGGER.info(gapsService.getPlexSearch().getPlexServer().getPlexLibraries().toString()); - List urls = gapsService.getPlexSearch() - .getPlexServer() - .getPlexLibraries() - .stream() - .filter(PlexLibrary::getSelected) - .map(plexLibrary -> "http://" + gapsService.getPlexSearch().getAddress() + ":" + gapsService.getPlexSearch().getPort() + "/library/sections/" + plexLibrary.getKey() + "/all/?X-Plex-Token=" + gapsService.getPlexSearch().getPlexToken()) - .collect(Collectors.toList()); + List urls = new ArrayList<>(); + gapsService.getPlexSearch() + .getPlexServers() + .forEach(plexServer -> urls + .addAll(plexServer + .getPlexLibraries() + .stream() + .filter(PlexLibrary::getSelected) + .map(plexLibrary -> "http://" + plexServer.getAddress() + ":" + plexServer.getPort() + "/library/sections/" + plexLibrary.getKey() + "/all/?X-Plex-Token=" + plexServer.getPlexToken()) + .collect(Collectors.toList()))); LOGGER.info("URLS: " + urls.size()); return urls; } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsServiceImpl.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsServiceImpl.java index 0686437..8f5ea7d 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsServiceImpl.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/GapsServiceImpl.java @@ -13,10 +13,9 @@ package com.jasonhhouse.gaps.service; import com.jasonhhouse.gaps.GapsService; import com.jasonhhouse.gaps.PlexLibrary; import com.jasonhhouse.gaps.PlexSearch; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -43,19 +42,21 @@ public class GapsServiceImpl implements GapsService { @Override public void updateLibrarySelections(@NotNull List selectedLibraries) { LOGGER.info("updateLibrarySelections( " + selectedLibraries + " )"); - LOGGER.info("BEFORE:" + getPlexSearch().getPlexServer().getPlexLibraries().toString()); - Map map = getPlexSearch().getPlexServer().getPlexLibraries().stream().collect(Collectors.toMap(PlexLibrary::getKey, Function.identity())); + Map map = new HashMap<>(); + getPlexSearch() + .getPlexServers() + .forEach(plexServer -> plexServer + .getPlexLibraries() + .forEach(plexLibrary -> map.put(plexServer.getMachineIdentifier() + plexLibrary.getKey(), plexLibrary))); for (String selectedLibrary : selectedLibraries) { - Integer key = Integer.valueOf(selectedLibrary); - if (map.containsKey(key)) { - map.get(key).setSelected(true); + if (map.containsKey(selectedLibrary)) { + map.get(selectedLibrary).setSelected(true); } else { LOGGER.warn("Can't find library"); } } - LOGGER.info("AFTER:" + getPlexSearch().getPlexServer().getPlexLibraries().toString()); } @Override @@ -68,18 +69,6 @@ public class GapsServiceImpl implements GapsService { @Override public void updatePlexSearch(PlexSearch plexSearch) { LOGGER.info("updatePlexSearch( " + plexSearch + " )"); - if (StringUtils.isNotEmpty(plexSearch.getAddress())) { - this.plexSearch.setAddress(plexSearch.getAddress()); - } - - if (plexSearch.getPort() != null) { - this.plexSearch.setPort(plexSearch.getPort()); - } - - if (StringUtils.isNotEmpty(plexSearch.getPlexToken())) { - this.plexSearch.setPlexToken(plexSearch.getPlexToken()); - } - if (StringUtils.isNotEmpty(plexSearch.getMovieDbApiKey())) { this.plexSearch.setMovieDbApiKey(plexSearch.getMovieDbApiKey()); } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/IoService.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/IoService.java index 5f3ed75..ae9e4ef 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/IoService.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/IoService.java @@ -13,6 +13,7 @@ package com.jasonhhouse.gaps.service; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.jasonhhouse.gaps.Movie; +import com.jasonhhouse.gaps.PlexLibrary; import com.jasonhhouse.gaps.PlexSearch; import com.jasonhhouse.gaps.PlexServer; import com.jasonhhouse.gaps.Rss; @@ -27,14 +28,17 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -56,6 +60,8 @@ public class IoService { public static final String RSS_FEED_JSON_FILE = "rssFeed.json"; + public static final String PLEX_CONFIGURATION = "plexConfiguration.json"; + public static final String PROPERTIES = "gaps.properties"; private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -109,10 +115,6 @@ public class IoService { } } - public boolean doesOwnedMoviesFileExist() { - return new File(STORAGE_FOLDER + OWNED_MOVIES).exists(); - } - /** * Write the recommended movie list to the RSS file for endpoint to display. * @@ -194,6 +196,67 @@ public class IoService { writeMovieIdsToFile(ownedMovies, file); } + public boolean doOwnedMoviesFilesExist(List plexServers) { + LOGGER.info("doOwnedMoviesFilesExist()"); + + for (PlexServer plexServer : plexServers) { + for (PlexLibrary plexLibrary : plexServer.getPlexLibraries()) { + final File ownedMovieFile = new File(STORAGE_FOLDER + plexServer.getMachineIdentifier() + File.separator + plexLibrary.getKey() + File.separator + OWNED_MOVIES); + + if (ownedMovieFile.exists()) { + return true; + } + } + } + + return false; + } + + /** + * Prints out all recommended movies to recommendedMovies.json + */ + public Map readAllOwnedMovies(List plexServers) { + LOGGER.info("readAllOwnedMovies()"); + + Map ownedMovieMap = new HashMap<>(); + + for (PlexServer plexServer : plexServers) { + for (PlexLibrary plexLibrary : plexServer.getPlexLibraries()) { + final File ownedMovieFile = new File(STORAGE_FOLDER + plexServer.getMachineIdentifier() + File.separator + plexLibrary.getKey() + File.separator + OWNED_MOVIES); + + if (!ownedMovieFile.exists()) { + LOGGER.warn(ownedMovieFile + " does not exist"); + continue; + } + + readMovieIdsFromFile(); + + try (BufferedReader br = new BufferedReader(new FileReader(ownedMovieFile))) { + StringBuilder fullFile = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + fullFile.append(line); + } + + LOGGER.info(fullFile.toString()); + Set ownedMovies = objectMapper.readValue(fullFile.toString(), new TypeReference>() { + }); + LOGGER.info("everyMovie.size():" + ownedMovies.size()); + + for (Movie movie : ownedMovies) { + ownedMovieMap.put(plexLibrary, movie); + } + } catch (FileNotFoundException e) { + LOGGER.error("Can't find file " + ownedMovieFile); + } catch (IOException e) { + LOGGER.error("Can't write to file " + ownedMovieFile); + } + } + } + + return ownedMovieMap; + } + /** * Prints out all movies to a text file movieIds.json */ @@ -204,38 +267,6 @@ public class IoService { writeMovieIdsToFile(everyMovie, file); } - /* public void writeOwnedMoviesToFile(Set movies, File file) { - LOGGER.info("movies: " + movies); - - if (file.exists()) { - boolean deleted = file.delete(); - if (!deleted) { - LOGGER.error("Can't delete existing file " + file.getName()); - return; - } - } - - try { - boolean created = file.createNewFile(); - if (!created) { - LOGGER.error("Can't create file " + file.getAbsolutePath()); - return; - } - } catch (IOException e) { - LOGGER.error("Can't create file " + file.getAbsolutePath(), e); - return; - } - - try (FileOutputStream outputStream = new FileOutputStream(file)) { - byte[] output = objectMapper.writeValueAsBytes(movies); - outputStream.write(output); - } catch (FileNotFoundException e) { - LOGGER.error("Can't find file " + file.getAbsolutePath(), e); - } catch (IOException e) { - LOGGER.error("Can't write to file " + file.getAbsolutePath(), e); - } - }*/ - public void writeMovieIdsToFile(Set everyMovie, File file) { if (file.exists()) { boolean deleted = file.delete(); @@ -266,6 +297,66 @@ public class IoService { } } + public void writePlexConfiguration(PlexServer plexServer) { + final String fileName = STORAGE_FOLDER + PLEX_CONFIGURATION; + File file = new File(fileName); + if (file.exists()) { + boolean deleted = file.delete(); + if (!deleted) { + LOGGER.error("Can't delete existing file " + file.getName()); + return; + } + } + + try { + boolean created = file.createNewFile(); + if (!created) { + LOGGER.error("Can't create file " + file.getAbsolutePath()); + return; + } + } catch (IOException e) { + LOGGER.error("Can't create file " + file.getAbsolutePath(), e); + return; + } + + try (FileOutputStream outputStream = new FileOutputStream(file)) { + byte[] output = objectMapper.writeValueAsBytes(plexServer); + outputStream.write(output); + } catch (FileNotFoundException e) { + LOGGER.error("Can't find file " + file.getAbsolutePath(), e); + } catch (IOException e) { + LOGGER.error("Can't write to file " + file.getAbsolutePath(), e); + } + } + + public @NotNull List readPlexConfiguration() { + final String fileName = STORAGE_FOLDER + PLEX_CONFIGURATION; + File file = new File(fileName); + List plexServers = new ArrayList<>(); + if (!file.exists()) { + LOGGER.warn("Can't find json file '" + fileName + "'. Most likely first run."); + return plexServers; + } + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + StringBuilder fullFile = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + fullFile.append(line); + } + + LOGGER.info(fullFile.toString()); + plexServers = objectMapper.readValue(fullFile.toString(), new TypeReference>() { + }); + LOGGER.info(plexServers.toString()); + } catch (FileNotFoundException e) { + LOGGER.error("Can't find file " + fileName); + } catch (IOException e) { + LOGGER.error("Can't write to file " + fileName); + } + + return plexServers; + } + /** * Prints out all recommended files to a text file called gaps_recommended_movies.txt */ @@ -284,10 +375,10 @@ public class IoService { fullFile.append(line); } - LOGGER.debug(fullFile.toString()); + LOGGER.info(fullFile.toString()); everyMovie = objectMapper.readValue(fullFile.toString(), new TypeReference>() { }); - LOGGER.debug("everyMovie.size():" + everyMovie.size()); + LOGGER.info("everyMovie.size():" + everyMovie.size()); } catch (FileNotFoundException e) { LOGGER.error("Can't find file " + fileName); } catch (IOException e) { @@ -297,42 +388,6 @@ public class IoService { return everyMovie; } - /** - * Prints out all recommended files to a text file called gaps_recommended_movies.txt - */ - public void writeToFile(Set recommended) { - File file = new File(RECOMMENDED_MOVIES); - if (file.exists()) { - boolean deleted = file.delete(); - if (!deleted) { - LOGGER.error("Can't delete existing file " + RECOMMENDED_MOVIES); - return; - } - } - - try { - boolean created = file.createNewFile(); - if (!created) { - LOGGER.error("Can't create file " + RECOMMENDED_MOVIES); - return; - } - } catch (IOException e) { - LOGGER.error("Can't create file " + RECOMMENDED_MOVIES, e); - return; - } - - try (FileOutputStream outputStream = new FileOutputStream(RECOMMENDED_MOVIES)) { - for (Movie movie : recommended) { - String output = movie.toString() + System.lineSeparator(); - outputStream.write(output.getBytes()); - } - } catch (FileNotFoundException e) { - LOGGER.error("Can't find file " + RECOMMENDED_MOVIES, e); - } catch (IOException e) { - LOGGER.error("Can't write to file " + RECOMMENDED_MOVIES, e); - } - } - public void writeProperties(PlexSearch plexSearch) throws IOException { Properties properties = new Properties(); @@ -340,17 +395,7 @@ public class IoService { properties.setProperty(PlexSearch.MOVIE_DB_API_KEY, plexSearch.getMovieDbApiKey()); } - if (StringUtils.isNotEmpty(plexSearch.getAddress())) { - properties.setProperty(PlexSearch.ADDRESS, plexSearch.getAddress()); - } - - if (plexSearch.getPort() != null) { - properties.setProperty(PlexSearch.PORT, Integer.toString(plexSearch.getPort())); - } - - if (StringUtils.isNotEmpty(plexSearch.getPlexToken())) { - properties.setProperty(PlexSearch.PLEX_TOKEN, plexSearch.getPlexToken()); - } + properties.setProperty("version", "v0.2.1"); properties.store(new FileWriter(new File(STORAGE_FOLDER + PROPERTIES)), ""); } @@ -372,21 +417,6 @@ public class IoService { plexSearch.setMovieDbApiKey(movieDbApiKey); } - if (properties.containsKey(PlexSearch.ADDRESS)) { - String address = properties.getProperty(PlexSearch.ADDRESS); - plexSearch.setAddress(address); - } - - if (properties.containsKey(PlexSearch.PORT)) { - String port = properties.getProperty(PlexSearch.PORT); - plexSearch.setPort(Integer.parseInt(port)); - } - - if (properties.containsKey(PlexSearch.PLEX_TOKEN)) { - String plexToken = properties.getProperty(PlexSearch.PLEX_TOKEN); - plexSearch.setPlexToken(plexToken); - } - return plexSearch; } } diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/PlexQueryImpl.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/PlexQueryImpl.java index add0858..9ec624b 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/PlexQueryImpl.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/service/PlexQueryImpl.java @@ -12,7 +12,6 @@ package com.jasonhhouse.gaps.service; import com.jasonhhouse.gaps.PlexLibrary; import com.jasonhhouse.gaps.PlexQuery; -import com.jasonhhouse.gaps.PlexSearch; import com.jasonhhouse.gaps.PlexServer; import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList; import java.io.ByteArrayInputStream; @@ -50,16 +49,16 @@ public class PlexQueryImpl implements PlexQuery { private static final Logger LOGGER = LoggerFactory.getLogger(PlexQueryImpl.class); @Override - public @NotNull List getLibraries(@NotNull PlexSearch plexSearch) { + public @NotNull List getLibraries(@NotNull PlexServer plexServer) { LOGGER.info("queryPlexLibraries()"); HttpUrl url = new HttpUrl.Builder() .scheme("http") - .host(plexSearch.getAddress()) - .port(plexSearch.getPort()) + .host(plexServer.getAddress()) + .port(plexServer.getPort()) .addPathSegment("library") .addPathSegment("sections") - .addQueryParameter("X-Plex-Token", plexSearch.getPlexToken()) + .addQueryParameter("X-Plex-Token", plexServer.getPlexToken()) .build(); //ToDo @@ -141,14 +140,14 @@ public class PlexQueryImpl implements PlexQuery { } @Override - public @NotNull PlexServer getPlexServer(@NotNull PlexSearch plexSearch) { + public @NotNull PlexServer queryPlexServer(@NotNull PlexServer plexServer) { LOGGER.info("queryPlexLibraries()"); HttpUrl url = new HttpUrl.Builder() .scheme("http") - .host(plexSearch.getAddress()) - .port(plexSearch.getPort()) - .addQueryParameter("X-Plex-Token", plexSearch.getPlexToken()) + .host(plexServer.getAddress()) + .port(plexServer.getPort()) + .addQueryParameter("X-Plex-Token", plexServer.getPlexToken()) .build(); //ToDo @@ -192,7 +191,7 @@ public class PlexQueryImpl implements PlexQuery { String machineIdentifier = machineIdentifierNode.getNodeValue().trim(); LOGGER.info("machineIdentifier:" + machineIdentifier); - return new PlexServer(friendlyName, machineIdentifier); + return new PlexServer(friendlyName, machineIdentifier, plexServer.getPlexToken(), plexServer.getAddress(), plexServer.getPort()); } catch (IOException e) { String reason = "Error connecting to Plex to get library list: " + url; LOGGER.error(reason, e); diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexLibrariesValidator.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexLibrariesValidator.java deleted file mode 100644 index 6c6bc27..0000000 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexLibrariesValidator.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2019 Jason H House - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.jasonhhouse.gaps.validator; - -import com.jasonhhouse.gaps.PlexLibrary; -import com.jasonhhouse.gaps.PlexSearch; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; - -public class PlexLibrariesValidator implements Validator { - - private static final Logger LOGGER = LoggerFactory.getLogger(PlexLibrariesValidator.class); - - @Override - public boolean supports(@NotNull Class clazz) { - return PlexSearch.class.equals(clazz); - } - - @Override - public void validate(@NotNull Object obj, @NotNull Errors errors) { - LOGGER.info("validate( " + obj + ", " + errors + " )"); - - PlexSearch plexSearch = (PlexSearch) obj; - if (CollectionUtils.isEmpty(plexSearch.getPlexServer().getPlexLibraries())) { - errors.rejectValue("libraries", "libraries.empty"); - return; - } - - for (PlexLibrary plexLibrary : plexSearch.getPlexServer().getPlexLibraries()) { - if (StringUtils.isEmpty(plexLibrary.getTitle())) { - errors.rejectValue("libraries", "plexLibrary.getTitle().empty"); - } - - if (plexLibrary.getKey() == null) { - errors.rejectValue("libraries", "plexLibrary.getKey().empty"); - } - - if (plexLibrary.getSelected() == null) { - plexLibrary.setSelected(false); - } - } - - } -} - diff --git a/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexPropertiesValidator.java b/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexServerValidator.java similarity index 91% rename from GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexPropertiesValidator.java rename to GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexServerValidator.java index b112261..bfb63ea 100644 --- a/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexPropertiesValidator.java +++ b/GapsWeb/src/main/java/com/jasonhhouse/gaps/validator/PlexServerValidator.java @@ -11,6 +11,7 @@ package com.jasonhhouse.gaps.validator; import com.jasonhhouse.gaps.PlexSearch; +import com.jasonhhouse.gaps.PlexServer; import com.jasonhhouse.gaps.controller.GapsController; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -19,20 +20,20 @@ import org.slf4j.LoggerFactory; import org.springframework.validation.Errors; import org.springframework.validation.Validator; -public class PlexPropertiesValidator implements Validator { +public class PlexServerValidator implements Validator { - private static final Logger LOGGER = LoggerFactory.getLogger(GapsController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(PlexServerValidator.class); @Override public boolean supports(@NotNull Class clazz) { - return PlexSearch.class.equals(clazz); + return PlexServer.class.equals(clazz); } @Override public void validate(@NotNull Object obj, @NotNull Errors errors) { LOGGER.info("validate( " + obj + ", " + errors + " )"); - PlexSearch plexSearch = (PlexSearch) obj; + PlexServer plexSearch = (PlexServer) obj; if (StringUtils.isEmpty(plexSearch.getAddress())) { errors.rejectValue("address", "address.empty"); } diff --git a/GapsWeb/src/main/resources/static/js/ownedMovies.js b/GapsWeb/src/main/resources/static/js/ownedMovies.js index 58269e8..622ae32 100644 --- a/GapsWeb/src/main/resources/static/js/ownedMovies.js +++ b/GapsWeb/src/main/resources/static/js/ownedMovies.js @@ -27,7 +27,7 @@ $(document).ready(function () { {data: "title"}, {data: "year"}, {data: "language"}, - {data: "link"}, + {data: "link"}/*, { data: "find_missing", render: function (data, type, row) { @@ -37,7 +37,7 @@ $(document).ready(function () { return data; }, className: "dt-body-center" - } + }*/ ], select: { style: 'os', diff --git a/GapsWeb/src/main/resources/templates/configuration.html b/GapsWeb/src/main/resources/templates/configuration.html new file mode 100644 index 0000000..5577879 --- /dev/null +++ b/GapsWeb/src/main/resources/templates/configuration.html @@ -0,0 +1,137 @@ + + + + + + Gaps + + + + + + + + + + + + +
+

Configuration

+ + +
+
+

To use Gaps, you'll need a MovieDB api key. Navigate over to The Movie DB, + create an account, and make an API Key. Copy that key and paste it below.

+ +
+
+ + +
+ + +
+
+
+
+
+ + + Use your network IP address. localhost and + 127.0.0.1 will not work for Plex and Gaps in Docker. Use ifconfig/ipconfig to find that in a + terminal or command window. +
+ +
+ + + Plex default is 32400. If Plex uses something + other than 32400 +
+ +
+ + + You can learn how to find your Plex Token + here + . +
+ + +
+ +

Servers

+ +
+
+
+
+
+
    +
  • +
+
+ Test + Remove +
+ +
+
+
+
+
+
+

folders

+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/GapsWeb/src/main/resources/templates/emptyState.html b/GapsWeb/src/main/resources/templates/emptyState.html index 8265407..6f6c57b 100644 --- a/GapsWeb/src/main/resources/templates/emptyState.html +++ b/GapsWeb/src/main/resources/templates/emptyState.html @@ -27,7 +27,10 @@