kapsikkum-unmanic – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | #!/usr/bin/env python3 |
2 | # -*- coding: utf-8 -*- |
||
3 | |||
4 | """ |
||
5 | unmanic.plugins.py |
||
6 | |||
7 | Written by: Josh.5 <jsunnex@gmail.com> |
||
8 | Date: 25 Aug 2021, (3:49 PM) |
||
9 | |||
10 | Copyright: |
||
11 | Copyright (C) Josh Sunnex - All Rights Reserved |
||
12 | |||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy |
||
14 | of this software and associated documentation files (the "Software"), to deal |
||
15 | in the Software without restriction, including without limitation the rights |
||
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
17 | copies of the Software, and to permit persons to whom the Software is |
||
18 | furnished to do so, subject to the following conditions: |
||
19 | |||
20 | The above copyright notice and this permission notice shall be included in all |
||
21 | copies or substantial portions of the Software. |
||
22 | |||
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||
26 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
27 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
28 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE |
||
29 | OR OTHER DEALINGS IN THE SOFTWARE. |
||
30 | |||
31 | """ |
||
32 | import os |
||
33 | import sys |
||
34 | import traceback |
||
35 | |||
36 | import tornado.web |
||
37 | import tornado.log |
||
38 | |||
39 | from unmanic.webserver.helpers import plugins |
||
40 | |||
41 | |||
42 | def get_plugin_by_path(path): |
||
43 | # Get the plugin ID from the url |
||
44 | split_path = path.split('/') |
||
45 | plugin_type = split_path[2] |
||
46 | plugin_id = split_path[3] |
||
47 | if plugin_type == 'plugin_api': |
||
48 | # Fetch all api plugins |
||
49 | results = plugins.get_enabled_plugin_plugin_apis() |
||
50 | else: |
||
51 | # Fetch all frontend plugins |
||
52 | results = plugins.get_enabled_plugin_data_panels() |
||
53 | # Check if their path matches |
||
54 | plugin_module = None |
||
55 | for result in results: |
||
56 | if plugin_id == result.get('plugin_id'): |
||
57 | plugin_module = result |
||
58 | break |
||
59 | return plugin_module |
||
60 | |||
61 | |||
62 | class DataPanelRequestHandler(tornado.web.RequestHandler): |
||
63 | name = None |
||
64 | |||
65 | def initialize(self): |
||
66 | self.name = 'DataPanel' |
||
67 | |||
68 | def get(self, path): |
||
69 | self.handle_panel_request() |
||
70 | |||
71 | def handle_panel_request(self): |
||
72 | # Get the remainder of the path after the plugin ID. This will be passed as path |
||
73 | path = list(filter(None, self.request.path.split('/')[4:])) |
||
74 | |||
75 | # Generate default data |
||
76 | data = { |
||
77 | 'content_type': 'text/html', |
||
78 | 'content': "<!doctype html>" |
||
79 | "<html>" |
||
80 | "<head></head>" |
||
81 | "<body></body>" |
||
82 | "</html>", |
||
83 | 'path': "/" + "/".join(path), |
||
84 | 'uri': self.request.uri, |
||
85 | 'query': self.request.query, |
||
86 | 'arguments': self.request.arguments, |
||
87 | } |
||
88 | plugin_module = get_plugin_by_path(self.request.path) |
||
89 | if not plugin_module: |
||
90 | self.set_status(404) |
||
91 | self.write('404 Not Found') |
||
92 | return |
||
93 | |||
94 | # Run plugin and fetch return data |
||
95 | if not plugins.exec_data_panels_plugin_runner(data, plugin_module.get('plugin_id')): |
||
96 | tornado.log.app_log.exception( |
||
97 | "Exception while carrying out plugin runner on DataPanel '{}'".format(plugin_module.get('plugin_id'))) |
||
98 | |||
99 | self.render_data(data) |
||
100 | return |
||
101 | |||
102 | def render_data(self, data): |
||
103 | self.set_header("Content-Type", data.get('content_type', 'text/html')) |
||
104 | self.write(data.get('content')) |
||
105 | |||
106 | |||
107 | class PluginAPIRequestHandler(tornado.web.RequestHandler): |
||
108 | name = None |
||
109 | |||
110 | def initialize(self): |
||
111 | self.name = 'PluginAPI' |
||
112 | |||
113 | def get(self, path): |
||
114 | self.handle_panel_request() |
||
115 | |||
116 | def post(self, path): |
||
117 | self.handle_panel_request() |
||
118 | |||
119 | def handle_panel_request(self): |
||
120 | path = list(filter(None, self.request.path.split('/')[4:])) |
||
121 | |||
122 | # Generate default data |
||
123 | data = { |
||
124 | 'content_type': 'application/json', |
||
125 | 'content': {}, |
||
126 | 'path': "/" + "/".join(path), |
||
127 | 'uri': self.request.uri, |
||
128 | 'query': self.request.query, |
||
129 | 'arguments': self.request.arguments, |
||
130 | 'body': self.request.body, |
||
131 | } |
||
132 | plugin_module = get_plugin_by_path(self.request.path) |
||
133 | if not plugin_module: |
||
134 | self.set_status(404, reason="404 Not Found") |
||
135 | status_code = self.get_status() |
||
136 | self.write({ |
||
137 | 'error': "%(code)d: %(message)s" % {"code": status_code, "message": self._reason}, |
||
138 | 'messages': {}, |
||
139 | }) |
||
140 | return |
||
141 | |||
142 | # Run plugin and fetch return data |
||
143 | try: |
||
144 | if not plugins.exec_plugin_api_plugin_runner(data, plugin_module.get('plugin_id')): |
||
145 | tornado.log.app_log.exception( |
||
146 | "Exception while carrying out plugin runner on PluginAPI '{}'".format(plugin_module.get('plugin_id'))) |
||
147 | except Exception as e: |
||
148 | self.set_status(500, reason="Error running plugin API: {}".format(str(e))) |
||
149 | status_code = self.get_status() |
||
150 | exc_info = sys.exc_info() |
||
151 | traceback_lines = [] |
||
152 | if exc_info and exc_info[0]: |
||
153 | for line in traceback.format_exception(*exc_info): |
||
154 | traceback_lines.append(line) |
||
155 | self.write({ |
||
156 | 'error': "%(code)d: %(message)s" % {"code": status_code, "message": self._reason}, |
||
157 | 'messages': {}, |
||
158 | 'traceback': traceback_lines |
||
159 | }) |
||
160 | return |
||
161 | |||
162 | self.render_data(data) |
||
163 | |||
164 | def render_data(self, data): |
||
165 | self.set_header("Content-Type", data.get('content_type', 'application/json')) |
||
166 | self.write(data.get('content')) |
||
167 | |||
168 | |||
169 | class PluginStaticFileHandler(tornado.web.StaticFileHandler): |
||
170 | """ |
||
171 | A static file handler which serves static content from a plugin '/static/' directory. |
||
172 | """ |
||
173 | |||
174 | def initialize(self, path, default_filename=None): |
||
175 | plugin_module = get_plugin_by_path(self.request.path) |
||
176 | if plugin_module: |
||
177 | path = os.path.join(plugin_module.get('plugin_path'), 'static') |
||
178 | super(PluginStaticFileHandler, self).initialize(path, default_filename) |