Passed
Push — master ( b1826e...729ce0 )
by manny
01:49
created

racetime_obs.script_setup()   B

Complexity

Conditions 2

Size

Total Lines 46
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 46
rs 8.9919
c 0
b 0
f 0
cc 2
nop 1
1
from threading import Thread
2
from typing import List
3
4
import obspython as obs
5
import racetime_client
6
from helpers.obs_context_manager import data_ar, source_ar, source_list_ar
7
from models.category import Category
8
from models.race import Race
9
from rtgg_obs import RacetimeObs
10
11
rtgg_obs = RacetimeObs()
12
13
14
def script_description():
15
    return (
16
        "<center><p>Select a text source to use as your timer and enter your"
17
        "full username on racetime.gg  (including discriminator). This only"
18
        "needs to be done once.\n\nThen select the race room each race you "
19
        "join and stop worrying about whether you started your timer or not."
20
        "<hr/></p>"
21
    )
22
23
24
def script_load(settings):
25
    rtgg_obs.timer.use_podium_colors = obs.obs_data_get_bool(
26
        settings, "use_podium")
27
28
    race_update_t = Thread(target=rtgg_obs.race_update_thread)
29
    race_update_t.daemon = True
30
    race_update_t.start()
31
32
33
def script_save(settings):
34
    obs.obs_data_set_bool(settings, "use_podium",
35
                          rtgg_obs.timer.use_podium_colors)
36
37
38
def script_update(settings):
39
    script_update_setup_settings(settings)
40
    script_update_timer_settings(settings)
41
    script_update_coop_settings(settings)
42
    script_update_qualifier_settings(settings)
43
44
45
def script_update_qualifier_settings(settings):
46
    rtgg_obs.qualifier.enabled = obs.obs_data_get_bool(
47
        settings, "use_qualifier")
48
    rtgg_obs.qualifier.qualifier_cutoff = obs.obs_data_get_int(
49
        settings, "qualifier_cutoff")
50
    rtgg_obs.logger.debug(
51
        f"qualifier_cutoff is {rtgg_obs.qualifier.qualifier_cutoff}")
52
    rtgg_obs.qualifier.par_source = obs.obs_data_get_string(
53
        settings, "qualifier_par_source")
54
    rtgg_obs.qualifier.score_source = obs.obs_data_get_string(
55
        settings, "qualifier_score_source")
56
57
58
def script_update_coop_settings(settings):
59
    rtgg_obs.coop.enabled = obs.obs_data_get_bool(settings, "use_coop")
60
    rtgg_obs.coop.partner = obs.obs_data_get_string(settings, "coop_partner")
61
    rtgg_obs.coop.opponent1 = obs.obs_data_get_string(
62
        settings, "coop_opponent1")
63
    rtgg_obs.coop.opponent2 = obs.obs_data_get_string(
64
        settings, "coop_opponent2")
65
    rtgg_obs.coop.source = obs.obs_data_get_string(settings, "coop_source")
66
    rtgg_obs.coop.label_source = obs.obs_data_get_string(
67
        settings, "coop_label")
68
69
70
def script_update_timer_settings(settings):
71
    obs.timer_remove(update_sources)
72
73
    rtgg_obs.timer.use_podium_colors = obs.obs_data_get_bool(
74
        settings, "use_podium")
75
    rtgg_obs.timer.pre_color = obs.obs_data_get_int(settings, "pre_color")
76
    rtgg_obs.timer.first_color = obs.obs_data_get_int(settings, "first_color")
77
    rtgg_obs.timer.second_color = obs.obs_data_get_int(
78
        settings, "second_color")
79
    rtgg_obs.timer.third_color = obs.obs_data_get_int(settings, "third_color")
80
    rtgg_obs.timer.racing_color = obs.obs_data_get_int(
81
        settings, "racing_color")
82
    rtgg_obs.timer.finished_color = obs.obs_data_get_int(
83
        settings, "finished_color")
84
85
    if rtgg_obs.selected_race != "":
86
        rtgg_obs.timer.enabled = True
87
    else:
88
        rtgg_obs.timer.enabled = False
89
90
    if rtgg_obs.timer.is_enabled():
91
        obs.timer_add(update_sources, 100)
92
    rtgg_obs.logger.debug(f"timer.enabled is {rtgg_obs.timer.enabled}")
93
    rtgg_obs.logger.debug(f"timer.source_name is {rtgg_obs.timer.source_name}")
94
    rtgg_obs.logger.debug(f"selected_race is {rtgg_obs.selected_race}")
95
96
97
def script_update_setup_settings(settings):
98
    rtgg_obs.update_logger(
99
        obs.obs_data_get_bool(settings, "enable_log"),
100
        obs.obs_data_get_bool(settings, "log_to_file"),
101
        obs.obs_data_get_string(settings, "log_file"),
102
        obs.obs_data_get_string(settings, "log_level")
103
    )
104
105
    rtgg_obs.full_name = obs.obs_data_get_string(settings, "username")
106
107
    rtgg_obs.timer.source_name = obs.obs_data_get_string(settings, "source")
108
109
    rtgg_obs.selected_race = obs.obs_data_get_string(settings, "race")
110
    rtgg_obs.category = obs.obs_data_get_string(settings, "category_filter")
111
112
113
def script_defaults(settings):
114
    obs.obs_data_set_default_string(settings, "race_info", "Race info")
115
    obs.obs_data_set_default_string(settings, "race", "")
116
    obs.obs_data_set_default_int(settings, "qualifier_cutoff", 3)
117
118
119
def script_properties():
120
    props = obs.obs_properties_create()
121
    script_setup(props)
122
    script_timer_settings(props)
123
    script_coop_settings(props)
124
    script_qualifier_settings(props)
125
126
    return props
127
128
129
def script_qualifier_settings(props):
130
    p = obs.obs_properties_add_bool(
131
            props, "use_qualifier",
132
            "Display race results as tournament qualifier?"
133
        )
134
    obs.obs_property_set_modified_callback(p, qualifier_toggled)
135
    qualifier_group = obs.obs_properties_create()
136
    obs.obs_properties_add_group(
137
            props, "qualifier_group", "Qualifier Mode",
138
            obs.OBS_GROUP_NORMAL, qualifier_group
139
        )
140
    obs.obs_property_set_visible(
141
        obs.obs_properties_get(props, "qualifier_group"),
142
        rtgg_obs.qualifier.enabled
143
    )
144
    p = obs.obs_properties_add_int_slider(
145
            qualifier_group, "qualifier_cutoff",
146
            "Use Top X as par time, where X=", 3, 10, 1
147
        )
148
    p = obs.obs_properties_add_list(
149
            qualifier_group,
150
            "qualifier_par_source",
151
            "Qualifier Par Time Source",
152
            obs.OBS_COMBO_TYPE_EDITABLE,
153
            obs.OBS_COMBO_FORMAT_STRING
154
        )
155
    fill_source_list(p)
156
    p = obs.obs_properties_add_list(
157
            qualifier_group,
158
            "qualifier_score_source",
159
            "Qualifier Score Source",
160
            obs.OBS_COMBO_TYPE_EDITABLE,
161
            obs.OBS_COMBO_FORMAT_STRING
162
        )
163
    fill_source_list(p)
164
165
166
def script_coop_settings(props):
167
    p = obs.obs_properties_add_bool(
168
        props, "use_coop", "Display coop information?")
169
    obs.obs_property_set_modified_callback(p, coop_toggled)
170
    coop_group = obs.obs_properties_create()
171
    obs.obs_properties_add_group(
172
        props, "coop_group", "Co-op Mode", obs.OBS_GROUP_NORMAL, coop_group
173
    )
174
    obs.obs_property_set_visible(
175
        obs.obs_properties_get(props, "coop_group"), rtgg_obs.coop.enabled)
176
    p = obs.obs_properties_add_list(
177
        coop_group, "coop_partner", "Co-op Partner",
178
        obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
179
    )
180
    p = obs.obs_properties_add_list(
181
        coop_group, "coop_opponent1", "Co-op Opponent 1",
182
        obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
183
    )
184
    p = obs.obs_properties_add_list(
185
        coop_group, "coop_opponent2", "Co-op Opponent 2",
186
        obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
187
    )
188
    fill_coop_entrant_lists(props)
189
    p = obs.obs_properties_add_list(
190
        coop_group, "coop_source", "Coop Text Source",
191
        obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING
192
    )
193
    obs.obs_property_set_long_description(p, (
194
        "This text source will display the time that the last racer needs to"
195
        " finish for their team to win"
196
    ))
197
    fill_source_list(p)
198
    p = obs.obs_properties_add_list(
199
        coop_group, "coop_label", "Coop Label Text Source",
200
        obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING
201
    )
202
    obs.obs_property_set_long_description(p, (
203
        "This text source will be use to display a label such as "
204
        "\'<PartnerName> needs to finish before\' based on who the last racer"
205
        " is"
206
    ))
207
    fill_source_list(p)
208
209
210
def script_timer_settings(props):
211
    p = obs.obs_properties_add_bool(
212
        props, "use_podium", "Use custom color for podium finishes?")
213
    obs.obs_property_set_modified_callback(p, podium_toggled)
214
    podium_group = obs.obs_properties_create()
215
    obs.obs_properties_add_group(
216
        props, "podium_group", "Podium Colors",
217
        obs.OBS_GROUP_NORMAL, podium_group
218
    )
219
    obs.obs_property_set_visible(obs.obs_properties_get(
220
        props, "podium_group"), rtgg_obs.timer.use_podium_colors)
221
    obs.obs_properties_add_color(podium_group, "pre_color", "Pre-race:")
222
    obs.obs_properties_add_color(podium_group, "racing_color", "Still racing:")
223
    obs.obs_properties_add_color(podium_group, "first_color", "1st place:")
224
    obs.obs_properties_add_color(podium_group, "second_color", "2nd place:")
225
    obs.obs_properties_add_color(podium_group, "third_color", "3rd place:")
226
    obs.obs_properties_add_color(
227
        podium_group, "finished_color", "After podium:")
228
229
230
def script_setup(props):
231
    setup_group = obs.obs_properties_create()
232
    obs.obs_properties_add_group(
233
        props, "initial_setup", "Initial setup - Check to make changes",
234
        obs.OBS_GROUP_CHECKABLE, setup_group
235
    )
236
    p = obs.obs_properties_add_list(
237
        setup_group, "source", "Text Source", obs.OBS_COMBO_TYPE_EDITABLE,
238
        obs.OBS_COMBO_FORMAT_STRING
239
    )
240
    fill_source_list(p)
241
    obs.obs_properties_add_text(
242
        setup_group, "username", "Username", obs.OBS_TEXT_DEFAULT)
243
    logging = obs.obs_properties_add_bool(
244
        setup_group, "enable_log", "Enable logging")
245
    log_levels = obs.obs_properties_add_list(
246
        setup_group, "log_level", "Log lever", obs.OBS_COMBO_TYPE_LIST,
247
        obs.OBS_COMBO_FORMAT_STRING
248
    )
249
    obs.obs_property_list_add_string(log_levels, "Error", "Error")
250
    obs.obs_property_list_add_string(log_levels, "Debug", "Debug")
251
    obs.obs_property_list_add_string(log_levels, "Info", "Info")
252
    obs.obs_property_set_long_description(
253
        logging, "Generally, only log errors unless you are developing or are "
254
        "trying to find a specific problem."
255
    )
256
    obs.obs_properties_add_bool(setup_group, "log_to_file", "Log to file?")
257
    category_list = obs.obs_properties_add_list(
258
        props, "category_filter", "Filter by Category",
259
        obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
260
    )
261
    race_list = obs.obs_properties_add_list(
262
        props, "race", "Race",
263
        obs.OBS_COMBO_TYPE_LIST, obs.OBS_COMBO_FORMAT_STRING
264
        )
265
    obs.obs_property_set_modified_callback(race_list, new_race_selected)
266
    obs.obs_property_set_modified_callback(
267
        category_list, new_category_selected)
268
269
    p = obs.obs_properties_add_text(
270
        props, "race_info", "Race Desc", obs.OBS_TEXT_MULTILINE)
271
    obs.obs_property_set_enabled(p, False)
272
273
    refresh = obs.obs_properties_add_button(
274
        props, "button", "Refresh", lambda *props: None)
275
    obs.obs_property_set_modified_callback(refresh, refresh_pressed)
276
277
278
def refresh_pressed(props, prop, *args, **kwargs):
279
    fill_source_list(obs.obs_properties_get(props, "source"))
280
    fill_source_list(obs.obs_properties_get(props, "coop_label"))
281
    fill_source_list(obs.obs_properties_get(props, "coop_text"))
282
    fill_source_list(obs.obs_properties_get(props, "qualifier_par_source"))
283
    fill_source_list(obs.obs_properties_get(props, "qualifier_score_source"))
284
    fill_race_list(obs.obs_properties_get(props, "race"),
285
                   obs.obs_properties_get(props, "category_filter"))
286
    if rtgg_obs.race is not None:
287
        rtgg_obs.coop.update_coop_text(rtgg_obs.race, rtgg_obs.full_name)
288
        rtgg_obs.qualifier.update_qualifier_text(
289
            rtgg_obs.race, rtgg_obs.full_name)
290
    return True
291
292
293
def new_race_selected(props, prop, settings):
294
    rtgg_obs.selected_race = obs.obs_data_get_string(settings, "race")
295
    r = racetime_client.get_race_by_name(rtgg_obs.selected_race)
296
    if r is not None:
297
        rtgg_obs.race = r
298
        rtgg_obs.coop.update_coop_text(rtgg_obs.race, rtgg_obs.full_name)
299
        rtgg_obs.qualifier.update_qualifier_text(
300
            rtgg_obs.race, rtgg_obs.full_name)
301
        rtgg_obs.logger.info(f"new race selected: {rtgg_obs.race}")
302
        obs.obs_data_set_default_string(settings, "race_info", r.info)
303
        fill_coop_entrant_lists(props)
304
    else:
305
        obs.obs_data_set_default_string(
306
            settings, "race_info", "Race not found")
307
308
    rtgg_obs.race_changed = True
309
    return True
310
311
312
def new_category_selected(props, prop, settings):
313
    rtgg_obs.category = obs.obs_data_get_string(settings, "category_filter")
314
    rtgg_obs.logger.info(f"new category selected: {rtgg_obs.category}")
315
    fill_race_list(obs.obs_properties_get(props, "race"), prop)
316
    return True
317
318
319
def podium_toggled(props, prop, settings):
320
    vis = obs.obs_data_get_bool(settings, "use_podium")
321
    obs.obs_property_set_visible(
322
        obs.obs_properties_get(props, "podium_group"), vis)
323
    return True
324
325
326
def coop_toggled(props, prop, settings):
327
    vis = obs.obs_data_get_bool(settings, "use_coop")
328
    obs.obs_property_set_visible(
329
        obs.obs_properties_get(props, "coop_group"), vis)
330
    return True
331
332
333
def qualifier_toggled(props, prop, settings):
334
    vis = obs.obs_data_get_bool(settings, "use_qualifier")
335
    obs.obs_property_set_visible(
336
        obs.obs_properties_get(props, "qualifier_group"), vis)
337
    return True
338
339
340
def update_sources():
341
    if rtgg_obs.race is not None:
342
        if rtgg_obs.timer.is_enabled():
343
            color, time = rtgg_obs.timer.get_timer_text(
344
                rtgg_obs.race, rtgg_obs.full_name)
345
            set_source_text(rtgg_obs.timer.source_name, time, color)
346
        if rtgg_obs.coop.is_enabled():
347
            set_source_text(rtgg_obs.coop.source, rtgg_obs.coop.text, None)
348
            set_source_text(rtgg_obs.coop.label_source,
349
                            rtgg_obs.coop.label_text, None)
350
        if rtgg_obs.qualifier.is_enabled():
351
            set_source_text(rtgg_obs.qualifier.par_source,
352
                            rtgg_obs.qualifier.par_text, None)
353
            set_source_text(rtgg_obs.qualifier.score_source,
354
                            rtgg_obs.qualifier.entrant_score, None)
355
        pass
356
357
358
def fill_source_list(p):
359
    obs.obs_property_list_clear(p)
360
    obs.obs_property_list_add_string(p, "", "")
361
    with source_list_ar() as sources:
362
        if sources is not None:
363
            for source in sources:
364
                source_id = obs.obs_source_get_unversioned_id(source)
365
                if (
366
                    source_id == "text_gdiplus" or
367
                    source_id == "text_ft2_source"
368
                ):
369
                    name = obs.obs_source_get_name(source)
370
                    obs.obs_property_list_add_string(p, name, name)
371
372
373
def fill_race_list(race_list, category_list):
374
    obs.obs_property_list_clear(race_list)
375
    obs.obs_property_list_clear(category_list)
376
    obs.obs_property_list_add_string(category_list, "All", "All")
377
378
    obs.obs_property_list_add_string(race_list, "", "")
379
    races = racetime_client.get_races()
380
    if races is not None:
381
        fill_category_list(category_list, races)
382
        for race in filter_races_by_category(races, rtgg_obs.category):
383
            obs.obs_property_list_add_string(race_list, race.name, race.name)
384
385
386
def fill_category_list(category_list, races: List[Race]):
387
    categories = []
388
    for race in races:
389
        if race.category.name not in categories:
390
            categories.append(race.category.name)
391
            obs.obs_property_list_add_string(
392
                category_list, race.category.name, race.category.name)
393
394
395
def filter_races_by_category(races: List[Race], category: Category) -> Race:
396
    for race in races:
397
        if (
398
            rtgg_obs.category == "" or rtgg_obs.category == "All" or
399
            race.category.name == rtgg_obs.category
400
        ):
401
            yield race
402
403
404
def fill_coop_entrant_lists(props):
405
    fill_entrant_list(
406
        rtgg_obs.race, obs.obs_properties_get(props, "coop_partner"))
407
    fill_entrant_list(rtgg_obs.race, obs.obs_properties_get(
408
        props, "coop_opponent1"))
409
    fill_entrant_list(rtgg_obs.race, obs.obs_properties_get(
410
        props, "coop_opponent2"))
411
412
413
def fill_entrant_list(race, entrant_list):
414
    obs.obs_property_list_clear(entrant_list)
415
    obs.obs_property_list_add_string(entrant_list, "", "")
416
    if race is not None:
417
        for entrant in race.entrants:
418
            obs.obs_property_list_add_string(
419
                entrant_list, entrant.user.full_name, entrant.user.full_name)
420
421
# copied and modified from scripted-text.py by UpgradeQ
422
423
424
def set_source_text(source_name: str, text: str, color: int):
425
    with source_ar(source_name) as source, data_ar() as settings:
426
        obs.obs_data_set_string(settings, "text", text)
427
        source_id = obs.obs_source_get_unversioned_id(source)
428
        if color is not None:
429
            if source_id == "text_gdiplus":
430
                obs.obs_data_set_int(settings, "color", color)  # colored text
431
432
            # freetype2 is BGR, should be reversed for getting correct color
433
            else:
434
                number = "".join(hex(color)[2:])
435
                color = int("0xff" f"{number}", base=16)
436
                obs.obs_data_set_int(settings, "color1", color)
437
438
        obs.obs_source_update(source, settings)
439