Merge pull request #75 from JasonHHouse/improvement/health_check_and_controller_break_up

Improvement/health check and controller break up
This commit is contained in:
Gregory Knox
2019-11-10 12:38:09 -05:00
committed by GitHub
22 changed files with 178 additions and 122 deletions

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;/*
package com.jasonhhouse.gaps;/*
* 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:

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;/*
package com.jasonhhouse.gaps;/*
* 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:

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;/*
package com.jasonhhouse.gaps;/*
* 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:
@@ -33,15 +33,15 @@ public final class Movie implements Comparable<Movie> {
this.collection = collection;
}
Movie(String imdbId, String name, int year, String collection) {
public Movie(String imdbId, String name, int year, String collection) {
this(-1, imdbId, name, year, collection);
}
Movie(int tvdbId, String name, int year, String collection) {
public Movie(int tvdbId, String name, int year, String collection) {
this(-1, null, name, year, collection);
}
Movie(String name, int year, String collection) {
public Movie(String name, int year, String collection) {
this(-1, name, year, collection);
}

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;/*
package com.jasonhhouse.gaps;/*
* 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:

View File

@@ -8,9 +8,8 @@
* 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;
package com.jasonhhouse.gaps;
import java.util.List;
import java.util.Objects;
public final class SearchResults {

View File

@@ -12,7 +12,7 @@
<artifactId>GapsWeb</artifactId>
<properties>
<start-class>com.jasonhhouse.Gaps.GapsApplication</start-class>
<start-class>com.jasonhhouse.gaps.GapsApplication</start-class>
</properties>
<dependencies>
@@ -136,6 +136,11 @@
<version>17.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@@ -188,7 +193,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.jasonhhouse.Gaps.GapsApplication</mainClass>
<mainClass>com.jasonhhouse.gaps.GapsApplication</mainClass>
</configuration>
</plugin>
</plugins>

View File

@@ -8,17 +8,15 @@
* 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;
package com.jasonhhouse.gaps;
import org.springframework.boot.CommandLineRunner;
import java.util.concurrent.Executor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* Search for all missing movies in your plex collection by MovieDB collection.
*/

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;
package com.jasonhhouse.gaps;
import okhttp3.HttpUrl;
import org.jetbrains.annotations.NotNull;

View File

@@ -8,7 +8,7 @@
* 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;
package com.jasonhhouse.gaps;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

View File

@@ -8,7 +8,7 @@
* 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;
package com.jasonhhouse.gaps;
public class SearchCancelledException extends Exception {
public SearchCancelledException() {

View File

@@ -8,7 +8,7 @@
* 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;
package com.jasonhhouse.gaps;
import java.util.Objects;

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;
package com.jasonhhouse.gaps;
import okhttp3.HttpUrl;
import org.jetbrains.annotations.NotNull;

View File

@@ -8,7 +8,7 @@
* 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;
package com.jasonhhouse.gaps;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;

View File

@@ -0,0 +1,37 @@
package com.jasonhhouse.gaps.controller;
import com.jasonhhouse.gaps.GapsSearch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "cancelSearch")
public class CancelController {
private final Logger logger = LoggerFactory.getLogger(CancelController.class);
private final GapsSearch gapsSearch;
@Autowired
CancelController(GapsSearch gapsSearch) {
this.gapsSearch = gapsSearch;
}
/**
* Forces a stop to searching. Used commonly if navigated away from page.
*
* @return success of search canceled
*/
@RequestMapping(method = RequestMethod.PUT)
public ResponseEntity<String> cancelSearch() {
logger.info("cancelSearch()");
gapsSearch.cancelSearch();
return new ResponseEntity<>("Canceled Search", HttpStatus.OK);
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.controller;
import com.jasonhhouse.gaps.GapsSearch;
import com.jasonhhouse.gaps.PlexLibrary;
import java.util.Set;
import okhttp3.HttpUrl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;
/**
* Handles REST and WebSocket calls for Gaps.
*/
@Controller
public class PlexController {
private final Logger logger = LoggerFactory.getLogger(PlexController.class);
private final GapsSearch gapsSearch;
@Autowired
PlexController(GapsSearch gapsSearch) {
this.gapsSearch = gapsSearch;
}
/**
* Searches Plex for the "Movie" libraries it can find
*
* @param address Host name of the machine to connect to Plex on
* @param port Port Plex runs on
* @param token User specific Plex token
* @return List of PlexLibraries found
*/
@RequestMapping(value = "getPlexLibraries", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Set<PlexLibrary>> getPlexLibraries(@RequestParam("address") String address,
@RequestParam("port") int port,
@RequestParam("token") String token) {
logger.info("getPlexLibraries()");
if (StringUtils.isEmpty(token)) {
String reason = "Plex token cannot be empty";
logger.error(reason);
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, reason);
}
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(address)
.port(port)
.addPathSegment("library")
.addPathSegment("sections")
.addQueryParameter("X-Plex-Token", token)
.build();
Set<PlexLibrary> plexLibraries = gapsSearch.getPlexLibraries(url);
return new ResponseEntity<>(plexLibraries, HttpStatus.OK);
}
}

View File

@@ -1,15 +1,10 @@
/*
* 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;
package com.jasonhhouse.gaps.controller;
import okhttp3.HttpUrl;
import com.jasonhhouse.gaps.Gaps;
import com.jasonhhouse.gaps.GapsSearch;
import com.jasonhhouse.gaps.Movie;
import java.util.Set;
import java.util.concurrent.Future;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@@ -18,77 +13,23 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;
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.server.ResponseStatusException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
/**
* Handles REST and WebSocket calls for Gaps.
*/
@Controller
public class GapsControllerImpl {
public class TmdbController {
private final Logger logger = LoggerFactory.getLogger(GapsControllerImpl.class);
private final Logger logger = LoggerFactory.getLogger(TmdbController.class);
private final GapsSearch gapsSearch;
private final SimpMessagingTemplate template;
@Autowired
GapsControllerImpl(GapsSearch gapsSearch, SimpMessagingTemplate template) {
TmdbController(GapsSearch gapsSearch) {
this.gapsSearch = gapsSearch;
this.template = template;
}
/**
* Searches Plex for the "Movie" libraries it can find
*
* @param address Host name of the machine to connect to Plex on
* @param port Port Plex runs on
* @param token User specific Plex token
* @return List of PlexLibraries found
*/
@RequestMapping(value = "getPlexLibraries", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Set<PlexLibrary>> getPlexLibraries(@RequestParam("address") String address,
@RequestParam("port") int port,
@RequestParam("token") String token) {
logger.info("getPlexLibraries()");
if (StringUtils.isEmpty(token)) {
String reason = "Plex token cannot be empty";
logger.error(reason);
throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, reason);
}
HttpUrl url = new HttpUrl.Builder()
.scheme("http")
.host(address)
.port(port)
.addPathSegment("library")
.addPathSegment("sections")
.addQueryParameter("X-Plex-Token", token)
.build();
Set<PlexLibrary> plexLibraries = gapsSearch.getPlexLibraries(url);
return new ResponseEntity<>(plexLibraries, HttpStatus.OK);
}
/**
* Forces a stop to searching. Used commonly if navigated away from page.
*
* @return success of search canceled
*/
@RequestMapping(value = "cancelSearch", method = RequestMethod.PUT)
public ResponseEntity<String> cancelSearch() {
logger.info("cancelSearch()");
gapsSearch.cancelSearch();
return new ResponseEntity<>("Canceled Search", HttpStatus.OK);
}
/**
@@ -157,5 +98,4 @@ public class GapsControllerImpl {
return gapsSearch.run(gaps);
}
}

View File

@@ -8,8 +8,15 @@
* 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;
package com.jasonhhouse.gaps.service;
import com.jasonhhouse.gaps.Gaps;
import com.jasonhhouse.gaps.GapsSearch;
import com.jasonhhouse.gaps.Movie;
import com.jasonhhouse.gaps.PlexLibrary;
import com.jasonhhouse.gaps.SearchCancelledException;
import com.jasonhhouse.gaps.SearchResults;
import com.jasonhhouse.gaps.UrlGenerator;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -67,9 +74,9 @@ import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@Service
public class GapsSearchBean implements GapsSearch {
public class GapsSearchService implements GapsSearch {
private final Logger logger = LoggerFactory.getLogger(GapsSearchBean.class);
private final Logger logger = LoggerFactory.getLogger(GapsSearchService.class);
private final Set<Movie> searched;
@@ -88,7 +95,7 @@ public class GapsSearchBean implements GapsSearch {
private final SimpMessagingTemplate template;
@Autowired
public GapsSearchBean(@Qualifier("real") UrlGenerator urlGenerator, SimpMessagingTemplate template) {
public GapsSearchService(@Qualifier("real") UrlGenerator urlGenerator, SimpMessagingTemplate template) {
this.template = template;
this.ownedMovies = new HashSet<>();
this.searched = new HashSet<>();

View File

@@ -43,4 +43,10 @@ server:
key-store-password: gapsgapsgaps
key-alias: gaps
security:
require-ssl: true
require-ssl: true
info:
app:
name: Gaps
description: Gaps searches through your Plex Server or local folders for all movies, then queries for known movies in the same collection. If those movies don't exist in your library, Gaps will recommend getting those movies, legally of course.
version: 0.0.5

View File

@@ -117,6 +117,7 @@ function search() {
searchPosition.html('');
backButton.text('restart');
setCopyToClipboardEnabled(true);
disconnect();
},
error: function (err) {
disconnect();
@@ -153,7 +154,6 @@ function connect() {
stompClient.subscribe('/topic/newMovieFound', function (status) {
const obj = JSON.parse(status.body);
showSearchStatus(obj);
shouldDisconnect(obj)
});
});
}
@@ -165,28 +165,15 @@ function disconnect() {
console.log("Disconnected");
}
function shouldDisconnect(obj) {
if (obj && obj.searchedMovieCount && obj.totalMovieCount && obj.totalMovieCount === obj.searchedMovieCount) {
disconnect();
//Cancel Search
$.ajax({
type: "PUT",
url: "cancelSearch",
contentType: "application/json",
});
}
}
function showSearchStatus(obj) {
if (!obj) {
searchResults.html("");
} else {
movieCounter++;
let percentage = Math.trunc(obj.searchedMovieCount / obj.totalMovieCount * 100);
searchPosition.html(`<h5>${obj.searchedMovieCount} of ${obj.totalMovieCount} movies searched. ${percentage}% complete.</h5>`);
if(obj.nextMovie) {
movieCounter++;
searchResults.append(buildMovieDiv(obj.nextMovie));
}
}

View File

@@ -1 +1 @@
"use strict";let stompClient;let backButton;let copyToClipboard;let searchResults;let searchPosition;let progressContainer;let searchTitle;let searchDescription;let movieCounter;document.addEventListener("DOMContentLoaded",function(){const elements=document.querySelectorAll(".modal");M.Modal.init(elements);backButton=$("#cancel");copyToClipboard=$("#copyToClipboard");searchResults=$("#searchResults");searchPosition=$("#searchPosition");progressContainer=$("#progressContainer");searchTitle=$("#searchTitle");searchDescription=$("#searchDescription");backButton.click(function(){$("#warningModal").modal("open")});setCopyToClipboardEnabled(false);copyToClipboard.click(function(){CopyToClipboard("searchResults");M.toast({html:"Copied to Clipboard"})});$("#agree").click(function(){$.ajax({type:"PUT",url:"https://"+location.hostname+":"+location.port+"/cancelSearch",contentType:"application/json"});location.assign("index.html")});connect();search()});window.onbeforeunload=function(){disconnect()};function setCopyToClipboardEnabled(bool){if(bool){copyToClipboard.removeClass("disabled")}else{copyToClipboard.addClass("disabled")}}function search(){movieCounter=0;progressContainer.hide();searchTitle.text("Searching for Movies...");searchDescription.text("Gaps is looking through your Plex libraries. This could take a while so just sit tight and we'll find all the missing movies for you.");const libraries=JSON.parse(Cookies.get("libraries"));const address=Cookies.get("address");const port=Cookies.get("port");const plexToken=Cookies.get("plex_token");const movieDbApiKey=Cookies.get("movie_db_api_key");let plexMovieUrls=[];for(const library of libraries){let data={"X-Plex-Token":plexToken};let plexMovieUrl="http://"+address+":"+port+"/library/sections/"+library.key+"/all/?"+encodeQueryData(data);plexMovieUrls.push(plexMovieUrl)}const gaps={movieDbApiKey:movieDbApiKey,writeToFile:true,searchFromPlex:true,movieUrls:plexMovieUrls};$.ajax({type:"POST",url:"https://"+location.hostname+":"+location.port+"/submit",data:JSON.stringify(gaps),contentType:"application/json",timeout:0,success:function(movies){searchResults.html(buildMoviesDiv(movies));movieCounter=movies.length;searchTitle.text(`${movieCounter} movies to add to complete your collections`);searchDescription.text("Below is everything Gaps found that is missing from your movie collections.");progressContainer.hide();backButton.text("restart");setCopyToClipboardEnabled(true)},error:function(err){disconnect();let message="Unknown error. Check docker Gaps log file.";if(err){message=JSON.parse(err.responseText).message;console.error(message)}searchTitle.text("An error occurred...");searchDescription.text("");progressContainer.hide();backButton.text("restart");setCopyToClipboardEnabled(false)}});showSearchStatus()}function buildMoviesDiv(movies){let result="";for(let movie of movies){result+=buildMovieDiv(movie)}return result}function buildMovieDiv(movie){return"<div>"+buildMovie(movie)+"</div>"}function buildMovie(movie){return`${movie.name} (${movie.year}) from '${movie.collection}'`}function connect(){const socket=new SockJS("/gs-guide-websocket");stompClient=Stomp.over(socket);stompClient.connect({},function(){stompClient.subscribe("/topic/currentSearchResults",function(status){let obj=JSON.parse(status.body);showSearchStatus(obj);shouldDisconnect(obj)})})}function disconnect(){if(stompClient!==null){stompClient.disconnect()}console.log("Disconnected")}function shouldDisconnect(obj){if(obj&&obj.searchedMovieCount&&obj.totalMovieCount&&obj.totalMovieCount===obj.searchedMovieCount){disconnect();$.ajax({type:"PUT",url:"http://"+location.hostname+":"+location.port+"/cancelSearch",contentType:"application/json"})}}function showSearchStatus(obj){if(!obj||!obj.searchedMovieCount&&!obj.totalMovieCount&&obj.totalMovieCount===0){searchResults.html("")}else{progressContainer.show();let percentage=Math.trunc(obj.searchedMovieCount/obj.totalMovieCount*100);searchPosition.html(`<h5>${obj.searchedMovieCount} of ${obj.totalMovieCount} movies searched. ${percentage}% complete.</h5>`);movieCounter=obj.moviesFound.length;searchResults.html(buildMoviesDiv(obj.moviesFound));$("#progressBar").css("width",percentage+"%")}}function encodeQueryData(data){const ret=[];for(let d in data){ret.push(encodeURIComponent(d)+"="+encodeURIComponent(data[d]))}return ret.join("&")}function CopyToClipboard(containerId){let range;if(document.selection){range=document.body.createTextRange();range.moveToElementText(document.getElementById(containerId));range.select().createTextRange();document.execCommand("copy")}else if(window.getSelection){range=document.createRange();range.selectNode(document.getElementById(containerId));window.getSelection().addRange(range);document.execCommand("copy")}}
"use strict";let stompClient;let backButton;let copyToClipboard;let searchResults;let searchPosition;let progressContainer;let searchTitle;let searchDescription;let movieCounter;document.addEventListener("DOMContentLoaded",function(){const elements=document.querySelectorAll(".modal");M.Modal.init(elements);backButton=$("#cancel");copyToClipboard=$("#copyToClipboard");searchResults=$("#searchResults");searchPosition=$("#searchPosition");progressContainer=$("#progressContainer");searchTitle=$("#searchTitle");searchDescription=$("#searchDescription");backButton.click(function(){$("#warningModal").modal("open")});setCopyToClipboardEnabled(false);copyToClipboard.click(function(){CopyToClipboard("searchResults");M.toast({html:"Copied to Clipboard"})});$("#agree").click(function(){$.ajax({type:"PUT",url:"cancelSearch",contentType:"application/json"});location.assign("index.html")});connect();search()});window.onbeforeunload=function(){disconnect()};function setCopyToClipboardEnabled(bool){if(bool){copyToClipboard.removeClass("disabled")}else{copyToClipboard.addClass("disabled")}}function search(){movieCounter=0;progressContainer.show();searchResults.html("");searchTitle.text("Searching for Movies...");searchDescription.text("Gaps is looking through your Plex libraries. This could take a while so just sit tight and we'll find all the missing movies for you.");const libraries=JSON.parse(Cookies.get("libraries"));const address=Cookies.get("address");const port=Cookies.get("port");const plexToken=Cookies.get("plex_token");const movieDbApiKey=Cookies.get("movie_db_api_key");let plexMovieUrls=[];for(const library of libraries){let data={"X-Plex-Token":plexToken};let plexMovieUrl="http://"+address+":"+port+"/library/sections/"+library.key+"/all/?"+encodeQueryData(data);plexMovieUrls.push(plexMovieUrl)}const gaps={movieDbApiKey:movieDbApiKey,writeToFile:true,searchFromPlex:true,movieUrls:plexMovieUrls};$.ajax({type:"POST",url:"submit",data:JSON.stringify(gaps),contentType:"application/json",timeout:0,success:function(){searchTitle.text(`${movieCounter} movies to add to complete your collections`);searchDescription.text("Below is everything Gaps found that is missing from your movie collections.");progressContainer.hide();searchPosition.html("");backButton.text("restart");setCopyToClipboardEnabled(true);disconnect()},error:function(err){disconnect();let message="Unknown error. Check docker Gaps log file.";if(err){message=JSON.parse(err.responseText).message;console.error(message)}searchTitle.text("An error occurred...");searchDescription.text("");searchPosition.html("");progressContainer.hide();backButton.text("restart");setCopyToClipboardEnabled(false)}});showSearchStatus()}function buildMovieDiv(movie){return"<div>"+buildMovie(movie)+"</div>"}function buildMovie(movie){return`${movie.name} (${movie.year}) from '${movie.collection}'`}function connect(){const socket=new SockJS("/gs-guide-websocket");stompClient=Stomp.over(socket);stompClient.connect({},function(){stompClient.subscribe("/topic/newMovieFound",function(status){const obj=JSON.parse(status.body);showSearchStatus(obj)})})}function disconnect(){if(stompClient!==null){stompClient.disconnect()}console.log("Disconnected")}function showSearchStatus(obj){if(!obj){searchResults.html("")}else{let percentage=Math.trunc(obj.searchedMovieCount/obj.totalMovieCount*100);searchPosition.html(`<h5>${obj.searchedMovieCount} of ${obj.totalMovieCount} movies searched. ${percentage}% complete.</h5>`);if(obj.nextMovie){movieCounter++;searchResults.append(buildMovieDiv(obj.nextMovie))}}}function encodeQueryData(data){const ret=[];for(let d in data){ret.push(encodeURIComponent(d)+"="+encodeURIComponent(data[d]))}return ret.join("&")}function CopyToClipboard(containerId){let range;if(document.selection){range=document.body.createTextRange();range.moveToElementText(document.getElementById(containerId));range.select().createTextRange();document.execCommand("copy")}else if(window.getSelection){range=document.createRange();range.selectNode(document.getElementById(containerId));window.getSelection().addRange(range);document.execCommand("copy")}}

View File

@@ -8,16 +8,15 @@
* 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;
package com.jasonhhouse.gaps;
import com.jasonhhouse.gaps.service.GapsSearchService;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.server.ResponseStatusException;
@@ -28,17 +27,17 @@ import java.util.concurrent.Future;
import static org.junit.jupiter.api.Assertions.assertEquals;
class GapsSearchBeanTest {
class GapsSearchServiceTest {
private final Gaps gaps = new Gaps();
private GapsUrlGeneratorTest gapsUrlGeneratorTest;
private GapsSearchBean gapsSearch;
private GapsSearchService gapsSearch;
@BeforeEach
void setup() throws Exception {
gapsUrlGeneratorTest = new GapsUrlGeneratorTest();
SimpMessagingTemplate template = new SimpMessagingTemplate((message, l) -> true);
gapsSearch = new GapsSearchBean(gapsUrlGeneratorTest, template);
gapsSearch = new GapsSearchService(gapsUrlGeneratorTest, template);
// Create a MockWebServer. These are lean enough that you can create a new
// instance for every unit test.

View File

@@ -1,4 +1,4 @@
package com.jasonhhouse.Gaps;
package com.jasonhhouse.gaps;
import okhttp3.HttpUrl;
import okhttp3.mockwebserver.Dispatcher;