'use strict';
/**
* @fileoverview search-nico-js aims to provide a complete, asynchronous client library for the api.search.nicovideo.jp.
* For API details and how to use promises, see the JavaScript Promises articles.
* @author shoito
*/
if (typeof window === 'undefined') {
var Promise = Promise || require('promise');
var XMLHttpRequest = XMLHttpRequest || require('xmlhttprequest').XMLHttpRequest;
}
(function() {
function httpStatus(apiStatus) {
var statusMap = {
200: {'status': 200, 'text': 'OK'},
300: {'status': 400, 'text': 'Bad Request'},
101: {'status': 503, 'text': 'Service Unavailable'},
1001: {'status': 504, 'text': 'Gateway Timeout'}
};
return statusMap[apiStatus] || {'status': 500, 'text': 'Internal Server Error'};
}
var ApiRequest = (function() {
var ApiRequest = {};
ApiRequest.BASE_URL = 'http://api.search.nicovideo.jp';
ApiRequest.post = function(url, params, parseFunc) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status === 200) {
var response = parseFunc(xhr.responseText);
if (response.status === 200) {
resolve(response);
} else {
reject({'status': response.status, 'error': response.statusText, 'error_description': 'An error has occurred while requesting api'});
}
} else {
reject({'status': xhr.status, 'error': xhr.statusText, 'error_description': 'An error has occurred while requesting api'});
}
};
xhr.onerror = reject;
xhr.open('POST', ApiRequest.BASE_URL + url);
xhr.timeout = params.timeout;
xhr.send(JSON.stringify(params));
});
};
return ApiRequest;
})();
var ContentsSearchRequest = (function() {
var query = {
'query':'keyword',
'service':['video'],
'search':['title'],
'join':['cmsid'],
'filters':[],
'from':0,
'size':10,
'sort_by':'view_counter'
};
/**
* コンテンツ検索リクエストのコンストラクタ
* @class ContentsSearchRequest
* @classdesc コンテンツ検索リクエスト
* @memberOf SearchNico
*/
function ContentsSearchRequest(options) {
options = options || {};
if (typeof options.issuer === 'undefined' ||
typeof options.reason === 'undefined') {
throw new Error('issuer and reason parameters is required.');
}
query.timeout = options.timeout || 3000;
query.issuer = options.issuer;
query.reason = options.reason;
}
function parse(rawResponse) {
var response = {};
response.status = httpStatus(200).status;
rawResponse.split('\n').forEach(function(rawChunk) {
if (rawChunk === '') {
return;
}
var chunk = JSON.parse(rawChunk);
if (chunk.type === 'stats' && chunk.values) {
response.hits = chunk.values[0].total;
} else if (chunk.type === 'hits' && chunk.values) {
response.values = chunk.values.map(function(content) {
delete content._rowid;
return content;
});
} else if (chunk.errid) {
var http = httpStatus(chunk.errid);
response.status = http.status;
response.statusText = http.text;
}
});
if (typeof response.hits === 'undefined') {
response.hits = 0;
response.values = [];
}
return response;
}
/**
* equalフィルタオブジェクトを生成する
* @memberof SearchNico.ContentsSearchRequest
* @method equalFilter
* @instance
* @param {String} field - フィルタ対象フィールド
* @param {String/Number} value - フィルタ対象の値
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.equalFilter = function(field, value) {
return {'type': 'equal', 'field': field, 'value': value};
};
/**
* rangeフィルタオブジェクトを生成する
* @memberof SearchNico.ContentsSearchRequest
* @method rangeFilter
* @instance
* @param {String} field - フィルタ対象フィールド
* @param {String/Number} from - 開始値
* @param {String/Number} to - 終了値
* @param {Boolean} includeUpper - 上限値を含むかどうか
* @param {Boolean} includeLower - 下限値を含むかどうか
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.rangeFilter = function(field, from, to, includeUpper, includeLower) {
var filter = {'type': 'range', 'field': field, 'include_upper': true, 'include_lower': true};
if (typeof from !== 'undefined') {
filter.from = from;
}
if (typeof to !== 'undefined') {
filter.to = to;
}
if (typeof includeUpper !== 'undefined') {
filter.include_upper = includeUpper;
}
if (typeof includeLower !== 'undefined') {
filter.include_lower = includeLower;
}
return filter;
};
/**
* 検索対象サービスを設定する
* @memberof SearchNico.ContentsSearchRequest
* @method service
* @instance
* @param {String} name - 検索対象サービス
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.service = function(name) {
query.service = [name];
return this;
};
/**
* 検索キーワードを設定する
* @memberof SearchNico.ContentsSearchRequest
* @method keyword
* @instance
* @param {String} keyword - 検索キーワード
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.keyword = function(keyword) {
query.query = keyword;
return this;
};
/**
* 検索対象となるフィールドを設定する
* @memberof SearchNico.ContentsSearchRequest
* @method target
* @instance
* @param {Array} names - 検索対象フィールドリスト
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.target = function(names) {
query.search = names;
return this;
};
/**
* フィルタ条件を設定する
* @memberof SearchNico.ContentsSearchRequest
* @method filter
* @instance
* @param {Array} filters - フィルタ条件リスト
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.filter = function(filters) {
query.filters = filters;
return this;
};
/**
* ソート条件を設定する
* @memberof SearchNico.ContentsSearchRequest
* @method sort
* @instance
* @param {String} name - ソート条件となるフィールド
* @param {String} [order=desc] - asc/desc (昇順/降順)
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.sort = function(name, order) {
query.sort_by = name;
query.order = order || 'desc';
return this;
};
/**
* 検索結果に含めるフィールドを設定する
* @memberof SearchNico.ContentsSearchRequest
* @method select
* @instance
* @param {Array} names - 取得対象のフィールドリスト
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.select = function(names) {
query.join = names;
return this;
};
/**
* 検索結果の取得開始位置を設定する
* @memberof SearchNico.ContentsSearchRequest
* @method from
* @instance
* @param {Number} from - 検索結果の取得開始位置
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.from = function(from) {
query.from = from;
return this;
};
/**
* 検索結果の取得件数を設定する
* @memberof SearchNico.ContentsSearchRequest
* @method size
* @instance
* @param {Number} size - コンテンツ検索結果の取得件数
* @return {ContentsSearchRequest} コンテンツ検索リクエストインスタンス
*/
ContentsSearchRequest.prototype.size = function(size) {
query.size = size;
return this;
};
/**
* 検索リクエストのプロミスオブジェクトを取得する
* @memberof SearchNico.ContentsSearchRequest
* @method fetch
* @instance
* @return {Promise} プロミスオブジェクト - resolve/reject関数がプロミスの成功または失敗に応じて呼ばれる
*/
ContentsSearchRequest.prototype.fetch = function() {
return ApiRequest.post('/api/', query, parse);
};
return ContentsSearchRequest;
})();
var TagsSearchRequest = (function() {
var query = {
'query':'keyword',
'service':['video'],
'from':0,
'size':10
};
/**
* 関連タグ検索リクエストのコンストラクタ
* @class TagsSearchRequest
* @classdesc 関連タグ検索リクエスト
* @memberof SearchNico
*/
function TagsSearchRequest(options) {
options = options || {};
if (typeof options.issuer === 'undefined' ||
typeof options.reason === 'undefined') {
throw new Error('issuer and reason parameters is required.');
}
query.timeout = options.timeout || 3000;
query.issuer = options.issuer;
query.reason = options.reason;
}
function parse(rawResponse) {
var response = {};
response.status = httpStatus(200).status;
rawResponse.split('\n').forEach(function(rawChunk) {
if (rawChunk === '') {
return;
}
var chunk = JSON.parse(rawChunk);
if (chunk.type === 'tags' && chunk.values) {
response.values = chunk.values.map(function(value) {
return value.tag;
});
} else if (chunk.errid) {
var http = httpStatus(chunk.errid);
response.status = http.status;
response.statusText = http.text;
}
});
if (typeof response.values === 'undefined') {
response.values = [];
}
return response;
}
/**
* 検索対象サービスを設定する
* @memberof SearchNico.TagsSearchRequest
* @method service
* @instance
* @param {String} name - 関連タグの検索対象サービス
* @return {TagsSearchRequest} 関連タグ検索リクエストインスタンス
*/
TagsSearchRequest.prototype.service = function(name) {
query.service = ['tag_' + name];
return this;
};
/**
* 検索キーワードを設定する
* @memberof SearchNico.TagsSearchRequest
* @method keyword
* @instance
* @param {String} keyword - 関連タグの検索キーワード
* @return {TagsSearchRequest} 関連タグ検索リクエストインスタンス
*/
TagsSearchRequest.prototype.keyword = function(keyword) {
query.query = keyword;
return this;
};
/**
* 検索結果の取得開始位置を設定する
* @memberof SearchNico.TagsSearchRequest
* @method from
* @instance
* @param {Number} from - 検索結果の取得開始位置
* @return {TagsSearchRequest} 関連タグ検索リクエストインスタンス
*/
TagsSearchRequest.prototype.from = function(from) {
query.from = from;
return this;
};
/**
* 検索結果の取得件数を設定する
* @memberof SearchNico.TagsSearchRequest
* @method size
* @instance
* @param {Number} size - 関連タグの検索結果の取得件数
* @return {TagsSearchRequest} 関連タグ検索リクエストインスタンス
*/
TagsSearchRequest.prototype.size = function(size) {
query.size = size;
return this;
};
/**
* 検索リクエストのプロミスオブジェクトを取得する
* @memberof SearchNico.TagsSearchRequest
* @method fetch
* @instance
* @return {Promise} プロミスオブジェクト - resolve/reject関数がプロミスの成功または失敗に応じて呼ばれる
*/
TagsSearchRequest.prototype.fetch = function() {
return ApiRequest.post('/api/tag/', query, parse);
};
return TagsSearchRequest;
})();
/**
* niconico検索APIクライアント
* @namespace
* @name SearchNico
*/
var SearchNico = {};
/**
* コンテンツ検索リクエストのインスタンスを取得する
* @memberof SearchNico
* @method
* @param {Object} [options] - APIパラメーター
* @param {String} [options.issuer] - サービス/アプリケーション名
* @param {String} [options.reason=html5jc] - コンテスト/イベント名
* @param {Number} [options.tiemout=3000] - timeout(ms)
* @see {@link http://search.nicovideo.jp/docs/api/html5jc.html}
*/
SearchNico.contents = function(options) {
return new ContentsSearchRequest(options);
};
/**
* 関連タグ検索リクエストのインスタンスを取得する
* @memberof SearchNico
* @method
* @param {Object} [options] - APIパラメーター
* @param {String} [options.issuer] - サービス/アプリケーション名
* @param {String} [options.reason=html5jc] - コンテスト/イベント名
* @param {Number} [options.tiemout=3000] - timeout(ms)
* @see {@link http://search.nicovideo.jp/docs/api/html5jc.html}
*/
SearchNico.tags = function(options) {
return new TagsSearchRequest(options);
};
if (typeof module !== 'undefined') {
module.exports = SearchNico;
} else {
// <script src='https://www.promisejs.org/polyfills/promise-4.0.0.js'></script>
this.SearchNico = SearchNico;
}
}).call(this);