├── public ├── stylesheets │ └── main.css ├── javascripts │ └── hello.js └── images │ └── favicon.png ├── activator-launch-1.2.10.jar ├── app ├── views │ ├── index.scala.html │ └── main.scala.html └── controllers │ ├── Application.scala │ ├── AnimeMovieV1.scala │ └── AnimeV1.scala ├── project ├── build.properties └── plugins.sbt ├── .gitignore ├── history.md ├── LICENSE ├── deploy └── nginx.conf ├── test ├── IntegrationSpec.scala └── ApplicationSpec.scala ├── conf ├── routes └── application.conf ├── README.md └── activator /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-ShangriLa/sora-playframework-scala/HEAD/public/images/favicon.png -------------------------------------------------------------------------------- /activator-launch-1.2.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-ShangriLa/sora-playframework-scala/HEAD/activator-launch-1.2.10.jar -------------------------------------------------------------------------------- /app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat May 02 16:57:05 JST 2015 3 | template.uuid=ba2ec772-a214-4c55-ba57-cae483baac1f 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /history.md: -------------------------------------------------------------------------------- 1 | # 履歴 2 | 3 | ## 2016/12/12 4 | 5 | - 映画対応エンドポイント追加 6 | - /master/movie 7 | - アニメ映画は暫定的に10作品程度追加 8 | - 2016年以降は完備予定 9 | 10 | ## 2016/11 11 | 12 | - アニメ聖地対応 13 | - city_name & city_codeを暫定的に追加 14 | - マスターデータは順次対応 15 | -------------------------------------------------------------------------------- /app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.db._ 5 | import play.api.mvc._ 6 | import play.api.Play.current 7 | import play.api.libs.json.Json 8 | import anorm._ 9 | 10 | object Application extends Controller { 11 | 12 | def index = Action { 13 | Ok(views.html.index("ShangriLa Anime API Server")) 14 | } 15 | } -------------------------------------------------------------------------------- /app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /deploy/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 2; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | 9 | proxy_buffering off; 10 | proxy_set_header X-Real-IP $remote_addr; 11 | proxy_set_header X-Scheme $scheme; 12 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 13 | proxy_set_header Host $http_host; 14 | 15 | upstream api-anime-server-play-scala { 16 | server 127.0.0.1:19000; 17 | } 18 | 19 | server { 20 | listen 80; 21 | server_name api.moemoe.tokyo; 22 | location / { 23 | proxy_pass http://api-anime-server-play-scala; 24 | } 25 | } 26 | 27 | server { 28 | listen 80; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index 7 | 8 | #Anime API V1 9 | ## master 10 | GET /anime/v1/master/cours controllers.AnimeV1.masterList 11 | GET /anime/v1/master/$year_num<[0-9]{4}+> controllers.AnimeV1.year(year_num) 12 | GET /anime/v1/master/$year_num<[0-9]{4}+>/$cours<[1-4]+> controllers.AnimeV1.yearCours(year_num, cours) 13 | 14 | ## master-movie 15 | GET /anime/v1/master/movie/year controllers.AnimeMovieV1.masterList 16 | GET /anime/v1/master/movie/$year_num<[0-9]{4}+> controllers.AnimeMovieV1.year(year_num) 17 | GET /anime/v1/master/movie/$year_num<[0-9]{4}+>/$month<[1-9]?[0-9]> controllers.AnimeMovieV1.yearMonth(year_num, month) 18 | 19 | # Map static resources from the /public folder to the /assets URL path 20 | GET /assets/*file controllers.Assets.at(path="/public", file) 21 | -------------------------------------------------------------------------------- /conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="A_Q;sEU2Nau`aQ8w1TZVfwLor=7fNPikm;1<:PSwvm1VatpJ6epGedI4ODx4b@o7" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | # db.default.driver=org.h2.Driver 40 | # db.default.url="jdbc:h2:mem:play" 41 | # db.default.user=sa 42 | # db.default.password="" 43 | db.default.driver=com.mysql.jdbc.Driver 44 | db.default.url="jdbc:mysql://localhost/anime_admin_development?characterEncoding=UTF8" 45 | db.default.user="root" 46 | db.default.password="" 47 | 48 | # Evolutions 49 | # ~~~~~ 50 | # You can disable evolutions if needed 51 | # evolutionplugin=disabled 52 | 53 | # Logger 54 | # ~~~~~ 55 | # You can also configure logback (http://logback.qos.ch/), 56 | # by providing an application-logger.xml file in the conf directory. 57 | 58 | # Root logger: 59 | logger.root=ERROR 60 | 61 | # Logger used by the framework: 62 | logger.play=INFO 63 | 64 | # Logger provided to your application: 65 | logger.application=DEBUG 66 | 67 | -------------------------------------------------------------------------------- /app/controllers/AnimeMovieV1.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import java.util.Date 4 | 5 | import anorm._ 6 | import play.api.Logger 7 | import play.api.Play.current 8 | import play.api.db._ 9 | import play.api.libs.json.{JsNumber, JsString, Json} 10 | import play.api.mvc._ 11 | 12 | object AnimeMovieV1 extends Controller { 13 | 14 | def masterList = Action { 15 | 16 | // TODO @AKB428 Ehcacheを使う 17 | DB.withConnection { implicit c => 18 | val records = SQL("SELECT year FROM movie_year_infos ORDER BY year")().map { 19 | row => row[Int]("year") 20 | }.toList 21 | 22 | Ok(Json.toJson(records)) 23 | } 24 | } 25 | 26 | def year(year_num: String) = Action { 27 | DB.withConnection { implicit c => 28 | val bases_records = SQL("SELECT * FROM base_movies WHERE year = {YEAR} ORDER BY start_date").on("YEAR" -> year_num)().map { 29 | row => 30 | Map( 31 | "id" -> JsNumber(row[Int]("id")), 32 | "title" -> JsString(row[String]("title")), 33 | "title_short1" -> JsString(row[String]("title_short1")), 34 | "title_short2" -> JsString(row[Option[String]]("title_short2").getOrElse("")), 35 | "title_short3" -> JsString(row[Option[String]]("title_short3").getOrElse("")), 36 | "year" -> JsNumber(row[Int]("year")), 37 | "start_month" -> JsNumber(BigDecimal(row[Option[Int]]("start_month").getOrElse(0))), 38 | "start_date" -> JsString(row[Option[Date]]("start_date").getOrElse("").toString), 39 | "release_schedule_season" -> JsNumber(BigDecimal(row[Option[Int]]("release_schedule_season").getOrElse(0))), 40 | "movie_type" -> JsNumber(row[Int]("movie_type")), 41 | "bases_id" -> JsNumber(BigDecimal(row[Option[Int]]("bases_id").getOrElse(0))), 42 | "public_url" -> JsString(row[String]("public_url")), 43 | "twitter_account" -> JsString(row[String]("twitter_account")), 44 | "twitter_hash_tag" -> JsString(row[String]("twitter_hash_tag")), 45 | "sex" -> JsNumber(BigDecimal(row[Option[Int]]("sex").getOrElse(0))), 46 | "sequel" -> JsNumber(BigDecimal(row[Option[Int]]("sequel").getOrElse(0))), 47 | "created_at" -> JsString(row[Date]("created_at").toString), 48 | "updated_at" -> JsString(row[Date]("updated_at").toString), 49 | "city_name" -> JsString(row[Option[String]]("city_name").getOrElse("")), 50 | "city_code" -> JsNumber(BigDecimal(row[Option[Int]]("city_code").getOrElse(0))) 51 | ) 52 | }.toList 53 | 54 | Ok(Json.toJson(bases_records)) 55 | } 56 | } 57 | 58 | def yearMonth(year_num: String, month: String) = Action { 59 | DB.withConnection { implicit c => 60 | val bases_records = SQL("SELECT * FROM base_movies WHERE year = {year_num} and start_month = {month} ORDER BY start_date"). 61 | on("year_num" -> year_num, "month" -> month)().map { 62 | row => 63 | Map( 64 | "id" -> JsNumber(row[Int]("id")), 65 | "title" -> JsString(row[String]("title")), 66 | "title_short1" -> JsString(row[String]("title_short1")), 67 | "title_short2" -> JsString(row[Option[String]]("title_short2").getOrElse("")), 68 | "title_short3" -> JsString(row[Option[String]]("title_short3").getOrElse("")), 69 | "year" -> JsNumber(row[Int]("year")), 70 | "start_month" -> JsNumber(BigDecimal(row[Option[Int]]("start_month").getOrElse(0))), 71 | "start_date" -> JsString(row[Option[Date]]("start_date").getOrElse("").toString), 72 | "release_schedule_season" -> JsNumber(BigDecimal(row[Option[Int]]("release_schedule_season").getOrElse(0))), 73 | "movie_type" -> JsNumber(row[Int]("movie_type")), 74 | "bases_id" -> JsNumber(BigDecimal(row[Option[Int]]("bases_id").getOrElse(0))), 75 | "public_url" -> JsString(row[String]("public_url")), 76 | "twitter_account" -> JsString(row[String]("twitter_account")), 77 | "twitter_hash_tag" -> JsString(row[String]("twitter_hash_tag")), 78 | "sex" -> JsNumber(BigDecimal(row[Option[Int]]("sex").getOrElse(0))), 79 | "sequel" -> JsNumber(BigDecimal(row[Option[Int]]("sequel").getOrElse(0))), 80 | "created_at" -> JsString(row[Date]("created_at").toString), 81 | "updated_at" -> JsString(row[Date]("updated_at").toString), 82 | "city_name" -> JsString(row[Option[String]]("city_name").getOrElse("")), 83 | "city_code" -> JsNumber(BigDecimal(row[Option[Int]]("city_code").getOrElse(0))) 84 | ) 85 | }.toList 86 | 87 | Ok(Json.toJson(bases_records)) 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /app/controllers/AnimeV1.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import java.util.Date 4 | 5 | import anorm._ 6 | import play.api.Play.current 7 | import play.api.db._ 8 | import play.api.libs.json._ 9 | 10 | 11 | import play.api.mvc._ 12 | import play.api.Logger 13 | 14 | object AnimeV1 extends Controller { 15 | 16 | def masterList = Action { 17 | // TODO @AKB428 Ehcacheを使う 18 | DB.withConnection { implicit c => 19 | val records = SQL("SELECT * FROM cours_infos ORDER BY id")().map { 20 | row => (row[Int]("id").toString, 21 | Map("id" -> row[Int]("id"), "year" -> row[Int]("year"), "cours" -> row[Int]("cours"))) 22 | }.toMap 23 | 24 | Ok(Json.toJson(records)) 25 | } 26 | } 27 | 28 | def year(year_num: String) = Action { 29 | // TODO @AKB428 Ehcacheを使う 30 | DB.withConnection { implicit c => 31 | val cours_infos_records = SQL("SELECT * FROM cours_infos WHERE YEAR = {year_num} ORDER BY id").on("year_num" -> year_num)().map { 32 | row => row[Int]("id") 33 | }.toList 34 | Logger.debug(cours_infos_records.toString()) 35 | if (cours_infos_records.size == 0) { 36 | Logger.warn(s"Execute no data request year=$year_num") 37 | Ok(Json.toJson(cours_infos_records)) 38 | } 39 | else { 40 | 41 | val seq_cours_infos_records: Seq[Int] = cours_infos_records 42 | 43 | val bases_records = SQL("SELECT * FROM bases WHERE cours_id IN ({IDS}) ORDER BY id").on("IDS" -> seq_cours_infos_records)().map { 44 | row => 45 | Map( 46 | "id" -> JsNumber(row[Int]("id")), 47 | "title" -> JsString(row[String]("title"))) 48 | }.toList 49 | 50 | Ok(Json.toJson(bases_records)) 51 | } 52 | } 53 | } 54 | 55 | def yearCours(year_num: String, cours: String) = Action { request => 56 | 57 | val params = request.queryString.map { case (k,v) => k -> v.mkString } 58 | 59 | // TODO @AKB428 Ehcacheを使う 60 | DB.withConnection { implicit c => 61 | val cours_infos_records = SQL("SELECT * FROM cours_infos WHERE YEAR = {year_num} AND cours = {cours} "). 62 | on("year_num" -> year_num, "cours" -> cours)().map { 63 | row => row[Int]("id") 64 | }.toList 65 | Logger.debug(cours_infos_records.toString()) 66 | 67 | if (cours_infos_records.size == 0) { 68 | Logger.warn(s"Execute no data request year=$year_num cours=$cours") 69 | Ok(Json.toJson(cours_infos_records)) 70 | } 71 | else if (params.contains("ogp")){ 72 | 73 | Logger.debug("OGP") 74 | 75 | val seq_cours_infos_records: Seq[Int] = cours_infos_records 76 | 77 | val bases_records = SQL("SELECT bases.*, site_meta_data.og_title, site_meta_data.og_type, site_meta_data.og_description, site_meta_data.og_url, site_meta_data.og_image, site_meta_data.og_site_name FROM bases left join site_meta_data on bases.id = site_meta_data.bases_id WHERE COURS_ID IN ({IDS}) ORDER BY ID").on("IDS" -> seq_cours_infos_records)().map { 78 | row => 79 | Map( 80 | "id" -> JsNumber(row[Int]("id")), 81 | "title" -> JsString(row[String]("title")), 82 | "title_short1" -> JsString(row[String]("title_short1")), 83 | "title_short2" -> JsString(row[String]("title_short2")), 84 | "title_short3" -> JsString(row[String]("title_short3")), 85 | "public_url" -> JsString(row[String]("public_url")), 86 | "twitter_account" -> JsString(row[String]("twitter_account")), 87 | "twitter_hash_tag" -> JsString(row[String]("twitter_hash_tag")), 88 | "cours_id" -> JsNumber(row[Int]("cours_id")), 89 | "sex" -> JsNumber(BigDecimal(row[Option[Int]]("sex").getOrElse(0))), 90 | "sequel" -> JsNumber(BigDecimal(row[Option[Int]]("sequel").getOrElse(0))), 91 | "created_at" -> JsString(row[Date]("created_at").toString), 92 | "updated_at" -> JsString(row[Date]("updated_at").toString), 93 | "city_name" -> JsString(row[String]("city_name")), 94 | "city_code" -> JsNumber(row[Int]("city_code")), 95 | "ogp" -> Json.obj( 96 | "og_title" -> JsString(row[String]("og_title")), 97 | "og_type" -> JsString(row[String]("og_type")), 98 | "og_description" -> JsString(row[String]("og_description")), 99 | "og_url" -> JsString(row[String]("og_url")), 100 | "og_image" -> JsString(row[String]("og_image")), 101 | "og_site_name" -> JsString(row[String]("og_site_name")) 102 | ) 103 | ) 104 | }.toList 105 | 106 | Ok(Json.toJson(bases_records)) 107 | } 108 | else { 109 | 110 | val seq_cours_infos_records: Seq[Int] = cours_infos_records 111 | 112 | val bases_records = SQL("SELECT * FROM bases WHERE COURS_ID IN ({IDS}) ORDER BY ID").on("IDS" -> seq_cours_infos_records)().map { 113 | row => 114 | Map( 115 | "id" -> JsNumber(row[Int]("id")), 116 | "title" -> JsString(row[String]("title")), 117 | "title_short1" -> JsString(row[String]("title_short1")), 118 | "title_short2" -> JsString(row[String]("title_short2")), 119 | "title_short3" -> JsString(row[String]("title_short3")), 120 | "public_url" -> JsString(row[String]("public_url")), 121 | "twitter_account" -> JsString(row[String]("twitter_account")), 122 | "twitter_hash_tag" -> JsString(row[String]("twitter_hash_tag")), 123 | "cours_id" -> JsNumber(row[Int]("cours_id")), 124 | "sex" -> JsNumber(BigDecimal(row[Option[Int]]("sex").getOrElse(0))), 125 | "sequel" -> JsNumber(BigDecimal(row[Option[Int]]("sequel").getOrElse(0))), 126 | "created_at" -> JsString(row[Date]("created_at").toString), 127 | "updated_at" -> JsString(row[Date]("updated_at").toString), 128 | "city_name" -> JsString(row[String]("city_name")), 129 | "city_code" -> JsNumber(row[Int]("city_code")) 130 | ) 131 | }.toList 132 | 133 | Ok(Json.toJson(bases_records)) 134 | } 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sora 穹 2 | 3 | ShangriLa Anime API Server 4 | 5 | ## ShangriLa Anime API Server システム概要 6 | 7 | ### 説明 8 | 9 | アニメ作品の情報を返すREST形式のAPIサーバーです。 10 | 11 | こちらは2015年〜2020年までのAnimeAPIリファレンス実装とAPIドキュメントです。 12 | 2021年以降は実装はGolang版[anime_api_golang](https://github.com/Project-ShangriLa/anime_api_golang)に移行されています。(クライアントから呼び出す際のAPIの基本仕様の変更はありません) 13 | 14 | 15 | ### サーバーシステム要件 16 | 17 | * Java7+ 18 | * フレームワーク: Play Framework 2.3+ 19 | * 言語: Scala 2.11+ 20 | 21 | ### インストール 22 | 23 | * MySQL or MariaDB インストール 24 | * anime_admin_development データベース作成 25 | * DDL登録 [ShanriLa DDL](https://github.com/Project-ShangriLa/shangrila/tree/master/DDL) 26 | * マスターテーブルのインサート 27 | * このディレクトリでactivatorを起動しPlayフレームワークをインストール 28 | 29 | ### 起動方法 (リリース) 30 | 31 | ``` 32 | shell> activator 33 | activator shell> start 80 34 | ``` 35 | 36 | ### 起動方法 (デバッグ) 37 | 38 | ``` 39 | shell> activator run 40 | ``` 41 | 42 | ### ライセンス 43 | 44 | Apache 2 license 45 | 46 | ## V1 API リファレンス 47 | 48 | ### エンドポイント 49 | 50 | http://api.moemoe.tokyo/anime/v1 51 | 52 | ### 認証 53 | 54 | V1では認証を行いません。 55 | 56 | 57 | ### レートリミット 58 | 59 | なし 60 | 61 | ### GET /anime/v1/master/cours 62 | 63 | ShangriLa API Serverが持っているアニメ情報のクールごとの情報のリストを返却します。 64 | 65 | #### Request Body 66 | 67 | なし 68 | 69 | #### Response Body 70 | 71 | | Property | Value |description|Sample| 72 | | :------------ | :------------------ |:------|:-------| 73 | | cours_idの値 | Cours Object |cours_idはシステムで割り振ったクールごとのユニークなID(coursマスターのID)|1| 74 | 75 | ##### Cours Object 76 | 77 | | Property | Value |description|Sample| 78 | | :------------ | :------------------ |:------|:-------| 79 | | id | Number |cours_id|6| 80 | | year| Number |該当する西暦(YYYY)|2015| 81 | | cours| Number |yearの中での順番[1〜4]|2| 82 | 83 | 84 | レスポンス例 85 | 86 | ``` 87 | $curl http://api.moemoe.tokyo/anime/v1/master/cours | jq . 88 | 89 | { 90 | "4": { 91 | "id": 4, 92 | "year": 2014, 93 | "cours": 4 94 | }, 95 | "5": { 96 | "id": 5, 97 | "year": 2015, 98 | "cours": 1 99 | }, 100 | "6": { 101 | "id": 6, 102 | "year": 2015, 103 | "cours": 2 104 | } 105 | } 106 | ``` 107 | 108 | 109 | ### GET /aime/v1/master/:year 110 | 111 | :yearで指定されたYYYY年のアニメ1クールから4クールまでの情報をすべて返却します 112 | 113 | #### Request Body 114 | 115 | なし 116 | 117 | #### Response Body 118 | 119 | | Property | Value |description|Sample| 120 | | :------------ | :------------------ |:--------|:-------| 121 | | Array |Base Object|データがない場合は空の配列| 122 | 123 | ##### Base Object 124 | 125 | | Property | Value |description|Sample| 126 | | :------------ | :------------------ |:--------|:-------| 127 | | id |Number|APIで管理するアニメ作品に割り当てられているユニークなID|125| 128 | | title |String|アニメ作品名|"冴えない彼女の育てかた"| 129 | 130 | 131 | レスポンス例 132 | 133 | ``` 134 | curl http://api.moemoe.tokyo/anime/v1/master/2015 | jq . 135 | [ 136 | { 137 | "id": 124, 138 | "title": "幸腹グラフィティ" 139 | }, 140 | { 141 | "id": 125, 142 | "title": "銃皇無尽のファフニール" 143 | }, 144 | { 145 | "id": 126, 146 | "title": "冴えない彼女の育てかた" 147 | }, 148 | { 149 | "id": 127, 150 | "title": "暗殺教室" 151 | }, 152 | { 153 | "id": 129, 154 | "title": "探偵歌劇ミルキィホームズTD" 155 | } 156 | ] 157 | ``` 158 | 159 | 160 | ### GET /anime/v1/master/:year/:n 161 | 162 | :yearで指定されたYYYY年アニメの:nで指定されたクールの情報をすべて返します。 163 | 164 | * /2015/1 だったら2015年1期(冬期) のアニメ作品情報を全て返却します。 165 | * /2015/2 だったら2015年2期(春期) のアニメ作品情報を全て返却します。 166 | 167 | #### Request Body 168 | 169 | なし 170 | 171 | #### Response Body 172 | 173 | 174 | | Property | Value |description|Sample| 175 | | :------------ | :------------------ |:--------|:-------| 176 | | Array |Base Object|データがない場合は空の配列|| 177 | 178 | ##### Base Object 179 | 180 | requiredに◯がないものは値なし(=データメンテナンスしていない)の場合があります。 181 | 182 | またプロパティは追加される可能性があります。 183 | 184 | | Property |Value |Required|description|Sample| 185 | | :------------|:-----|:-------|:----------|:-----| 186 | | id |Number|◯|APIで管理するアニメ作品に割り当てられているユニークなID|125| 187 | | title |String|◯|アニメ作品名|"冴えない彼女の育てかた"| 188 | | title_short1 |String|-|アニメ作品名の略称1|"冴えカノ"| 189 | | title_short2 |String|-|アニメ作品名の略称2|| 190 | | title_short3 |String|-|アニメ作品名の略称3|| 191 | | public_url |String|◯|アニメ作品の公式URL|"http://www.saenai.tv/"| 192 | | twitter_account|String|◯|ツイッターアカウント|"saenai_heroine"| 193 | | twitter_hash_tag|String|◯|ツイッターハッシュタグ|"saekano"| 194 | | cours_id |Number|◯|coursマスターのID|5| 195 | | created_at |String|◯|データの作成日時|"2015-01-08 09:37:01.0"| 196 | | updated_at |String|◯|データの更新日時|"2015-01-08 09:37:01.0"| 197 | | sex |Number|-|男性向け=0, 女性向け=1|0| 198 | | sequel |Number|-|続編モノの場合は1以上の数値が入る|0| 199 | | city_code |Number|-|代表聖地地区の5桁の市区町村コード(RESAS APIなど地方自治体のオープンデータと連携する時に使用)|22203| 200 | | city_name |String|-|代表聖地地区の市区町村名|静岡県静岡市| 201 | 202 | レスポンス例 203 | 204 | ``` 205 | curl http://api.moemoe.tokyo/anime/v1/master/2016/4 | jq . 206 | [ 207 | { 208 | "title_short2": "", 209 | "twitter_account": "3lion_anime", 210 | "public_url": "http://www.nhk.or.jp/anime/3lion/", 211 | "title_short1": "3月のライオン", 212 | "sex": 0, 213 | "twitter_hash_tag": "3月のライオン", 214 | "id": 465, 215 | "sequel": 0, 216 | "created_at": "2016-09-19 19:24:09.0", 217 | "city_name": "東京都中央区佃", 218 | "cours_id": 12, 219 | "title": "3月のライオン", 220 | "city_code": 13102, 221 | "title_short3": "", 222 | "updated_at": "2016-09-19 19:24:09.0" 223 | }, 224 | { 225 | "title_short2": "Occultic;Nine", 226 | "twitter_account": "occultic_nine", 227 | "public_url": "http://www.occultic-nine.com/", 228 | "title_short1": "オカルティック・ナイン", 229 | "sex": 0, 230 | "twitter_hash_tag": "オカン", 231 | "id": 466, 232 | "sequel": 0, 233 | "created_at": "2016-09-19 19:24:09.0", 234 | "city_name": "東京都武蔵野市吉祥寺", 235 | "cours_id": 12, 236 | "title": "Occultic;Nine-オカルティック・ナイン-", 237 | "city_code": 13203, 238 | "title_short3": "オカン", 239 | "updated_at": "2016-09-19 19:24:09.0" 240 | }, 241 | ] 242 | ``` 243 | 244 | ## V1 Internal API 245 | 246 | ### 非公開用API (更新系/サーバー制御)用 247 | 248 | #### (TODO) PUT /v1/internal/master/ 249 | 250 | データを更新する 251 | 252 | #### (TODO) PUT /v1/internal/chache/ehcahe/refresh 253 | 254 | Ehcacheを強制リフレッシュする 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | require_arg () { 161 | local type="$1" 162 | local opt="$2" 163 | local arg="$3" 164 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 165 | die "$opt requires <$type> argument" 166 | fi 167 | } 168 | is_function_defined() { 169 | declare -f "$1" > /dev/null 170 | } 171 | 172 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 173 | detect_terminal_for_ui() { 174 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 175 | addResidual "ui" 176 | } 177 | # SPECIAL TEST FOR MAC 178 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 179 | echo "Detected MAC OSX launched script...." 180 | echo "Swapping to UI" 181 | addResidual "ui" 182 | } 183 | } 184 | 185 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 186 | process_args () { 187 | while [[ $# -gt 0 ]]; do 188 | case "$1" in 189 | -h|-help) usage; exit 1 ;; 190 | -v|-verbose) verbose=1 && shift ;; 191 | -d|-debug) debug=1 && shift ;; 192 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 193 | -jvm-debug) 194 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 195 | addDebugger "$2" && shift 196 | else 197 | addDebugger 9999 198 | fi 199 | shift ;; 200 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 201 | -D*) addJava "$1" && shift ;; 202 | -J*) addJava "${1:2}" && shift ;; 203 | *) addResidual "$1" && shift ;; 204 | esac 205 | done 206 | 207 | is_function_defined process_my_args && { 208 | myargs=("${residual_args[@]}") 209 | residual_args=() 210 | process_my_args "${myargs[@]}" 211 | } 212 | } 213 | 214 | # Actually runs the script. 215 | run() { 216 | # TODO - check for sane environment 217 | 218 | # process the combined args, then reset "$@" to the residuals 219 | process_args "$@" 220 | detect_terminal_for_ui 221 | set -- "${residual_args[@]}" 222 | argumentCount=$# 223 | 224 | #check for jline terminal fixes on cygwin 225 | if is_cygwin; then 226 | stty -icanon min 1 -echo > /dev/null 2>&1 227 | addJava "-Djline.terminal=jline.UnixTerminal" 228 | addJava "-Dsbt.cygwin=true" 229 | fi 230 | 231 | # run sbt 232 | execRunner "$java_cmd" \ 233 | "-Dactivator.home=$(make_url "$activator_home")" \ 234 | $(get_mem_opts $app_mem) \ 235 | ${java_opts[@]} \ 236 | ${java_args[@]} \ 237 | -jar "$app_launcher" \ 238 | "${app_commands[@]}" \ 239 | "${residual_args[@]}" 240 | 241 | local exit_code=$? 242 | if is_cygwin; then 243 | stty icanon echo > /dev/null 2>&1 244 | fi 245 | exit $exit_code 246 | } 247 | 248 | # Loads a configuration file full of default command line options for this script. 249 | loadConfigFile() { 250 | cat "$1" | sed '/^\#/d' 251 | } 252 | 253 | ### ------------------------------- ### 254 | ### Start of customized settings ### 255 | ### ------------------------------- ### 256 | usage() { 257 | cat < [options] 259 | 260 | Command: 261 | ui Start the Activator UI 262 | new [name] [template-id] Create a new project with [name] using template [template-id] 263 | list-templates Print all available template names 264 | -h | -help Print this message 265 | 266 | Options: 267 | -v | -verbose Make this runner chattier 268 | -d | -debug Set sbt log level to debug 269 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 270 | -jvm-debug Turn on JVM debugging, open at the given port. 271 | 272 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 273 | -java-home Alternate JAVA_HOME 274 | 275 | # jvm options and output control 276 | -Dkey=val Pass -Dkey=val directly to the java runtime 277 | -J-X Pass option -X directly to the java runtime 278 | (-J is stripped) 279 | 280 | # environment variables (read from context) 281 | JAVA_OPTS Environment variable, if unset uses "" 282 | SBT_OPTS Environment variable, if unset uses "" 283 | ACTIVATOR_OPTS Environment variable, if unset uses "" 284 | 285 | In the case of duplicated or conflicting options, the order above 286 | shows precedence: environment variables lowest, command line options highest. 287 | EOM 288 | } 289 | 290 | ### ------------------------------- ### 291 | ### Main script ### 292 | ### ------------------------------- ### 293 | 294 | declare -a residual_args 295 | declare -a java_args 296 | declare -a app_commands 297 | declare -r real_script_path="$(realpath "$0")" 298 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 299 | declare -r app_version="1.2.10" 300 | 301 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 302 | declare -r script_name=activator 303 | declare -r java_cmd=$(get_java_cmd) 304 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 305 | userhome="$HOME" 306 | if is_cygwin; then 307 | # cygwin sets home to something f-d up, set to real windows homedir 308 | userhome="$USERPROFILE" 309 | fi 310 | declare -r activator_user_home_dir="${userhome}/.activator" 311 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 312 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 313 | 314 | # Now check to see if it's a good enough version 315 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 316 | if [[ "$java_version" == "" ]]; then 317 | echo 318 | echo No java installations was detected. 319 | echo Please go to http://www.java.com/getjava/ and download 320 | echo 321 | exit 1 322 | elif [[ ! "$java_version" > "1.6" ]]; then 323 | echo 324 | echo The java installation you have is not up to date 325 | echo Activator requires at least version 1.6+, you have 326 | echo version $java_version 327 | echo 328 | echo Please go to http://www.java.com/getjava/ and download 329 | echo a valid Java Runtime and install before running Activator. 330 | echo 331 | exit 1 332 | fi 333 | 334 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 335 | # a "versioned" config trumps one on the top level 336 | if [[ -f "$java_opts_config_version" ]]; then 337 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 338 | elif [[ -f "$java_opts_config_home" ]]; then 339 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 340 | fi 341 | 342 | run "$@" 343 | --------------------------------------------------------------------------------