| 1 | #!/usr/bin/env python |
|---|
| 2 | |
|---|
| 3 | # Deejayd, a media player daemon |
|---|
| 4 | # Copyright (C) 2007-2009 Mickael Royer <mickael.royer@gmail.com> |
|---|
| 5 | # Alexandre Rossi <alexandre.rossi@gmail.com> |
|---|
| 6 | # |
|---|
| 7 | # This program is free software; you can redistribute it and/or modify |
|---|
| 8 | # it under the terms of the GNU General Public License as published by |
|---|
| 9 | # the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | # (at your option) any later version. |
|---|
| 11 | # |
|---|
| 12 | # This program is distributed in the hope that it will be useful, |
|---|
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | # GNU General Public License for more details. |
|---|
| 16 | # |
|---|
| 17 | # You should have received a copy of the GNU General Public License along |
|---|
| 18 | # with this program; if not, write to the Free Software Foundation, Inc., |
|---|
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|---|
| 20 | |
|---|
| 21 | """ |
|---|
| 22 | Use to create documentation of the protocol |
|---|
| 23 | """ |
|---|
| 24 | |
|---|
| 25 | # init translation |
|---|
| 26 | import gettext |
|---|
| 27 | from deejayd.ui.i18n import DeejaydTranslations |
|---|
| 28 | try: t = gettext.translation("deejayd", class_=DeejaydTranslations) |
|---|
| 29 | except IOError: |
|---|
| 30 | t = DeejaydTranslations() |
|---|
| 31 | t.install() |
|---|
| 32 | |
|---|
| 33 | from twisted.python import reflect |
|---|
| 34 | from deejayd.mediafilters import * |
|---|
| 35 | from deejayd.interfaces import DeejaydSignal |
|---|
| 36 | from deejayd.rpc import protocol |
|---|
| 37 | from deejayd.rpc.jsonbuilders import JSONRPCResponse, JSONRPCRequest,\ |
|---|
| 38 | Get_json_filter, DeejaydJSONSignal |
|---|
| 39 | |
|---|
| 40 | common_request = [ |
|---|
| 41 | {"prefix": "", "desc": "General Commands",\ |
|---|
| 42 | "object": protocol._DeejaydMainJSONRPC}, |
|---|
| 43 | {"prefix": "player.", "desc": "Player Commands",\ |
|---|
| 44 | "object": protocol.DeejaydPlayerJSONRPC}, |
|---|
| 45 | {"prefix": "audiolib.", "desc": "Audio Library Commands",\ |
|---|
| 46 | "object": protocol.DeejaydAudioLibraryJSONRPC}, |
|---|
| 47 | {"prefix": "videolib.", "desc": "Video Library Commands",\ |
|---|
| 48 | "object": protocol.DeejaydVideoLibraryJSONRPC}, |
|---|
| 49 | {"prefix": "playlist.", "desc": "Playlist Mode Commands",\ |
|---|
| 50 | "object": protocol.DeejaydPlaylistModeJSONRPC}, |
|---|
| 51 | {"prefix": "panel.", "desc": "Panel Mode Commands",\ |
|---|
| 52 | "object": protocol.DeejaydPanelModeJSONRPC}, |
|---|
| 53 | {"prefix": "video.", "desc": "Video Mode Commands",\ |
|---|
| 54 | "object": protocol.DeejaydVideoModeJSONRPC}, |
|---|
| 55 | {"prefix": "webradio.", "desc": "Webradio Mode Commands",\ |
|---|
| 56 | "object": protocol.DeejaydWebradioModeJSONRPC}, |
|---|
| 57 | {"prefix": "dvd.", "desc": "Dvd Mode Commands",\ |
|---|
| 58 | "object": protocol.DeejaydDvdModeJSONRPC}, |
|---|
| 59 | {"prefix": "queue.", "desc": "Queue Commands",\ |
|---|
| 60 | "object": protocol.DeejaydQueueJSONRPC}, |
|---|
| 61 | {"prefix": "queue.", "desc": "Queue Commands",\ |
|---|
| 62 | "object": protocol.DeejaydQueueJSONRPC}, |
|---|
| 63 | {"prefix": "recpls.", "desc": "Recorded Playlist Commands",\ |
|---|
| 64 | "object": protocol.DeejaydRecordedPlaylistJSONRPC}, |
|---|
| 65 | ] |
|---|
| 66 | |
|---|
| 67 | class WikiFormat: |
|---|
| 68 | |
|---|
| 69 | def commandDoc(self): |
|---|
| 70 | return """ |
|---|
| 71 | As written in specification, request is like that : |
|---|
| 72 | {{{ |
|---|
| 73 | `%(request)s` |
|---|
| 74 | }}} |
|---|
| 75 | |
|---|
| 76 | """ % { |
|---|
| 77 | "request": JSONRPCRequest("method_name",\ |
|---|
| 78 | ["params1", "params2"], id="id").to_pretty_json(),\ |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | def answerDoc(self): |
|---|
| 82 | return """ |
|---|
| 83 | As written in specification, response is like that : |
|---|
| 84 | {{{ |
|---|
| 85 | %s |
|---|
| 86 | }}} |
|---|
| 87 | |
|---|
| 88 | For deejayd, result parameter has always the same syntax : |
|---|
| 89 | {{{ |
|---|
| 90 | `{ |
|---|
| 91 | "type": answer_type, |
|---|
| 92 | "answer": the real answer value |
|---|
| 93 | }` |
|---|
| 94 | }}} |
|---|
| 95 | With response types equals to: |
|---|
| 96 | * ack |
|---|
| 97 | * list |
|---|
| 98 | * dict |
|---|
| 99 | * mediaList |
|---|
| 100 | * dvdInfo |
|---|
| 101 | * fileAndDirList |
|---|
| 102 | """ % JSONRPCResponse("deejayd_response", "id").to_pretty_json() |
|---|
| 103 | |
|---|
| 104 | def formatSectionDoc(self, section): |
|---|
| 105 | cmds = reflect.prefixedMethodNames(section["object"], 'jsonrpc_') |
|---|
| 106 | cmds = [getattr(section["object"], 'jsonrpc_%s'%cmd) for cmd in cmds] |
|---|
| 107 | return """ |
|---|
| 108 | === `%(section)s` === |
|---|
| 109 | |
|---|
| 110 | %(commands)s |
|---|
| 111 | """ % { |
|---|
| 112 | "section": section["desc"], |
|---|
| 113 | "commands": "\n\n".join(map(self.formatCommandDoc, cmds,\ |
|---|
| 114 | [section["prefix"] for i in range(len(cmds))])), |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | def formatCommandDoc(self, cmd, prefix = ""): |
|---|
| 118 | args = '' |
|---|
| 119 | |
|---|
| 120 | command_args = cmd.params or [] |
|---|
| 121 | for arg in command_args: |
|---|
| 122 | props = [] |
|---|
| 123 | |
|---|
| 124 | # An argument is optional by default |
|---|
| 125 | if 'req' not in arg.keys(): |
|---|
| 126 | arg['req'] = False |
|---|
| 127 | if arg['req']: |
|---|
| 128 | props.append('Mandatory') |
|---|
| 129 | else: |
|---|
| 130 | props.append('Optional') |
|---|
| 131 | |
|---|
| 132 | args += " * {{{%(name)s}}} (%(props)s) : %(type)s\n"\ |
|---|
| 133 | % { 'name': arg['name'], |
|---|
| 134 | 'props': ' and '.join(props), |
|---|
| 135 | 'type' : arg['type'] } |
|---|
| 136 | |
|---|
| 137 | if len(command_args) == 0: |
|---|
| 138 | args = " * ''This command does not accept any argument.''\n" |
|---|
| 139 | |
|---|
| 140 | rvalues = None |
|---|
| 141 | try: |
|---|
| 142 | if isinstance(cmd.answer_type, list): |
|---|
| 143 | rvalues = cmd.answer_type |
|---|
| 144 | else: |
|---|
| 145 | rvalues = [cmd.answer_type] |
|---|
| 146 | except AttributeError: |
|---|
| 147 | rvalues = ['ack'] |
|---|
| 148 | |
|---|
| 149 | return """==== `%(name)s` ==== |
|---|
| 150 | |
|---|
| 151 | %(desc)s |
|---|
| 152 | |
|---|
| 153 | Arguments : |
|---|
| 154 | %(args)s |
|---|
| 155 | Expected return value : ''`%(rvalues)s`'' |
|---|
| 156 | |
|---|
| 157 | """ % { 'name' : prefix+cmd.__name__[8:], |
|---|
| 158 | 'desc' : cmd.__doc__.strip('\n'), |
|---|
| 159 | 'args' : args, |
|---|
| 160 | 'rvalues' : rvalues } |
|---|
| 161 | |
|---|
| 162 | def build(self, sections): |
|---|
| 163 | filter = And(Equals("artist", "artist_name"),\ |
|---|
| 164 | Or(Contains("genre", "Rock"), Higher("Rating", "4"))) |
|---|
| 165 | signal = DeejaydSignal("signal_name", {"attr1": "value1"}) |
|---|
| 166 | return """= deejayd - JSON-RPC Protocol = |
|---|
| 167 | |
|---|
| 168 | Deejayd protocol follows JSON-RPC 1.0 specification available |
|---|
| 169 | [http://json-rpc.org/wiki/specification here]. |
|---|
| 170 | All data between the client and server is encoded in UTF-8. |
|---|
| 171 | |
|---|
| 172 | == Commands Format == |
|---|
| 173 | |
|---|
| 174 | %(cmd_format)s |
|---|
| 175 | |
|---|
| 176 | == Response Format == |
|---|
| 177 | |
|---|
| 178 | %(answer)s |
|---|
| 179 | |
|---|
| 180 | == Specific Objects == |
|---|
| 181 | |
|---|
| 182 | === Mediafilter Objects === |
|---|
| 183 | |
|---|
| 184 | Mediafilter object has been serialized in a specific way to be passed as |
|---|
| 185 | an method argument or receive with an answer. An example is given here. |
|---|
| 186 | {{{ |
|---|
| 187 | `%(filter)s` |
|---|
| 188 | }}} |
|---|
| 189 | |
|---|
| 190 | === Signal Objects === |
|---|
| 191 | |
|---|
| 192 | Signal is available for TCP connection only. |
|---|
| 193 | Signal object has been serialized in a specific way to be send to client. |
|---|
| 194 | An example is given here. |
|---|
| 195 | {{{ |
|---|
| 196 | `%(signal)s` |
|---|
| 197 | }}} |
|---|
| 198 | |
|---|
| 199 | == Common Available Commands == |
|---|
| 200 | |
|---|
| 201 | %(commands)s |
|---|
| 202 | |
|---|
| 203 | == Http Specific Commands == |
|---|
| 204 | |
|---|
| 205 | === General Commands === |
|---|
| 206 | |
|---|
| 207 | %(serverinfo_cmd)s |
|---|
| 208 | |
|---|
| 209 | %(web_commands)s |
|---|
| 210 | |
|---|
| 211 | == TCP Specific Commands == |
|---|
| 212 | |
|---|
| 213 | === General Commands === |
|---|
| 214 | |
|---|
| 215 | %(close_cmd)s |
|---|
| 216 | |
|---|
| 217 | %(tcp_commands)s |
|---|
| 218 | """ % { |
|---|
| 219 | "cmd_format": self.commandDoc(), |
|---|
| 220 | "answer": self.answerDoc(), |
|---|
| 221 | "filter": Get_json_filter(filter).to_pretty_json(), |
|---|
| 222 | "commands": "\n\n".join(map(self.formatSectionDoc, sections)), |
|---|
| 223 | "signal": DeejaydJSONSignal(signal).to_pretty_json(), |
|---|
| 224 | "web_commands": self.formatSectionDoc({\ |
|---|
| 225 | "prefix": "web.", |
|---|
| 226 | "desc": "Commands specific to webui", |
|---|
| 227 | "object": protocol.DeejaydWebJSONRPC, |
|---|
| 228 | }), |
|---|
| 229 | "tcp_commands": self.formatSectionDoc({\ |
|---|
| 230 | "prefix": "signal.", |
|---|
| 231 | "desc": "Signal subscription commands", |
|---|
| 232 | "object": protocol.DeejaydSignalJSONRPC, |
|---|
| 233 | }), |
|---|
| 234 | "serverinfo_cmd": self.formatCommandDoc(\ |
|---|
| 235 | protocol.DeejaydHttpJSONRPC.jsonrpc_serverInfo), |
|---|
| 236 | "close_cmd": self.formatCommandDoc(\ |
|---|
| 237 | protocol.DeejaydTcpJSONRPC.jsonrpc_close), |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | if __name__ == "__main__": |
|---|
| 241 | docs = WikiFormat().build(common_request) |
|---|
| 242 | |
|---|
| 243 | f = open("doc/deejayd_rpc_protocol","w") |
|---|
| 244 | try: f.write(docs) |
|---|
| 245 | finally: f.close() |
|---|
| 246 | |
|---|
| 247 | |
|---|
| 248 | # vim: ts=4 sw=4 expandtab |
|---|