File: src/mylist/MyList.coffee
###*
# Properties
# attrメソッドを介して取得します。(とりあえずマイリストの場合、属性は一切設定されません。)
# Example: mylist.attr("id") // -> マイリストIDを取得
# - id : number -- マイリストID
# - name : string -- リスト名
# - description : string -- マイリストの説明
# - public : boolean -- 公開マイリストかどうか
# - iconId : number -- マイリストのアイコンID
# - defaultSort : number -- 標準のソート方法(?)
# - sortOrder : number -- ソート方式(?)
# - userId : number -- ユーザー番号
# - createTime : Date -- マイリストの作成日
# - updateTime : Date -- マイリストの更新日
###
_ = require "lodash"
{Emitter} = require "event-kit"
Request = require "request-promise"
{sprintf} = require("sprintf")
QueryString = require "querystring"
NicoUrl = require "../NicoURL"
NicoException = require "../NicoException"
MyListItem = require "./MyListItem"
module.exports =
class MyList extends Emitter
@_cache : {}
@_attr :
id : -1
name : null
description : null
public : null
iconId : -1
defaultSort : -1
sortOrder : -1
userId : -1
createTime : null
updateTime : null
###*
# @param {MyListMeta} myListMeta
# @param {NicoSession} session
# @return Promise
###
@instanceById : (myListMeta, session) ->
id = myListMeta.id
list = new MyList(myListMeta, session)
return Promise.resolve(MyList._cache[id]) if MyList._cache[id]?
list.fetch().then ->
Promise.resolve list
###*
# マイリストが最新の内容に更新された時に発火します
# @event MyList#did-refresh
# @property {MyList} list
###
###*
# マイリストから項目が削除された時に発火します
# @event MyList#did-delete-item
# @property {MyList} list
# @property {MyListItem} item
###
###*
# @private
# @property {NicoSession} _session セッション
###
_session : null
###*
# @private
# @property {Object} _urlSet MyList APIのurl
###
_urlSet : null
###*
# @private
# @property {Object} _attr マイリスト情報
###
_attr : null
###*
# @property {Array.<MyListItem>} items 登録されている動画のリスト
###
items : null
###
# @param {MyListMeta} metaInfo 操作対象の MyListMetaのインスタンス。
# @param {NicoSession} session セッション
###
constructor : (metaInfo, @_session) ->
@_attr = metaInfo.toJSON()
@items = []
Object.defineProperties @,
id :
get : -> metaInfo.get("id")
_urlSet :
value : if metaInfo.get("id") is "home" then NicoUrl.MyList.DefList else NicoUrl.MyList.Normal
###*
# このマイリストが"とりあえずマイリスト"か調べます。
# @return {boolean} とりあえずマイリストならtrueを返します。
###
isDefaultList : ->
return @id is "home"
###*
# マイリストに登録されている動画を取得します。
#
# @fires MyList#refreshed
# @return {Promise}
###
fetch : (options) ->
Request.get
resolveWithFullResponse : true
url : sprintf(@_urlSet.LIST, @id)
jar : @_session.cookie
.catch (e) ->
Promise.reject new NicoException
message : "Failed to fetch items (Connection error: #{e.message})"
previous : e
.then (res) =>
try
json = JSON.parse res.body
catch e
return Promise.reject new NicoException
message : "Failed to parse response"
response : res.body
previous : e
return if json.status isnt "ok"
Promise.reject new NicoException
message : "Failed to fetch contents (unknown)"
response : res.body
@items = []
_.each json.mylistitem.reverse(), (item) =>
m = MyListItem.fromApiResponse(item, @)
@items.push m
@emit "did-refresh", {list: @}
return
###*
# マイリストのメタ情報を取得します。
# @param {string} attr 取得する属性名
###
attr : (attr) ->
return @_attr[attr]
###*
# @private
# @param {MyListItem|Array.<MyListItem>} items
###
_pickHavingItemIds : (items) ->
items = [items] unless Array.isArray(items)
validItems = _.select items, (item) -> item instanceof MyListItem
havingItems = _.select items, "list", @
havingItemIds = _.pluck havingItems, 'id'
###*
# マイリストに動画を追加します。
# @param {NicoVideoInfo|string} movie 追加する動画のNicoVideoInfoオブジェクトか動画ID
# @param {string?} desc マイリストの動画メモの内容
# @return {Promise}
###
addMovie : (movie, desc = "") ->
self = this
dfd = Promise.defer()
id = null
# movieが文字列じゃない上に、オブジェクトじゃないとか、idプロパティがない場合
if not typeof movie isnt "string" and not movie.id?
return Promise.reject new TypeError("Invalid type for argument 1(movie)")
else
id = if _.isString(movie) then movie else movie.id
req =
item_type : 0
item_id : id
token : null
description : desc
group_id : @id
@isDefaultList() and (delete req.group_id)
#-- APIと通信
# アクセストークンを取得
@_session.mylist.fetchToken()
.then (token) =>
req.token = token
Request.post
resolveWithFullResponse : true
url : @_urlSet.ADD
jar : @_session.cookie
form : req
.then (res) =>
try
result = JSON.parse res.body
catch e
return Promise.reject "Mylist[%s]: Failed to add item (JSON parse error)"
return if result.status isnt "ok"
Promise.reject new NicoException
message : result.error.description
response : result
Promise.resolve {response: result}
###*
# マイリストから項目を削除します。
#
# 渡されたアイテム内のこのリストの項目でないものは無視されます。
#
# @param {MyListItem|Array.<MyListItem>} items 削除する項目の配列
# @return {Promise} 成功した時に削除された項目数でresolveします。
###
deleteItem : (items) ->
itemIds = @_pickHavingItemIds(items)
return Promise.resolve({response: null}) if itemIds.length is 0
@_session.mylist.fetchToken()
.then (token) =>
req =
group_id : @id
"id_list[0]" : itemIds
token : token
delete req.group_id if @isDefaultList()
Request.post
resolveWithFullResponse : true
url : @_urlSet.DELETE
jar : @_session.cookie
form : req
.then (res) ->
try
result = JSON.parse res.body
catch e
return Promise.reject new Error("Mylist[%s]: Failed to delete item (JSON parse error)")
if result.status is "ok"
Promise.resolve {response: json}
else
e = new Error(sprintf("MyList[%s]: Failed to delete item (reason: %s)", @id, result.error.description))
e.response = json
Promise.reject e
###*
# マイリストから別のマイリストへ項目を移動します。
#
# 渡された項目内のこのリストの項目でないものは無視されます。
#
# @param {MyListItem|Array.<MyListItem>} items 移動する項目の配列
# @param {MyList} targetMyList 移動先のマイリスト
# @return {Promise}
###
moveItem : (items, targetMyList) ->
if targetMyList not instanceof MyList
throw new TypeError("targetMyList must be instance of MyList")
itemIds = @_pickHavingItemIds(items)
return Promise.resolve({response: null}) if itemIds.length is 0
@_session.mylist.fetchToken()
.then (token) =>
req =
group_id : @id
target_group_id : targetMyList.id
"id_list[0]" : itemIds
token : token
delete req.group_id if @isDefaultList()
Request.post
resolveWithFullResponse : true
url : @_urlSet.MOVE
jar : @_session.cookie
form : req
.then (res) ->
try
result = JSON.parse res.body
catch e
return Promise.reject "Mylist[%s]: Failed to delete item (JSON parse error)"
if result.status is "ok"
Promise.resolve {response: json}
else
e = new Error(sprintf("MyList[%s]: Failed to delete item (reason: %s)", @id, result.error.description))
e.response = result
Promise.reject e
#
# Event Handlers
#
onDidRefresh : (listener) ->
@on "did-refresh", listener
onDidDeleteItem : (listener) ->
@on "did-delete-item", listener