From 2bdeba1b45e422b84f6da60a31942cdac12dcea0 Mon Sep 17 00:00:00 2001 From: markmental Date: Fri, 27 Mar 2026 23:33:09 -0400 Subject: [PATCH] Guide cleanup --- src/theme.h | 21 ++++ src/ui.c | 284 +++++++++++++++++++++++++++++++++++++++++++--------- src/ui.h | 2 - src/ui.o | Bin 15504 -> 20056 bytes 4 files changed, 256 insertions(+), 51 deletions(-) diff --git a/src/theme.h b/src/theme.h index b249b35..1d88983 100644 --- a/src/theme.h +++ b/src/theme.h @@ -17,10 +17,31 @@ static const SDL_Color COLOR_PALE_BLUE = {0xcc, 0xd8, 0xf3, 0xff}; static const SDL_Color COLOR_TEXT_LIGHT = {0xf5, 0xf7, 0xfa, 0xff}; static const SDL_Color COLOR_TEXT_DARK = {0x0b, 0x11, 0x1d, 0xff}; static const SDL_Color COLOR_BLACK = {0x00, 0x00, 0x00, 0xff}; +static const SDL_Color COLOR_HEADER_SILVER = {0xe6, 0xe6, 0xe6, 0xff}; +static const SDL_Color COLOR_SELECTED_ROW = {0x06, 0x13, 0x2c, 0xff}; +static const SDL_Color COLOR_UNSELECTED_ROW = {0x33, 0x44, 0x66, 0xff}; +static const SDL_Color COLOR_GUIDE_TOP = {0x00, 0x33, 0x99, 0xff}; +static const SDL_Color COLOR_GUIDE_BOTTOM = {0x00, 0x11, 0x33, 0xff}; +static const SDL_Color COLOR_GRID_LINE = {0x89, 0xa0, 0xc5, 0xff}; +static const SDL_Color COLOR_PANEL_TEXT = {0x12, 0x18, 0x24, 0xff}; +static const SDL_Color COLOR_PANEL_SHADOW = {0x00, 0x00, 0x00, 0x60}; +static const SDL_Color COLOR_STATUS_DIVIDER = {0xb0, 0xb8, 0xc7, 0xff}; +static const SDL_Color COLOR_BLOCK_UNSELECTED = {0x1f, 0x5d, 0xc1, 0xff}; +static const SDL_Color COLOR_BLOCK_SELECTED = {0xff, 0xd7, 0x00, 0xff}; +static const SDL_Color COLOR_SELECTION_EDGE = {0xff, 0xf1, 0x8b, 0xff}; #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 #define GUIDE_VISIBLE_ROWS 5 #define TIMELINE_VISIBLE_SECONDS (90.0 * 60.0) +#define GUIDE_INFO_WIDTH 390 +#define GUIDE_PREVIEW_WIDTH 430 +#define GUIDE_PREVIEW_HEIGHT 194 +#define GUIDE_STATUS_HEIGHT 42 +#define GUIDE_GRID_TOP 212 +#define GUIDE_FOOTER_HEIGHT 54 +#define GUIDE_SIDEBAR_WIDTH 246 +#define GUIDE_X_START 258 +#define GUIDE_INFO_HEIGHT 184 #endif diff --git a/src/ui.c b/src/ui.c index 2aa6c32..3478d2e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -81,6 +81,100 @@ static void draw_cached_text(SDL_Renderer *renderer, const UiTextTexture *text_t SDL_RenderCopy(renderer, text_texture->texture, NULL, &dst); } +static void fill_vertical_gradient(SDL_Renderer *renderer, const SDL_Rect *rect, SDL_Color top, SDL_Color bottom) { + if (!rect || rect->h <= 0) { + return; + } + + for (int i = 0; i < rect->h; ++i) { + Uint8 r = (Uint8) (top.r + (bottom.r - top.r) * i / SDL_max(rect->h - 1, 1)); + Uint8 g = (Uint8) (top.g + (bottom.g - top.g) * i / SDL_max(rect->h - 1, 1)); + Uint8 b = (Uint8) (top.b + (bottom.b - top.b) * i / SDL_max(rect->h - 1, 1)); + SDL_SetRenderDrawColor(renderer, r, g, b, 255); + SDL_RenderDrawLine(renderer, rect->x, rect->y + i, rect->x + rect->w, rect->y + i); + } +} + +static void fill_rect_alpha(SDL_Renderer *renderer, const SDL_Rect *rect, SDL_Color color) { + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); + SDL_RenderFillRect(renderer, rect); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); +} + +static void draw_panel_shadow(SDL_Renderer *renderer, const SDL_Rect *rect) { + SDL_Rect shadow = {rect->x + 6, rect->y + 6, rect->w, rect->h}; + fill_rect_alpha(renderer, &shadow, COLOR_PANEL_SHADOW); +} + +static void draw_text_clipped(SDL_Renderer *renderer, + TTF_Font *font, + const char *text, + const SDL_Rect *clip_rect, + int x, + int y, + SDL_Color color) { + SDL_Rect previous_clip; + SDL_bool had_clip = SDL_RenderIsClipEnabled(renderer); + if (had_clip) { + SDL_RenderGetClipRect(renderer, &previous_clip); + } + SDL_RenderSetClipRect(renderer, clip_rect); + { + SDL_Texture *texture; + int width = 0; + int height = 0; + SDL_Rect dst; + + texture = text_to_texture(renderer, font, text, color, &width, &height); + if (texture) { + dst.x = x; + dst.y = y; + dst.w = width; + dst.h = height; + SDL_RenderCopy(renderer, texture, NULL, &dst); + SDL_DestroyTexture(texture); + } + } + SDL_RenderSetClipRect(renderer, had_clip ? &previous_clip : NULL); +} + +static void fit_text_with_ellipsis(TTF_Font *font, const char *text, int max_width, char *out, size_t out_size) { + int width = 0; + size_t len; + + if (!out || out_size == 0) { + return; + } + + out[0] = '\0'; + if (!font || !text || max_width <= 0) { + return; + } + + if (TTF_SizeUTF8(font, text, &width, NULL) == 0 && width <= max_width) { + snprintf(out, out_size, "%s", text); + return; + } + + len = strlen(text); + while (len > 0) { + snprintf(out, out_size, "%.*s...", (int) len, text); + if (TTF_SizeUTF8(font, out, &width, NULL) == 0 && width <= max_width) { + return; + } + len -= 1; + } + + snprintf(out, out_size, "..."); +} + +static void format_time_compact(char *buffer, size_t buffer_size, time_t value) { + struct tm local_time; + localtime_r(&value, &local_time); + strftime(buffer, buffer_size, "%I:%M %p", &local_time); +} + static void fill_rect(SDL_Renderer *renderer, const SDL_Rect *rect, SDL_Color color) { set_draw_color(renderer, color); SDL_RenderFillRect(renderer, rect); @@ -130,13 +224,99 @@ static void format_clock_label(char *buffer, size_t buffer_size, time_t now, int static void draw_timeline_header_cached(SDL_Renderer *renderer, const UiCache *cache, SDL_Rect rect) { int segments = 4; - draw_cached_text(renderer, &cache->timeline_day, rect.x + 12, rect.y + 8); - for (int i = 0; i < segments; ++i) { int x = rect.x + (rect.w * i) / segments; - draw_cached_text(renderer, &cache->timeline_labels[i], x + 6, rect.y + 10); - set_draw_color(renderer, COLOR_SLATE); - SDL_RenderDrawLine(renderer, x, rect.y + rect.h - 4, x, rect.y + rect.h + 5 * 76); + draw_cached_text(renderer, &cache->timeline_labels[i], x + 8, rect.y + 10); + set_draw_color(renderer, COLOR_GRID_LINE); + SDL_RenderDrawLine(renderer, x, rect.y + rect.h - 2, x, rect.y + rect.h + 5 * 76); + } +} + +static void draw_status_bar(SDL_Renderer *renderer, + TTF_Font *font, + const Channel *selected_channel, + const SDL_Rect *rect, + time_t now_wall) { + char clock_text[32]; + char channel_text[160]; + + if (!rect) { + return; + } + + fill_rect(renderer, rect, COLOR_HEADER_SILVER); + stroke_rect(renderer, rect, COLOR_GRID_LINE); + format_time_compact(clock_text, sizeof(clock_text), now_wall); + draw_text_clipped(renderer, font, clock_text, rect, rect->x + 12, rect->y + 10, COLOR_PANEL_TEXT); + set_draw_color(renderer, COLOR_STATUS_DIVIDER); + SDL_RenderDrawLine(renderer, rect->x + rect->w / 2, rect->y + 8, rect->x + rect->w / 2, rect->y + rect->h - 8); + + if (selected_channel) { + snprintf(channel_text, sizeof(channel_text), "%s %d", selected_channel->name, selected_channel->number); + draw_text_clipped(renderer, font, channel_text, rect, rect->x + rect->w - 260, rect->y + 10, COLOR_PANEL_TEXT); + } +} + +static void draw_info_panel(SDL_Renderer *renderer, + const UiFonts *fonts, + const Channel *selected_channel, + const SDL_Rect *rect, + Uint64 app_start_ticks, + Uint64 now_ticks, + time_t now_wall) { + SDL_Rect accent; + SDL_Rect clip_rect; + char time_range[64]; + char end_text[32]; + char description[384]; + time_t start_time; + time_t end_time; + double live_position; + + if (!rect || !selected_channel) { + return; + } + + draw_panel_shadow(renderer, rect); + fill_rect(renderer, rect, COLOR_HEADER_SILVER); + accent = (SDL_Rect){rect->x, rect->y, rect->w, 36}; + fill_rect(renderer, &accent, COLOR_HIGHLIGHT_YELLOW); + stroke_rect(renderer, rect, COLOR_GRID_LINE); + + live_position = channel_live_position_precise(selected_channel, app_start_ticks, now_ticks); + start_time = now_wall - (time_t) live_position; + end_time = start_time + (time_t) selected_channel->duration_seconds; + format_time_compact(time_range, sizeof(time_range), start_time); + format_time_compact(end_text, sizeof(end_text), end_time); + strncat(time_range, " - ", sizeof(time_range) - strlen(time_range) - 1); + strncat(time_range, end_text, sizeof(time_range) - strlen(time_range) - 1); + snprintf(description, + sizeof(description), + "Channel %d presents %s. Source: %s.", + selected_channel->number, + selected_channel->program_title, + selected_channel->file_name); + + clip_rect = (SDL_Rect){rect->x + 16, rect->y + 12, rect->w - 32, rect->h - 24}; + draw_text_clipped(renderer, fonts->large, selected_channel->name, &clip_rect, rect->x + 18, rect->y + 44, COLOR_PANEL_TEXT); + draw_text_clipped(renderer, fonts->medium, selected_channel->program_title, &clip_rect, rect->x + 18, rect->y + 88, COLOR_PANEL_TEXT); + draw_text_clipped(renderer, fonts->small, time_range, &clip_rect, rect->x + 18, rect->y + 124, COLOR_PANEL_TEXT); + draw_text_clipped(renderer, fonts->small, description, &clip_rect, rect->x + 18, rect->y + 148, COLOR_PANEL_TEXT); +} + +static void draw_grid_background(SDL_Renderer *renderer, const SDL_Rect *grid_rect, int row_height, double pixels_per_minute) { + fill_vertical_gradient(renderer, grid_rect, COLOR_GUIDE_TOP, COLOR_GUIDE_BOTTOM); + + for (int minute = 0; minute <= 90; minute += 30) { + int x = GUIDE_X_START + (int) (minute * pixels_per_minute); + set_draw_color(renderer, COLOR_GRID_LINE); + SDL_RenderDrawLine(renderer, x, grid_rect->y, x, grid_rect->y + grid_rect->h); + } + + for (int row = 0; row <= GUIDE_VISIBLE_ROWS; ++row) { + int y = grid_rect->y + row * row_height; + set_draw_color(renderer, COLOR_GRID_LINE); + SDL_RenderDrawLine(renderer, grid_rect->x, y, grid_rect->x + grid_rect->w, y); } } @@ -189,33 +369,28 @@ void ui_render_guide(SDL_Renderer *renderer, Uint64 now_ticks, time_t now_wall) { SDL_Rect full = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; - SDL_Rect header_card = {0, 0, 390, 168}; - SDL_Rect preview = {820, 0, 460, 210}; - SDL_Rect header_row = {0, 210, WINDOW_WIDTH, 42}; - SDL_Rect grid = {0, 252, WINDOW_WIDTH, WINDOW_HEIGHT - 306}; + SDL_Rect info_panel = {0, 0, GUIDE_INFO_WIDTH, GUIDE_INFO_HEIGHT}; + SDL_Rect preview = {WINDOW_WIDTH - GUIDE_PREVIEW_WIDTH, 0, GUIDE_PREVIEW_WIDTH, GUIDE_PREVIEW_HEIGHT}; + SDL_Rect status_bar = {GUIDE_INFO_WIDTH, 0, WINDOW_WIDTH - GUIDE_INFO_WIDTH, GUIDE_STATUS_HEIGHT}; + SDL_Rect header_row = {0, GUIDE_GRID_TOP - GUIDE_STATUS_HEIGHT, WINDOW_WIDTH, GUIDE_STATUS_HEIGHT}; + SDL_Rect grid = {0, GUIDE_GRID_TOP, WINDOW_WIDTH, WINDOW_HEIGHT - GUIDE_GRID_TOP - GUIDE_FOOTER_HEIGHT}; int row_height = grid.h / GUIDE_VISIBLE_ROWS; - int sidebar_width = 240; - int timeline_x = sidebar_width + 8; - int timeline_w = WINDOW_WIDTH - timeline_x - 12; + int timeline_w = WINDOW_WIDTH - GUIDE_X_START - 20; int start_index = active_channel - 2; - char detail[256]; - - fill_rect(renderer, &full, COLOR_NAVY_DARK); - fill_rect(renderer, &header_card, COLOR_PALE_BLUE); - fill_rect(renderer, &preview, COLOR_BLACK); - draw_video(renderer, video_texture, texture_width, texture_height, preview); - fill_rect(renderer, &header_row, COLOR_SLATE); + const Channel *selected_channel = NULL; + double pixels_per_minute = timeline_w / 90.0; if (channels && channels->count > 0 && active_channel >= 0 && active_channel < channels->count) { - const Channel *current = &channels->items[active_channel]; - snprintf(detail, sizeof(detail), "%.0f min - %s", current->duration_seconds / 60.0, current->file_name); - fill_rect(renderer, &(SDL_Rect){0, 0, 390, 38}, COLOR_HIGHLIGHT_YELLOW); - draw_cached_text(renderer, &cache->channels[active_channel].name_medium, 26, 52); - draw_cached_text(renderer, &cache->channels[active_channel].program_medium_dark, 20, 92); - draw_cached_text(renderer, &cache->channels[active_channel].detail_small_dark, 20, 124); - draw_cached_text(renderer, &cache->preview_badge, preview.x + preview.w - 126, preview.y + preview.h - 34); + selected_channel = &channels->items[active_channel]; } + fill_vertical_gradient(renderer, &full, COLOR_GUIDE_TOP, COLOR_GUIDE_BOTTOM); + draw_info_panel(renderer, fonts, selected_channel, &info_panel, app_start_ticks, now_ticks, now_wall); + draw_panel_shadow(renderer, &preview); + fill_rect(renderer, &preview, COLOR_BLACK); + draw_video(renderer, video_texture, texture_width, texture_height, preview); + draw_status_bar(renderer, fonts->medium, selected_channel, &status_bar, now_wall); + if (cache->timeline_label_slot != now_wall / 60) { char label[32]; for (int i = 0; i < 4; ++i) { @@ -226,6 +401,7 @@ void ui_render_guide(SDL_Renderer *renderer, cache->timeline_label_slot = now_wall / 60; } draw_timeline_header_cached(renderer, cache, header_row); + draw_grid_background(renderer, &grid, row_height, pixels_per_minute); if (start_index < 0) { start_index = 0; @@ -237,15 +413,15 @@ void ui_render_guide(SDL_Renderer *renderer, for (int row = 0; row < GUIDE_VISIBLE_ROWS; ++row) { int channel_index = start_index + row; SDL_Rect row_rect = {0, grid.y + row * row_height, WINDOW_WIDTH, row_height}; - SDL_Rect sidebar = {0, row_rect.y, sidebar_width, row_height}; - SDL_Rect timeline_rect = {timeline_x, row_rect.y + 6, timeline_w, row_height - 12}; + SDL_Rect sidebar = {0, row_rect.y, GUIDE_SIDEBAR_WIDTH, row_height}; + SDL_Rect timeline_rect = {GUIDE_X_START, row_rect.y + 6, timeline_w, row_height - 12}; SDL_Rect clip = timeline_rect; int is_selected = channel_index == active_channel; + SDL_Rect inset = {row_rect.x + 4, row_rect.y + 3, row_rect.w - 8, row_rect.h - 6}; - fill_rect(renderer, &row_rect, is_selected ? COLOR_DEEP_BLUE : COLOR_NAVY_DARK); - fill_rect(renderer, &sidebar, is_selected ? COLOR_DEEP_BLUE : COLOR_SLATE); - set_draw_color(renderer, COLOR_PALE_BLUE); - SDL_RenderDrawLine(renderer, row_rect.x, row_rect.y, row_rect.x + row_rect.w, row_rect.y); + fill_rect(renderer, &row_rect, is_selected ? COLOR_SELECTED_ROW : COLOR_UNSELECTED_ROW); + fill_rect_alpha(renderer, &inset, (SDL_Color){255, 255, 255, is_selected ? 12 : 6}); + fill_rect(renderer, &sidebar, is_selected ? COLOR_SELECTED_ROW : COLOR_UNSELECTED_ROW); if (!channels || channel_index >= channels->count) { continue; @@ -254,29 +430,43 @@ void ui_render_guide(SDL_Renderer *renderer, { const Channel *channel = &channels->items[channel_index]; double live_position = channel_live_position_precise(channel, app_start_ticks, now_ticks); - double pixels_per_second = timeline_w / TIMELINE_VISIBLE_SECONDS; - int block_x = timeline_x - (int) (live_position * pixels_per_second); - int block_w = (int) (channel->duration_seconds * pixels_per_second); + time_t guide_view_start_time = now_wall - (30 * 60); + time_t program_start_time = now_wall - (time_t) live_position; + int block_x = GUIDE_X_START + (int) (((double) (program_start_time - guide_view_start_time)) / 60.0 * pixels_per_minute); + int block_w = (int) ((channel->duration_seconds / 60.0) * pixels_per_minute); SDL_Rect block = {block_x, timeline_rect.y + 4, SDL_max(block_w, 48), timeline_rect.h - 8}; + SDL_Rect title_rect = {block.x + 8, block.y + 8, block.w - 16, block.h - 16}; + char title[128]; draw_cached_text(renderer, &cache->channels[channel_index].name_medium, 20, row_rect.y + 12); draw_cached_text(renderer, &cache->channels[channel_index].number_medium, 176, row_rect.y + 12); draw_cached_text(renderer, &cache->channels[channel_index].file_small, 20, row_rect.y + 38); - fill_rect(renderer, &timeline_rect, COLOR_NAVY_DARK); SDL_RenderSetClipRect(renderer, &clip); - fill_rect(renderer, &block, is_selected ? COLOR_HIGHLIGHT_YELLOW : COLOR_DEEP_BLUE); - stroke_rect(renderer, &block, COLOR_PALE_BLUE); - draw_cached_text(renderer, - is_selected ? &cache->channels[channel_index].program_medium_dark - : &cache->channels[channel_index].program_small_light, - block.x + 10, - block.y + 10); + fill_rect(renderer, &block, is_selected ? COLOR_BLOCK_SELECTED : COLOR_BLOCK_UNSELECTED); + stroke_rect(renderer, &block, is_selected ? COLOR_SELECTION_EDGE : COLOR_PALE_BLUE); + fit_text_with_ellipsis(is_selected ? fonts->medium : fonts->small, + channel->program_title, + title_rect.w, + title, + sizeof(title)); + draw_text_clipped(renderer, + is_selected ? fonts->medium : fonts->small, + title, + &title_rect, + title_rect.x, + title_rect.y, + is_selected ? COLOR_TEXT_DARK : COLOR_TEXT_LIGHT); SDL_RenderSetClipRect(renderer, NULL); - stroke_rect(renderer, &timeline_rect, COLOR_SLATE); } } + if (selected_channel && active_channel >= 0 && start_index <= active_channel && active_channel < start_index + GUIDE_VISIBLE_ROWS) { + SDL_Rect highlight = {0, grid.y + (active_channel - start_index) * row_height, WINDOW_WIDTH, row_height}; + stroke_rect(renderer, &highlight, COLOR_SELECTION_EDGE); + SDL_RenderDrawRect(renderer, &(SDL_Rect){GUIDE_X_START, highlight.y + 6, timeline_w, row_height - 12}); + } + draw_footer_legend(renderer, cache, WINDOW_WIDTH, WINDOW_HEIGHT); } @@ -299,14 +489,12 @@ int ui_cache_init(UiCache *cache, SDL_Renderer *renderer, const UiFonts *fonts, if (text_texture_init(&cache->no_media_title, renderer, fonts->large, "Passport-C Media Player", COLOR_TEXT_LIGHT) != 0 || text_texture_init(&cache->no_media_body, renderer, fonts->medium, "No channels found in ./media", COLOR_HIGHLIGHT_YELLOW) != 0 || text_texture_init(&cache->no_media_hint, renderer, fonts->small, "Add MP4 or MKV files to ./media and relaunch.", COLOR_PALE_BLUE) != 0 || - text_texture_init(&cache->timeline_day, renderer, fonts->medium, "FRI 6/30", COLOR_TEXT_DARK) != 0 || text_texture_init(&cache->footer_a, renderer, fonts->medium, "A", COLOR_TEXT_DARK) != 0 || text_texture_init(&cache->footer_time, renderer, fonts->medium, "Time", COLOR_TEXT_DARK) != 0 || text_texture_init(&cache->footer_b, renderer, fonts->medium, "B", COLOR_TEXT_LIGHT) != 0 || text_texture_init(&cache->footer_theme, renderer, fonts->medium, "Theme", COLOR_TEXT_DARK) != 0 || text_texture_init(&cache->footer_c, renderer, fonts->medium, "C", COLOR_TEXT_LIGHT) != 0 || - text_texture_init(&cache->footer_title, renderer, fonts->medium, "Title", COLOR_TEXT_DARK) != 0 || - text_texture_init(&cache->preview_badge, renderer, fonts->medium, "Nick * 56", COLOR_TEXT_LIGHT) != 0) { + text_texture_init(&cache->footer_title, renderer, fonts->medium, "Title", COLOR_TEXT_DARK) != 0) { ui_cache_destroy(cache); return -1; } @@ -350,14 +538,12 @@ void ui_cache_destroy(UiCache *cache) { text_texture_destroy(&cache->no_media_title); text_texture_destroy(&cache->no_media_body); text_texture_destroy(&cache->no_media_hint); - text_texture_destroy(&cache->timeline_day); text_texture_destroy(&cache->footer_a); text_texture_destroy(&cache->footer_time); text_texture_destroy(&cache->footer_b); text_texture_destroy(&cache->footer_theme); text_texture_destroy(&cache->footer_c); text_texture_destroy(&cache->footer_title); - text_texture_destroy(&cache->preview_badge); if (cache->timeline_labels) { for (int i = 0; i < 4; ++i) { diff --git a/src/ui.h b/src/ui.h index 523b4fa..3b3208b 100644 --- a/src/ui.h +++ b/src/ui.h @@ -34,14 +34,12 @@ typedef struct UiCache { UiTextTexture no_media_title; UiTextTexture no_media_body; UiTextTexture no_media_hint; - UiTextTexture timeline_day; UiTextTexture footer_a; UiTextTexture footer_time; UiTextTexture footer_b; UiTextTexture footer_theme; UiTextTexture footer_c; UiTextTexture footer_title; - UiTextTexture preview_badge; UiTextTexture *timeline_labels; time_t timeline_label_slot; UiChannelCache *channels; diff --git a/src/ui.o b/src/ui.o index 8df4c7d6a94c62e1d229217e2c02228c2958b86d..686e1e17f519f3a5f3b78707ecf00e4cd72f2f9f 100644 GIT binary patch literal 20056 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 literal 15504 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