├── 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 |
--------------------------------------------------------------------------------