-
-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathinitialization.py
More file actions
executable file
·314 lines (264 loc) · 14.2 KB
/
initialization.py
File metadata and controls
executable file
·314 lines (264 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import logging
import time
from utilities.settings import get_all_settings, get_setting
# Progress ranges for each phase
PROGRESS_RANGES = {
'reset': (0, 5), # 5 seconds
'plex': (5, 40), # 2 minutes
'sources': (40, 70), # 2 minutes
'release': (70, 85), # 30 seconds
'show_ids': (85, 92), # 1 minute
'show_titles': (92, 99), # 1 minute
'movie_ids': (99, 100), # 1 minute
'movie_titles': (100, 100) # 1 minute
}
# Duration for each phase in seconds
PHASE_DURATIONS = {
'reset': 5,
'plex': 120, # 2 minutes
'sources': 120, # 2 minutes
'release': 30, # 30 seconds
'show_ids': 60, # 1 minute
'show_titles': 60, # 1 minute
'movie_ids': 60, # 1 minute
'movie_titles': 60 # 1 minute
}
# Global variable to track initialization progress
initialization_status = {
'current_step': '',
'total_steps': 10,
'current_step_number': 0,
'progress_value': 0, # Current progress percentage
'substep_details': '',
'error_details': None,
'is_substep': False,
'phase_start_time': 0, # Start time of current phase
'current_phase': None # Current phase identifier
}
def get_initialization_status():
# Update progress based on elapsed time if in a timed phase
if initialization_status['current_phase'] and initialization_status['phase_start_time'] > 0:
phase = initialization_status['current_phase']
start_time = initialization_status['phase_start_time']
current_time = time.time()
# Calculate elapsed percentage of the phase
elapsed_time = current_time - start_time
phase_duration = PHASE_DURATIONS.get(phase, 0)
if phase_duration > 0:
progress_range = PROGRESS_RANGES.get(phase, (0, 0))
range_size = progress_range[1] - progress_range[0]
# Calculate progress within the phase
progress_pct = min(1.0, elapsed_time / phase_duration)
new_progress = progress_range[0] + (range_size * progress_pct)
# Update the progress value
initialization_status['progress_value'] = new_progress
return initialization_status
def start_phase(phase_name, step_name, details=''):
"""Start a new timed phase of initialization."""
initialization_status['current_phase'] = phase_name
initialization_status['phase_start_time'] = time.time()
initialization_status['current_step'] = step_name
initialization_status['substep_details'] = details
initialization_status['progress_value'] = PROGRESS_RANGES.get(phase_name, (0, 0))[0]
initialization_status['current_step_number'] += 1
def complete_phase(phase_name):
"""Mark a phase as complete, setting progress to the end of its range."""
if phase_name in PROGRESS_RANGES:
initialization_status['progress_value'] = PROGRESS_RANGES[phase_name][1]
initialization_status['phase_start_time'] = 0 # Stop the time-based progress
def update_initialization_step(step_name, substep_details='', error=None, is_substep=False):
"""Update initialization status without changing the progress timing."""
initialization_status['current_step'] = step_name
initialization_status['substep_details'] = substep_details
initialization_status['error_details'] = error
initialization_status['is_substep'] = is_substep
if not is_substep:
initialization_status['current_step_number'] += 1
#logging.info(f"Initialization {'substep' if is_substep else 'step'} {initialization_status['current_step_number']}/{initialization_status['total_steps']}: {step_name}")
if substep_details:
logging.info(f" Details: {substep_details}")
def format_item_log(item):
if item['type'] == 'movie':
return f"{item['title']} ({item['year']})"
elif item['type'] == 'episode':
return f"{item['title']} S{item['season_number']:02d}E{item['episode_number']:02d}"
else:
return item['title']
def reset_queued_item_status():
update_initialization_step("Reset Items", "Identifying items in processing states", is_substep=True)
logging.info("Resetting queued item status...")
states_to_reset = ['Sleeping']
total_reset = 0
from database import update_media_item_state, get_all_media_items
for state in states_to_reset:
items = list(get_all_media_items(state=state))
if items:
update_initialization_step("Reset Items", f"Processing {len(items)} items in {state} state", is_substep=True)
for item in items:
update_media_item_state(item['id'], 'Wanted')
total_reset += 1
logging.info(f"Reset item {format_item_log(item)} (ID: {item['id']}) from {state} to Wanted")
update_initialization_step("Reset Items", f"Reset {total_reset} items to Wanted state", is_substep=True)
def plex_collection_update(skip_initial_plex_update):
from queues.run_program import get_and_add_all_collected_from_plex, get_and_add_recent_collected_from_plex
from database import get_all_media_items
from utilities.plex_watch_history_functions import sync_get_watch_history_from_plex
from utilities.settings import get_setting
update_initialization_step("Plex Update", "Starting Plex scan")
logging.info("Updating Plex collection...")
# Check if we should pull watch history
if get_setting('Debug', 'do_not_add_plex_watch_history_items_to_queue', False):
update_initialization_step("Pulling Plex Watch History", "Retrieving watch history from Plex", is_substep=True)
try:
sync_get_watch_history_from_plex()
logging.info("Successfully retrieved Plex watch history")
except Exception as e:
logging.error(f"Error retrieving Plex watch history: {str(e)}")
try:
update_initialization_step("Plex Update",
"Performing quick scan" if skip_initial_plex_update else "Performing full library scan",
is_substep=True)
if skip_initial_plex_update:
result = get_and_add_recent_collected_from_plex()
else:
result = get_and_add_all_collected_from_plex()
# First check if we got any content from Plex
if result and isinstance(result, dict):
movies = result.get('movies', [])
episodes = result.get('episodes', [])
if len(movies) > 0 or len(episodes) > 0:
update_initialization_step("Plex Update",
f"Found {len(movies)} movies and {len(episodes)} episodes",
is_substep=True)
return True, True # Plex responded and had content
# If Plex returned no items, check if we have any existing collected items
existing_collected = list(get_all_media_items(state='Collected'))
if existing_collected:
msg = "Plex scan returned no new content but found existing collected items - skipping content sources to prevent data loss"
update_initialization_step("Plex Update", msg, error=msg, is_substep=True)
logging.warning(msg)
return True, False # Plex responded but had no content, have existing items
msg = "Plex scan returned no content and no existing collected items found - will proceed with content sources - this appears to be a new library"
update_initialization_step("Plex Update", msg, is_substep=True)
logging.info(msg)
return True, True # Plex responded but had no content, no existing items
except Exception as e:
error_msg = f"Error during Plex collection update: {str(e)}"
update_initialization_step("Plex Update", error_msg, error=error_msg, is_substep=True)
logging.error(error_msg)
logging.error("Skipping collection update to prevent data loss")
return False, False # Plex error occurred
def get_all_wanted_from_enabled_sources(runner=None):
from routes.debug_routes import get_and_add_wanted_content
content_sources = get_all_settings().get('Content Sources', {})
# Respect task toggles: include only sources whose corresponding task is enabled in the runner
selected_sources = []
for source_id in content_sources.keys():
task_id = f"task_{source_id}_wanted"
if runner and hasattr(runner, 'enabled_tasks'):
normalized = runner._normalize_task_name(task_id) if hasattr(runner, '_normalize_task_name') else task_id
if normalized in runner.enabled_tasks or task_id in runner.enabled_tasks:
selected_sources.append(source_id)
else:
# Fallback: if runner not available, assume enabled by default
selected_sources.append(source_id)
update_initialization_step("Content Sources",
f"Found {len(selected_sources)} sources",
is_substep=True)
for source_id in selected_sources:
update_initialization_step("Content Sources",
f"Retrieving wanted content from {source_id}",
is_substep=True)
get_and_add_wanted_content(source_id)
update_initialization_step("Content Sources",
f"Processed {len(selected_sources)} sources",
is_substep=True)
def initialize(skip_initial_plex_update=False):
"""Initialize the application state.
Note: This function is typically called on fresh program starts.
For mid-run restarts (e.g., after settings changes), initialization
is skipped to avoid unnecessary processing and potential data loss.
"""
from metadata.metadata import refresh_release_dates
# Reset initialization status
initialization_status['current_step'] = ''
initialization_status['current_step_number'] = 0
initialization_status['progress_value'] = 0
initialization_status['substep_details'] = ''
initialization_status['error_details'] = None
initialization_status['is_substep'] = False
initialization_status['phase_start_time'] = 0
initialization_status['current_phase'] = None
# Start initialization
update_initialization_step('Starting initialization')
# Keep for now: Disable reset_queued_item_status
# Reset Items Phase (5 seconds)
start_phase('reset', 'Reset Items', 'Starting item reset')
reset_queued_item_status()
complete_phase('reset')
plex_managed = get_setting('File Management', 'file_collection_management') == 'Plex'
plex_success = True # Assume success unless Plex runs and fails
should_process_sources = True # Assume true, might be set to false by plex_collection_update
if plex_managed:
from database import get_all_media_items # Import here for conditional check
existing_collected = list(get_all_media_items(state='Collected'))
if not existing_collected:
# No collected items found, run full Plex scan
update_initialization_step("Plex Update", "No collected items found, performing full library scan", is_substep=True)
logging.info("No collected items found, performing full library scan.")
start_phase('plex', 'Plex Update', 'Starting Plex scan')
# Force full scan, ignore skip_initial_plex_update parameter
plex_success, should_process_sources = plex_collection_update(skip_initial_plex_update=False)
complete_phase('plex')
else:
# Collected items exist, skip Plex scan
update_initialization_step("Plex Update", f"Found {len(existing_collected)} collected items, skipping Plex scan as requested.", is_substep=True)
logging.info(f"Found {len(existing_collected)} collected items, skipping Plex scan as requested.")
# Keep for now: Disable plex_collection_update when items exist
# # Plex Update Phase (2 minutes)
# start_phase('plex', 'Plex Update', 'Starting Plex scan')
# plex_success, should_process_sources = plex_collection_update(skip_initial_plex_update) # Original call
# complete_phase('plex')
# When skipping, assume success and allow source processing
plex_success = True
should_process_sources = True
# else: # Not Plex managed - flags remain True
# Now process sources based on the flags
if should_process_sources:
# Content Sources Phase (2 minutes)
start_phase('sources', 'Content Sources', 'Starting source processing')
# Pass ProgramRunner singleton so toggles can be respected
from queues.run_program import ProgramRunner
runner = ProgramRunner()
get_all_wanted_from_enabled_sources(runner)
complete_phase('sources')
else:
logging.info("Skipping content sources due to Plex update status.")
# Manually set progress past the skipped sources phase
if 'sources' in PROGRESS_RANGES:
initialization_status['progress_value'] = PROGRESS_RANGES['sources'][1]
# Always refresh release dates regardless of content sources status
# Refresh Release Dates Phase (30 seconds)
start_phase('release', 'Refresh Release Dates', 'Starting release date refresh')
refresh_release_dates()
complete_phase('release')
# Keep for now: Disable database maintenance
# from database.maintenance import update_show_ids, update_show_titles, update_movie_ids, update_movie_titles
# # Update Show IDs and Titles (1 minute)
# start_phase('show_ids', 'Update Show IDs', 'Updating show IDs')
# update_show_ids()
# complete_phase('show_ids')
# start_phase('show_titles', 'Update Show Titles', 'Updating show titles')
# update_show_titles()
# complete_phase('show_titles')
# # Update Movie IDs and Titles (1 minute)
# start_phase('movie_ids', 'Update Movie IDs', 'Updating movie IDs')
# update_movie_ids()
# complete_phase('movie_ids')
# start_phase('movie_titles', 'Update Movie Titles', 'Updating movie titles')
# update_movie_titles()
# complete_phase('movie_titles')
# Complete
final_status = "completed successfully" if plex_success else "completed with Plex update issues"
update_initialization_step("Complete", final_status)
return plex_success