From 6f40475dfb7dfb7b60b9bb191614ef17c0d2be47 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Mon, 11 Dec 2017 12:17:51 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F?= =?UTF-8?q?=D0=B5=D0=BC=D1=81=D1=8F=20=D0=BE=D1=82=20war?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Теперь KinoSearch самодостаточен и может запускаться без внешних веб-контейнеров --- pom.xml | 78 +++++++----- src/main/java/kinosearch/webapp/Main.java | 16 +++ .../java/kinosearch/webapp/SpringConfig.java | 40 ++++++ .../kinosearch/webapp/SpringMvcConfig.java | 31 +++++ src/main/java/kinosearch/webapp/WebApp.java | 62 ++++++++++ .../webapp/WebAppConfiguration.java | 56 --------- .../kinosearch/webapp/WebAppController.java | 83 +++++++------ .../kinosearch/webapp/WebAppInitializer.java | 24 ---- src/main/resources/application.properties | 2 + .../resources/kinosearch/webapp/about.ftl | 28 +++++ .../kinosearch/webapp/fother.inc.ftl} | 0 .../kinosearch/webapp/header.inc.ftl | 35 ++++++ .../resources/kinosearch/webapp/index.ftl | 62 ++++++++++ .../kinosearch/webapp/news.inc.ftl} | 6 +- .../resources/kinosearch/webapp/player.ftl | 117 ++++++++++++++++++ .../css/awesome-bootstrap-checkbox.css | 0 .../webapp/static}/css/bootstrap.min.css | 0 .../kinosearch/webapp/static}/css/style.css | 8 +- .../fonts/glyphicons-halflings-regular.eot | Bin .../fonts/glyphicons-halflings-regular.svg | 0 .../fonts/glyphicons-halflings-regular.ttf | Bin .../fonts/glyphicons-halflings-regular.woff | Bin .../fonts/glyphicons-halflings-regular.woff2 | Bin .../webapp/static}/js/bootstrap.min.js | 0 .../webapp/static}/js/jquery-2.1.4.min.js | 0 .../webapp/static}/js/js.cookie-2.1.0.min.js | 0 .../kinosearch/webapp/static}/js/player.js | 30 +++-- .../kinosearch/webapp/static/ks3logo.svg | 34 +++++ .../webapp/static}/noise-background.png | Bin src/main/webapp/WEB-INF/header.inc.html | 35 ------ src/main/webapp/WEB-INF/index.html | 62 ---------- src/main/webapp/WEB-INF/player.html | 117 ------------------ .../webapp/WEB-INF/simple_template/about.html | 28 ----- src/main/webapp/favicon.png | Bin 9662 -> 0 bytes 34 files changed, 541 insertions(+), 413 deletions(-) create mode 100644 src/main/java/kinosearch/webapp/Main.java create mode 100644 src/main/java/kinosearch/webapp/SpringConfig.java create mode 100644 src/main/java/kinosearch/webapp/SpringMvcConfig.java create mode 100644 src/main/java/kinosearch/webapp/WebApp.java delete mode 100644 src/main/java/kinosearch/webapp/WebAppConfiguration.java delete mode 100644 src/main/java/kinosearch/webapp/WebAppInitializer.java create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/kinosearch/webapp/about.ftl rename src/main/{webapp/WEB-INF/fother.inc.html => resources/kinosearch/webapp/fother.inc.ftl} (100%) create mode 100644 src/main/resources/kinosearch/webapp/header.inc.ftl create mode 100644 src/main/resources/kinosearch/webapp/index.ftl rename src/main/{webapp/WEB-INF/news.inc.html => resources/kinosearch/webapp/news.inc.ftl} (77%) create mode 100644 src/main/resources/kinosearch/webapp/player.ftl rename src/main/{webapp => resources/kinosearch/webapp/static}/css/awesome-bootstrap-checkbox.css (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/css/bootstrap.min.css (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/css/style.css (80%) rename src/main/{webapp => resources/kinosearch/webapp/static}/fonts/glyphicons-halflings-regular.eot (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/fonts/glyphicons-halflings-regular.svg (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/fonts/glyphicons-halflings-regular.ttf (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/fonts/glyphicons-halflings-regular.woff (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/fonts/glyphicons-halflings-regular.woff2 (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/js/bootstrap.min.js (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/js/jquery-2.1.4.min.js (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/js/js.cookie-2.1.0.min.js (100%) rename src/main/{webapp => resources/kinosearch/webapp/static}/js/player.js (89%) create mode 100644 src/main/resources/kinosearch/webapp/static/ks3logo.svg rename src/main/{webapp => resources/kinosearch/webapp/static}/noise-background.png (100%) delete mode 100644 src/main/webapp/WEB-INF/header.inc.html delete mode 100644 src/main/webapp/WEB-INF/index.html delete mode 100644 src/main/webapp/WEB-INF/player.html delete mode 100644 src/main/webapp/WEB-INF/simple_template/about.html delete mode 100644 src/main/webapp/favicon.png diff --git a/pom.xml b/pom.xml index e391166..444ddc7 100644 --- a/pom.xml +++ b/pom.xml @@ -15,46 +15,51 @@ UTF-8 1.8 - 4.2.5.RELEASE + 4.3.7.RELEASE + 9.4.0.v20161208 ru.dmitriymx kinosearch - 2.0.9 - war + 2.0.10-SNAPSHOT + + org.springframework + spring-core + ${spring.version} + org.springframework spring-context ${spring.version} + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + + + + + org.springframework + spring-webmvc + ${spring.version} + org.springframework spring-context-support ${spring.version} - - org.springframework - spring-webmvc - ${spring.version} - - - javax.servlet - javax.servlet-api - 3.1.0 - - - javax.servlet.jsp - javax.servlet.jsp-api - 2.3.1 - - - javax.servlet - jstl - 1.2 - + org.jsoup @@ -76,6 +81,7 @@ httpclient 4.5.2 + junit @@ -114,16 +120,30 @@ org.apache.maven.plugins - maven-war-plugin - 3.0.0 + maven-jar-plugin + 2.4 - false + + + true + dependency/ + + - org.eclipse.jetty - jetty-maven-plugin - 9.4.0.v20161208 + org.apache.maven.plugins + maven-dependency-plugin + 2.8 + + + copy-dependencies + package + + copy-dependencies + + + diff --git a/src/main/java/kinosearch/webapp/Main.java b/src/main/java/kinosearch/webapp/Main.java new file mode 100644 index 0000000..51956cf --- /dev/null +++ b/src/main/java/kinosearch/webapp/Main.java @@ -0,0 +1,16 @@ +/* + * DmitriyMX + * 2017-12-11 + */ +package kinosearch.webapp; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class Main { + public static void main(String[] args) { + ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); + WebApp webApp = ctx.getBean("webapp", WebApp.class); + webApp.start(); + } +} diff --git a/src/main/java/kinosearch/webapp/SpringConfig.java b/src/main/java/kinosearch/webapp/SpringConfig.java new file mode 100644 index 0000000..348a303 --- /dev/null +++ b/src/main/java/kinosearch/webapp/SpringConfig.java @@ -0,0 +1,40 @@ +/* + * DmitriyMX + * 2017-12-11 + */ +package kinosearch.webapp; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; + +@Configuration +@Import(kinosearch.core.SpringConfig.class) +@PropertySource("classpath:/application.properties") +public class SpringConfig { + @Bean + public WebApp webapp(@Value("${webapp.host}") String host, @Value("${webapp.port}") int port) { + return new WebApp(host, port); + } + + @Bean + public ViewResolver viewResolver() { + FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver(); + viewResolver.setContentType("text/html;charset=UTF-8"); + viewResolver.setCache(true); + viewResolver.setSuffix(".ftl"); + return viewResolver; + } + + @Bean + public FreeMarkerConfigurer freemarkerConfig() { + FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer(); + freeMarkerConfigurer.setTemplateLoaderPath("classpath:/kinosearch/webapp/"); + return freeMarkerConfigurer; + } +} diff --git a/src/main/java/kinosearch/webapp/SpringMvcConfig.java b/src/main/java/kinosearch/webapp/SpringMvcConfig.java new file mode 100644 index 0000000..7d3bcb9 --- /dev/null +++ b/src/main/java/kinosearch/webapp/SpringMvcConfig.java @@ -0,0 +1,31 @@ +/* + * DmitriyMX + * 2017-12-11 + */ +package kinosearch.webapp; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +@Configuration +@EnableWebMvc +public class SpringMvcConfig extends WebMvcConfigurerAdapter { + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + final String staticPath = "classpath:/kinosearch/webapp/static/"; + /* Styles */ + registry.addResourceHandler("/css/**") + .addResourceLocations(staticPath+"css/"); + /* Fonts */ + registry.addResourceHandler("/fonts/**") + .addResourceLocations(staticPath+"fonts/"); + /* JavaScript */ + registry.addResourceHandler("/js/**") + .addResourceLocations(staticPath+"js/"); + /* Other */ + registry.addResourceHandler("/*.svg").addResourceLocations(staticPath); + registry.addResourceHandler("/*.png").addResourceLocations(staticPath); + } +} diff --git a/src/main/java/kinosearch/webapp/WebApp.java b/src/main/java/kinosearch/webapp/WebApp.java new file mode 100644 index 0000000..d85c7a3 --- /dev/null +++ b/src/main/java/kinosearch/webapp/WebApp.java @@ -0,0 +1,62 @@ +/* + * DmitriyMX + * 2017-12-11 + */ +package kinosearch.webapp; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import java.net.InetSocketAddress; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class WebApp { + private final Logger logger = Logger.getLogger(WebApp.class.getName()); + private final String host; + private final int port; + + public WebApp(String host, int port) { + this.host = host; + this.port = port; + } + + void start() { + Server server = new Server(new InetSocketAddress(host, port)); + server.setHandler(getServletContextHandler(getContext())); + try { + server.start(); + server.join(); + } catch (Exception e) { + logger.log(Level.SEVERE, "Start server", e); + } + } + + private ServletContextHandler getServletContextHandler(WebApplicationContext context) { + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setErrorHandler(null); + contextHandler.setContextPath("/"); + contextHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), "/*"); + contextHandler.addEventListener(new ContextLoaderListener(context)); + return contextHandler; + } + + private WebApplicationContext getContext() { + AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); + context.setConfigLocation("kinosearch.webapp"); + return context; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } +} diff --git a/src/main/java/kinosearch/webapp/WebAppConfiguration.java b/src/main/java/kinosearch/webapp/WebAppConfiguration.java deleted file mode 100644 index 81b3049..0000000 --- a/src/main/java/kinosearch/webapp/WebAppConfiguration.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * DmitriyMX - * 2017-01-04 - */ -package kinosearch.webapp; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import kinosearch.core.KinoPlay; -import kinosearch.core.SpringConfig; -import org.springframework.context.annotation.*; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; -import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; - -@Configuration -@EnableWebMvc -@ComponentScan -@Import(SpringConfig.class) -public class WebAppConfiguration extends WebMvcConfigurerAdapter { - @Bean - public ViewResolver viewResolver() { - FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver(); - viewResolver.setCache(true); - viewResolver.setSuffix(".html"); - return viewResolver; - } - - @Bean - public FreeMarkerConfigurer freemarkerConfig() { - FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer(); - freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/"); - return freeMarkerConfigurer; - } - - @Bean - @Scope("singleton") - public Gson gson() { - return new GsonBuilder().registerTypeAdapter(KinoPlay.class, new KinoPlaySerializer()).create(); - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/css/**") - .addResourceLocations("/css/"); - registry.addResourceHandler("/fonts/**") - .addResourceLocations("/fonts/"); - registry.addResourceHandler("/js/**") - .addResourceLocations("/js/"); - registry.addResourceHandler("/*.png") - .addResourceLocations("/"); - } -} diff --git a/src/main/java/kinosearch/webapp/WebAppController.java b/src/main/java/kinosearch/webapp/WebAppController.java index 9a5af9d..d5c2395 100644 --- a/src/main/java/kinosearch/webapp/WebAppController.java +++ b/src/main/java/kinosearch/webapp/WebAppController.java @@ -16,57 +16,50 @@ import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; -import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; @Controller -@RequestMapping("/") public class WebAppController { - @Autowired - private ServletContext webAppContext; + private final Logger logger = Logger.getLogger(WebAppController.class.getName()); @Autowired private ApplicationContext coreContext; private void setDefaultModel(ModelMap model) { - model.addAttribute("basedir", webAppContext.getContextPath()); - model.addAttribute("version", "2.0.9"); + model.addAttribute("version", "2.0.10-SNAPSHOT"); model.addAttribute("rutext", "Поиск кино по пиратским кинотеатрам"); } - private void setDefaultResponse(HttpServletResponse response) { - response.setContentType("text/html"); - response.setCharacterEncoding("utf-8"); - } - - @RequestMapping(value = { "/", "/index.html" }, method = RequestMethod.GET) - public String index(ModelMap model, HttpServletRequest request, HttpServletResponse response) { + @RequestMapping(value = {"/", "/index.html"}, method = RequestMethod.GET) + public String index(ModelMap model) { setDefaultModel(model); - setDefaultResponse(response); - - if (request.getParameter("search") != null && !request.getParameter("search").trim().isEmpty()) { - boolean strong = (request.getParameter("strong") != null && request.getParameter("strong").equals("1")); - search(request.getParameter("search"), model, strong); - } - return "index"; } - private void search(String search, ModelMap model, boolean strong) { + @RequestMapping(value = {"/", "/index.html"}, method = RequestMethod.GET, params = {"search"}) + public String search(@RequestParam("search") String searchText, ModelMap model) { + if (searchText.trim().isEmpty()) { + return "redirect:/"; + } + List list = Collections.synchronizedList(new LinkedList<>()); Map kinoWarezMap = coreContext.getBeansOfType(KinoWarez.class); ThreadGroup threadGroup = new ThreadGroup(""); - for (KinoWarez kinoWarez : kinoWarezMap.values()) { //TODO на будущее надо ограничить количество одновременных потоков + for (KinoWarez kinoWarez : kinoWarezMap.values()) { //TODO надо ограничить количество одновременных потоков new Thread(threadGroup, () -> { - List outList = kinoWarez.search(search, strong); + List outList = kinoWarez.search(searchText, false); //FIXME "strong" нужно учитывать for (Kino kino : outList) { kino.setName("[" + kinoWarez.getName() + "] " + kino.getName()); @@ -81,9 +74,12 @@ public class WebAppController { Tools.SafeSleep(1000); } - model.put("searchtext", search); + model.put("searchtext", searchText); model.put("resultsearch", groupKino(list)); - model.put("strong", strong); + model.put("strong", false); //FIXME "strong" нужно учитывать + + setDefaultModel(model); + return "index"; } private List groupKino(List list) { @@ -138,32 +134,28 @@ public class WebAppController { return grouppedList; } - @RequestMapping(value = "/about.html", method = RequestMethod.GET) - public String about(ModelMap model, HttpServletResponse response) { - setDefaultModel(model); - setDefaultResponse(response); - return "simple_template/about"; - } - @RequestMapping(value = "/player/{warez}/**", method = RequestMethod.GET) - public String player(@PathVariable() String warez, ModelMap model, HttpServletRequest request, HttpServletResponse response) { - setDefaultModel(model); - setDefaultResponse(response); - + public String player(@PathVariable() String warez, ModelMap model, HttpServletRequest request) throws MalformedURLException { KinoWarez kinoWarez = coreContext.getBean(warez, KinoWarez.class); if (kinoWarez == null) { return "redirect:/"; } - KinoPlay kinoPlay = kinoWarez.player(request.getServletPath().substring(("/player/"+warez).length())); + + //TODO а необходимость в URL точно оправдана? + URL requestUrl = new URL(request.getRequestURL().toString()); + KinoPlay kinoPlay = kinoWarez.player(requestUrl.getPath().substring(("/player/"+warez).length())); Gson gson = coreContext.getBean(Gson.class); model.put("json", gson.toJson(kinoPlay)); + setDefaultModel(model); return "player"; } @RequestMapping(value = "/proxy/{warez}/**", method = RequestMethod.GET) public void proxy(@PathVariable String warez, HttpServletRequest request, HttpServletResponse response) throws IOException { - String path = request.getServletPath().substring(("/proxy/"+warez+"/").length()); + //TODO а необходимость в URL точно оправдана? + URL requestUrl = new URL(request.getRequestURL().toString()); + String path = requestUrl.getPath().substring(("/proxy/"+warez+"/").length()); URL url = new URL("http://" + path); HttpURLConnection con =(HttpURLConnection) url.openConnection(); @@ -211,4 +203,19 @@ public class WebAppController { webToProxyBuf.close(); con.disconnect(); } + + @RequestMapping(value = {"/about", "/about.html"}, method = RequestMethod.GET) + public String about(ModelMap model) { + setDefaultModel(model); + return "about"; + } + + @RequestMapping(value = "/favicon.ico") + public void favicon(HttpServletResponse response) { + try { + response.sendError(404); + } catch (IOException e) { + logger.log(Level.WARNING, "favicon 404", e); + } + } } diff --git a/src/main/java/kinosearch/webapp/WebAppInitializer.java b/src/main/java/kinosearch/webapp/WebAppInitializer.java deleted file mode 100644 index d0ed584..0000000 --- a/src/main/java/kinosearch/webapp/WebAppInitializer.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * DmitriyMX - * 2017-01-04 - */ -package kinosearch.webapp; - -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { - @Override - protected Class[] getRootConfigClasses() { - return new Class[]{WebAppConfiguration.class}; - } - - @Override - protected Class[] getServletConfigClasses() { - return new Class[0]; - } - - @Override - protected String[] getServletMappings() { - return new String[]{"/"}; - } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..8ddd94a --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,2 @@ +webapp.host=127.0.0.1 +webapp.port=8080 \ No newline at end of file diff --git a/src/main/resources/kinosearch/webapp/about.ftl b/src/main/resources/kinosearch/webapp/about.ftl new file mode 100644 index 0000000..f58e8b6 --- /dev/null +++ b/src/main/resources/kinosearch/webapp/about.ftl @@ -0,0 +1,28 @@ +[#ftl] +[#include "/header.inc.ftl"] +
+

+ Частенько бывает, что желанный фильм, мультфильм или сериал располагается только на одном каком-то кино-ресурсе. + И хорошо, если этот ресурс был первым в вашем списке ручного поиска кино.
+ А если нет?
+ А если у вас таких сайтов 5-10?
+ А если на всех ваших любимых сайтах не оказалась искомого?
+ Тогда вы лезете в Google/Яндекс и... + И обязательно напарываетесь на какую-то напичканную рекламой хрень, где еще и вылезет ошибка "фильм не найден". + Поиск "кинца" начинает затягиваться, а желание его посмотреть и вовсе улетучится. +

+

+ Нет, так быть не должно! Если уж решился смотреть кино в онлайне, то пусть это будет комфортно!
+ Мой проект возьмет всю рутину поиска на себя, а вам остается только выбрать место просмотра и наслаждаться + фильмом/сериалом/еще чем-то. +

+

+ Кинотеатры подбираются так, чтобы в них было поменьше рекламы и побольше нужных фильмов. А в скором времени я и + это ... упрощу =) +

+

+ Приятного просмотра! +

+
+

Автор: DmitriyMX/2015

+[#include "/fother.inc.ftl"] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/fother.inc.html b/src/main/resources/kinosearch/webapp/fother.inc.ftl similarity index 100% rename from src/main/webapp/WEB-INF/fother.inc.html rename to src/main/resources/kinosearch/webapp/fother.inc.ftl diff --git a/src/main/resources/kinosearch/webapp/header.inc.ftl b/src/main/resources/kinosearch/webapp/header.inc.ftl new file mode 100644 index 0000000..9232b96 --- /dev/null +++ b/src/main/resources/kinosearch/webapp/header.inc.ftl @@ -0,0 +1,35 @@ +[#ftl] + + + + + + + + KinoSearch :: ${rutext} + + + + + + + + +
+

KinoSearch

+

${rutext}

+
+
+ + + + +
+
+ + +
+
+ \ No newline at end of file diff --git a/src/main/resources/kinosearch/webapp/index.ftl b/src/main/resources/kinosearch/webapp/index.ftl new file mode 100644 index 0000000..a9fa28b --- /dev/null +++ b/src/main/resources/kinosearch/webapp/index.ftl @@ -0,0 +1,62 @@ +[#ftl] +[#include "/header.inc.ftl"] +[#if resultsearch??] + +
+ [#if resultsearch?has_content] + [#list resultsearch as kino] + [#if kino.getClass().getSimpleName() == "KinoGroup"] +
+ +  ${kino.name} + +
+
+ [#list kino.kinolist as kino_groupped] +
+
${kino_groupped.name}
+
+ +
+
+ [/#list] +
+
+
+ [#else] +
+
${kino.name}
+
+ +
+
+ [/#if] + [/#list] + [#else] +

Ничего не найдено =(

+ [/#if] +[#else] + [#include "/news.inc.ftl"] +[/#if] +[#include "/fother.inc.ftl"] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/news.inc.html b/src/main/resources/kinosearch/webapp/news.inc.ftl similarity index 77% rename from src/main/webapp/WEB-INF/news.inc.html rename to src/main/resources/kinosearch/webapp/news.inc.ftl index 015b1e1..bc38528 100644 --- a/src/main/webapp/WEB-INF/news.inc.html +++ b/src/main/resources/kinosearch/webapp/news.inc.ftl @@ -46,9 +46,9 @@

Version 2.0.7

-

Долгожданное обновление: наконец то появился плеер! О да! Долой центнеры рекламы, что предлагает нам... да что только не! Начиная от "купи слона", до "мега-умбер-онлайн игра, клонов которой уже нисчесть! играй сейчас! живо!!!".

-

Кхм. Простите.

-

Вообщем, тестовая вариация плеера запущена для кинотеатра OnlineLife. Если нареканий не будет, то запущу и для остальных.

+

Долгожданное обновление: наконец то появился плеер! О да! Долой центнеры рекламы, что предлагает нам... да что только не! Начиная от "купи слона", до "мега-умбер-онлайн игра, клонов которой уже нисчесть! играй сейчас! живо!!!".

+

Кхм. Простите.

+

Вообщем, тестовая вариация плеера запущена для кинотеатра OnlineLife. Если нареканий не будет, то запущу и для остальных.

diff --git a/src/main/resources/kinosearch/webapp/player.ftl b/src/main/resources/kinosearch/webapp/player.ftl new file mode 100644 index 0000000..d3b13b3 --- /dev/null +++ b/src/main/resources/kinosearch/webapp/player.ftl @@ -0,0 +1,117 @@ +[#ftl] +[#include "/header.inc.ftl"] + + + + + + +
+ + + + + +
+ +

+ +
+ + +
+[#include "/fother.inc.ftl"] \ No newline at end of file diff --git a/src/main/webapp/css/awesome-bootstrap-checkbox.css b/src/main/resources/kinosearch/webapp/static/css/awesome-bootstrap-checkbox.css similarity index 100% rename from src/main/webapp/css/awesome-bootstrap-checkbox.css rename to src/main/resources/kinosearch/webapp/static/css/awesome-bootstrap-checkbox.css diff --git a/src/main/webapp/css/bootstrap.min.css b/src/main/resources/kinosearch/webapp/static/css/bootstrap.min.css similarity index 100% rename from src/main/webapp/css/bootstrap.min.css rename to src/main/resources/kinosearch/webapp/static/css/bootstrap.min.css diff --git a/src/main/webapp/css/style.css b/src/main/resources/kinosearch/webapp/static/css/style.css similarity index 80% rename from src/main/webapp/css/style.css rename to src/main/resources/kinosearch/webapp/static/css/style.css index 7ebbf37..a40b57c 100644 --- a/src/main/webapp/css/style.css +++ b/src/main/resources/kinosearch/webapp/static/css/style.css @@ -12,12 +12,10 @@ body { font-weight: bold; } -.title .logo { - background-image: url("../favicon.png"); - width: 2em; +.title img { + vertical-align: bottom; + height: 1.119em; display: inline-block; - background-size: 2em auto; - background-position: 0px center; } .title a { diff --git a/src/main/webapp/fonts/glyphicons-halflings-regular.eot b/src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from src/main/webapp/fonts/glyphicons-halflings-regular.eot rename to src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.eot diff --git a/src/main/webapp/fonts/glyphicons-halflings-regular.svg b/src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from src/main/webapp/fonts/glyphicons-halflings-regular.svg rename to src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.svg diff --git a/src/main/webapp/fonts/glyphicons-halflings-regular.ttf b/src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from src/main/webapp/fonts/glyphicons-halflings-regular.ttf rename to src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.ttf diff --git a/src/main/webapp/fonts/glyphicons-halflings-regular.woff b/src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/main/webapp/fonts/glyphicons-halflings-regular.woff rename to src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.woff diff --git a/src/main/webapp/fonts/glyphicons-halflings-regular.woff2 b/src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from src/main/webapp/fonts/glyphicons-halflings-regular.woff2 rename to src/main/resources/kinosearch/webapp/static/fonts/glyphicons-halflings-regular.woff2 diff --git a/src/main/webapp/js/bootstrap.min.js b/src/main/resources/kinosearch/webapp/static/js/bootstrap.min.js similarity index 100% rename from src/main/webapp/js/bootstrap.min.js rename to src/main/resources/kinosearch/webapp/static/js/bootstrap.min.js diff --git a/src/main/webapp/js/jquery-2.1.4.min.js b/src/main/resources/kinosearch/webapp/static/js/jquery-2.1.4.min.js similarity index 100% rename from src/main/webapp/js/jquery-2.1.4.min.js rename to src/main/resources/kinosearch/webapp/static/js/jquery-2.1.4.min.js diff --git a/src/main/webapp/js/js.cookie-2.1.0.min.js b/src/main/resources/kinosearch/webapp/static/js/js.cookie-2.1.0.min.js similarity index 100% rename from src/main/webapp/js/js.cookie-2.1.0.min.js rename to src/main/resources/kinosearch/webapp/static/js/js.cookie-2.1.0.min.js diff --git a/src/main/webapp/js/player.js b/src/main/resources/kinosearch/webapp/static/js/player.js similarity index 89% rename from src/main/webapp/js/player.js rename to src/main/resources/kinosearch/webapp/static/js/player.js index 97a3abf..fe1df2b 100644 --- a/src/main/webapp/js/player.js +++ b/src/main/resources/kinosearch/webapp/static/js/player.js @@ -1,11 +1,9 @@ -function PlayerCore(baseDir, playerObj, titleObj, videoData) { - this.baseDir = baseDir; +function PlayerCore(playerObj, titleObj, videoData) { this.playerObj = playerObj; this.titleObj = titleObj; this.videoData = videoData; this.origDocTitle = document.title; - this.path = window.location.pathname.substr(baseDir.length); this.timeLast = 0; this.msToTime = function(ms) { @@ -23,22 +21,22 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { 'm': addZ(_min), 'h': addZ(_hr) }; - } + }; this.setTitle = function(title = videoData.title) { document.title = title + " :: " + this.origDocTitle; titleObj.text(title); titleObj.show(); - } + }; this.getType = function() { return this.videoData.type; - } + }; this.setupPlayerForOneFilm = function() { - playerObj.attr('src', this.baseDir + videoData.file); + playerObj.attr('src', videoData.file); playerObj.load(); - } + }; this.setupPlayerForSimpleSerial = function(serialBlock) { var menu = serialBlock.find('.dropdown-menu'); @@ -53,7 +51,7 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { }); serialBlock.removeClass('hide'); - } + }; this.setupPlayerForSeasonSerial = function(seasonBlock, serialBlock) { var menu = seasonBlock.find('.dropdown-menu'); @@ -62,13 +60,13 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { this.videoData.seasons.forEach(function(item, i) { var aTag = $('', {'href':'#', 'text':item.title}); aTag.click(function(){_self.setSeason(i, seasonBlock, serialBlock)}); - var liTag = $('
  • ') + var liTag = $('
  • '); liTag.append(aTag); menu.append(liTag); }); seasonBlock.removeClass('hide'); - } + }; this.setupPlayer = function() { var _self = this; @@ -90,17 +88,17 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { console.debug({'path': _self.path, 'saveTime': save_data}); } }); - } + }; this.setSerial = function(idx, serialBlock, sidx = 0) { var title; var playerSrc; if (this.getType() == 'seasons_serial') { title = videoData.seasons[sidx].serials[idx].title; - playerSrc = this.baseDir + videoData.seasons[sidx].serials[idx].file; + playerSrc = videoData.seasons[sidx].serials[idx].file; } else { title = videoData.serials[idx].title; - playerSrc = this.baseDir + videoData.serials[idx].file; + playerSrc = videoData.serials[idx].file; } this.setTitle(title); @@ -110,7 +108,7 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { playerObj.attr('src', playerSrc); playerObj.attr('data-serial', idx); playerObj.load(); - } + }; this.setSeason = function(idx, seasonBlock, serialBlock) { var title = videoData.seasons[idx].title; @@ -124,7 +122,7 @@ function PlayerCore(baseDir, playerObj, titleObj, videoData) { this.videoData.seasons[idx].serials.forEach(function(item, i) { var aTag = $('', {'href':'#', 'text':item.title}); aTag.click(function(){_self.setSerial(i, serialBlock, idx)}); - var liTag = $('
  • ') + var liTag = $('
  • '); liTag.append(aTag); menu.append(liTag); }); diff --git a/src/main/resources/kinosearch/webapp/static/ks3logo.svg b/src/main/resources/kinosearch/webapp/static/ks3logo.svg new file mode 100644 index 0000000..555ee0c --- /dev/null +++ b/src/main/resources/kinosearch/webapp/static/ks3logo.svg @@ -0,0 +1,34 @@ + + diff --git a/src/main/webapp/noise-background.png b/src/main/resources/kinosearch/webapp/static/noise-background.png similarity index 100% rename from src/main/webapp/noise-background.png rename to src/main/resources/kinosearch/webapp/static/noise-background.png diff --git a/src/main/webapp/WEB-INF/header.inc.html b/src/main/webapp/WEB-INF/header.inc.html deleted file mode 100644 index 0f8d6ea..0000000 --- a/src/main/webapp/WEB-INF/header.inc.html +++ /dev/null @@ -1,35 +0,0 @@ -[#ftl] - - - - - - - - KinoSearch :: ${rutext} - - - - - - - - -
    -

    KinoSearch

    -

    ${rutext}

    -
    -
    - - - - -
    -
    - - -
    -
    - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/index.html b/src/main/webapp/WEB-INF/index.html deleted file mode 100644 index 800b48d..0000000 --- a/src/main/webapp/WEB-INF/index.html +++ /dev/null @@ -1,62 +0,0 @@ -[#ftl] -[#include "/header.inc.html"] - [#if resultsearch??] - -
    - [#if resultsearch?has_content] - [#list resultsearch as kino] - [#if kino.getClass().getSimpleName() == "KinoGroup"] -
    - -  ${kino.name} - -
    -
    - [#list kino.kinolist as kino_groupped] -
    -
    ${kino_groupped.name}
    -
    - -
    -
    - [/#list] -
    -
    -
    - [#else] -
    -
    ${kino.name}
    -
    - -
    -
    - [/#if] - [/#list] - [#else] -

    Ничего не найдено =(

    - [/#if] - [#else] - [#include "/news.inc.html"] - [/#if] -[#include "/fother.inc.html"] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/player.html b/src/main/webapp/WEB-INF/player.html deleted file mode 100644 index b25d1b4..0000000 --- a/src/main/webapp/WEB-INF/player.html +++ /dev/null @@ -1,117 +0,0 @@ -[#ftl] -[#include "/header.inc.html"] - - - - - - -
    - - - - - -
    - -

    - -
    - - -
    -[#include "/fother.inc.html"] \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/simple_template/about.html b/src/main/webapp/WEB-INF/simple_template/about.html deleted file mode 100644 index bf84f4e..0000000 --- a/src/main/webapp/WEB-INF/simple_template/about.html +++ /dev/null @@ -1,28 +0,0 @@ -[#ftl] -[#include "/header.inc.html"] -
    -

    - Частенько бывает, что желанный фильм, мультфильм или сериал располагается только на одном каком-то кино-ресурсе. - И хорошо, если этот ресурс был первым в вашем списке ручного поиска кино.
    - А если нет?
    - А если у вас таких сайтов 5-10?
    - А если на всех ваших любимых сайтах не оказалась искомого?
    - Тогда вы лезете в Google/Яндекс и... - И обязательно напарываетесь на какую-то напичканную рекламой хрень, где еще и вылезет ошибка "фильм не найден". - Поиск "кинца" начинает затягиваться, а желание его посмотреть и вовсе улетучится. -

    -

    - Нет, так быть не должно! Если уж решился смотреть кино в онлайне, то пусть это будет комфортно!
    - Мой проект возьмет всю рутину поиска на себя, а вам остается только выбрать место просмотра и наслаждаться - фильмом/сериалом/еще чем-то. -

    -

    - Кинотеатры подбираются так, чтобы в них было поменьше рекламы и побольше нужных фильмов. А в скором времени я и - это ... упрощу =) -

    -

    - Приятного просмотра! -

    -
    -

    Автор: DmitriyMX / 2015

    -[#include "/fother.inc.html"] \ No newline at end of file diff --git a/src/main/webapp/favicon.png b/src/main/webapp/favicon.png deleted file mode 100644 index 987a50bd644d70783e3b7a58234b5d62d64428af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9662 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