kapsikkum-unmanic – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3  
4 """
5 unmanic.service.py
6  
7 Written by: Josh.5 <jsunnex@gmail.com>
8 Date: 06 Dec 2018, (7:21 AM)
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 argparse
33 import os
34 import time
35 import queue
36 import signal
37 import threading
38  
39 from unmanic import config, metadata
40 from unmanic.libs import libraryscanner, unlogger, common, eventmonitor
41 from unmanic.libs.db_migrate import Migrations
42 from unmanic.libs.scheduler import ScheduledTasksManager
43 from unmanic.libs.taskqueue import TaskQueue
44 from unmanic.libs.postprocessor import PostProcessor
45 from unmanic.libs.taskhandler import TaskHandler
46 from unmanic.libs.uiserver import FrontendPushMessages, UIServer
47 from unmanic.libs.foreman import Foreman
48  
49 unmanic_logging = unlogger.UnmanicLogger.__call__()
50 main_logger = unmanic_logging.get_logger()
51  
52  
53 def init_db(config_path):
54 # Set paths
55 app_dir = os.path.dirname(os.path.abspath(__file__))
56  
57 # Set database connection settings
58 database_settings = {
59 "TYPE": "SQLITE",
60 "FILE": os.path.join(config_path, 'unmanic.db'),
61 "MIGRATIONS_DIR": os.path.join(app_dir, 'migrations_v1'),
62 "MIGRATIONS_HISTORY_VERSION": 'v1',
63 }
64  
65 # Ensure the config path exists
66 if not os.path.exists(config_path):
67 os.makedirs(config_path)
68  
69 # Create database connection
70 from unmanic.libs.unmodels.lib import Database
71 db_connection = Database.select_database(database_settings)
72  
73 # Run database migrations
74 migrations = Migrations(database_settings)
75 migrations.update_schema()
76  
77 # Return the database connection
78 return db_connection
79  
80  
81 class Service:
82  
83 def __init__(self):
84 self.threads = []
85 self.run_threads = True
86 self.db_connection = None
87  
88 self.developer = None
89 self.dev_local_api = None
90  
91 self.event = threading.Event()
92  
93 def start_handler(self, data_queues, task_queue):
94 main_logger.info("Starting TaskHandler")
95 handler = TaskHandler(data_queues, task_queue, self.event)
96 handler.daemon = True
97 handler.start()
98 self.threads.append({
99 'name': 'TaskHandler',
100 'thread': handler
101 })
102 return handler
103  
104 def start_post_processor(self, data_queues, task_queue):
105 main_logger.info("Starting PostProcessor")
106 postprocessor = PostProcessor(data_queues, task_queue, self.event)
107 postprocessor.daemon = True
108 postprocessor.start()
109 self.threads.append({
110 'name': 'PostProcessor',
111 'thread': postprocessor
112 })
113 return postprocessor
114  
115 def start_foreman(self, data_queues, settings, task_queue):
116 main_logger.info("Starting Foreman")
117 foreman = Foreman(data_queues, settings, task_queue, self.event)
118 foreman.daemon = True
119 foreman.start()
120 self.threads.append({
121 'name': 'Foreman',
122 'thread': foreman
123 })
124 return foreman
125  
126 def start_library_scanner_manager(self, data_queues):
127 main_logger.info("Starting LibraryScannerManager")
128 library_scanner_manager = libraryscanner.LibraryScannerManager(data_queues, self.event)
129 library_scanner_manager.daemon = True
130 library_scanner_manager.start()
131 self.threads.append({
132 'name': 'LibraryScannerManager',
133 'thread': library_scanner_manager
134 })
135 return library_scanner_manager
136  
137 def start_inotify_watch_manager(self, data_queues, settings):
138 if eventmonitor.event_monitor_module:
139 main_logger.info("Starting EventMonitorManager")
140 event_monitor_manager = eventmonitor.EventMonitorManager(data_queues, self.event)
141 event_monitor_manager.daemon = True
142 event_monitor_manager.start()
143 self.threads.append({
144 'name': 'EventMonitorManager',
145 'thread': event_monitor_manager
146 })
147 return event_monitor_manager
148 else:
149 main_logger.warn("Unable to start EventMonitorManager as no event monitor module was found")
150  
151 def start_ui_server(self, data_queues, foreman):
152 main_logger.info("Starting UIServer")
153 uiserver = UIServer(data_queues, foreman, self.developer)
154 uiserver.daemon = True
155 uiserver.start()
156 self.threads.append({
157 'name': 'UIServer',
158 'thread': uiserver
159 })
160 return uiserver
161  
162 def start_scheduled_tasks_manager(self):
163 main_logger.info("Starting ScheduledTasksManager")
164 scheduled_tasks_manager = ScheduledTasksManager(self.event)
165 scheduled_tasks_manager.daemon = True
166 scheduled_tasks_manager.start()
167 self.threads.append({
168 'name': 'ScheduledTasksManager',
169 'thread': scheduled_tasks_manager
170 })
171 return scheduled_tasks_manager
172  
173 @staticmethod
174 def initial_register_unmanic(dev_local_api):
175 from unmanic.libs import session
176 s = session.Session(dev_local_api=dev_local_api)
177 s.register_unmanic(s.get_installation_uuid())
178  
179 def start_threads(self, settings):
180 # Create our data queues
181 data_queues = {
182 "library_scanner_triggers": queue.Queue(maxsize=1),
183 "scheduledtasks": queue.Queue(),
184 "inotifytasks": queue.Queue(),
185 "progress_reports": queue.Queue(),
186 "frontend_messages": FrontendPushMessages(),
187 "logging": unmanic_logging
188 }
189  
190 # Clear cache directory
191 main_logger.info("Clearing previous cache")
192 common.clean_files_in_cache_dir(settings.get_cache_path())
193  
194 main_logger.info("Starting all threads")
195  
196 # Register installation
197 self.initial_register_unmanic(self.dev_local_api)
198  
199 # Setup job queue
200 task_queue = TaskQueue(data_queues)
201  
202 # Setup post-processor thread
203 self.start_post_processor(data_queues, task_queue)
204  
205 # Start the foreman thread
206 foreman = self.start_foreman(data_queues, settings, task_queue)
207  
208 # Start new thread to handle messages from service
209 self.start_handler(data_queues, task_queue)
210  
211 # Start scheduled thread
212 self.start_library_scanner_manager(data_queues)
213  
214 # Start inotify watch manager
215 self.start_inotify_watch_manager(data_queues, settings)
216  
217 # Start new thread to run the web UI
218 self.start_ui_server(data_queues, foreman)
219  
220 # Start new thread to run the scheduled tasks manager
221 self.start_scheduled_tasks_manager()
222  
223 def stop_threads(self):
224 main_logger.info("Stopping all threads")
225 self.event.set()
226 for thread in self.threads:
227 main_logger.info("Sending thread {} abort signal".format(thread['name']))
228 thread['thread'].stop()
229 for thread in self.threads:
230 main_logger.info("Waiting for thread {} to stop".format(thread['name']))
231 thread['thread'].join(10)
232 main_logger.info("Thread {} has successfully stopped".format(thread['name']))
233 self.threads = []
234  
235 def sig_handle(self, signum, frame):
236 main_logger.info("Received {}".format(signum))
237 self.stop()
238  
239 def stop(self):
240 self.run_threads = False
241  
242 def run(self):
243 # Init the configuration
244 settings = config.Config()
245  
246 # Init the database
247 self.db_connection = init_db(settings.get_config_path())
248  
249 # Start all threads
250 self.start_threads(settings)
251  
252 # Watch for the term signal
253 if os.name == "nt":
254 while self.run_threads:
255 try:
256 time.sleep(1)
257 except (KeyboardInterrupt, SystemExit) as e:
258 break
259 else:
260 signal.signal(signal.SIGINT, self.sig_handle)
261 signal.signal(signal.SIGTERM, self.sig_handle)
262 while self.run_threads:
263 signal.pause()
264 time.sleep(.5)
265  
266 # Received term signal. Stop everything
267 self.stop_threads()
268 self.db_connection.stop()
269 while not self.db_connection.is_stopped():
270 time.sleep(.5)
271 continue
272 main_logger.info("Exit Unmanic")
273  
274  
275 def main():
276 parser = argparse.ArgumentParser(description='Unmanic')
277 parser.add_argument('--version', action='version',
278 version='%(prog)s {version}'.format(version=metadata.read_version_string('long')))
279 parser.add_argument('--manage_plugins', action='store_true',
280 help='manage installed plugins')
281 parser.add_argument('--dev',
282 action='store_true',
283 help='Enable developer mode')
284 parser.add_argument('--dev-local-api',
285 action='store_true',
286 help='Enable development against local unmanic support api')
287 parser.add_argument('--port', nargs='?',
288 help='Specify the port to run the webserver on')
289 # parser.add_argument('--unmanic_path', nargs='?',
290 # help='Specify the unmanic configuration path instead of ~/.unmanic')
291 args = parser.parse_args()
292  
293 # Configure application from args
294 settings = config.Config(
295 port=args.port,
296 unmanic_path=None
297 )
298  
299 if args.manage_plugins:
300 # Init the DB connection
301 db_connection = init_db(settings.get_config_path())
302  
303 # Run the plugin manager CLI
304 from unmanic.libs.unplugins.pluginscli import PluginsCLI
305 plugin_cli = PluginsCLI()
306 plugin_cli.run()
307  
308 # Stop the DB connection
309 db_connection.stop()
310 while not db_connection.is_stopped():
311 time.sleep(.2)
312 continue
313 else:
314 # Run the main Unmanic service
315 service = Service()
316 service.developer = args.dev
317 service.dev_local_api = args.dev_local_api
318 service.run()
319  
320  
321 if __name__ == "__main__":
322 main()