mirror of
https://github.com/cyberboy666/r_e_c_u_r.git
synced 2025-12-05 16:00:06 +01:00
added a new LOOP_TYPE parallel
This commit is contained in:
40
actions.py
40
actions.py
@@ -84,8 +84,19 @@ class Actions(object):
|
||||
self.display.browser_menu.generate_browser_list()
|
||||
|
||||
def _load_this_slot_into_next_player(self, slot):
|
||||
if self.data.update_next_slot_number(slot):
|
||||
self.video_driver.reload_next_player()
|
||||
### load next player for seamless type otherwise respect player mode
|
||||
if self.data.settings['sampler']['LOOP_TYPE']['value'] == 'seamless':
|
||||
if self.data.update_next_slot_number(slot):
|
||||
self.video_driver.reload_next_player()
|
||||
else:
|
||||
if self.data.player_mode == 'next':
|
||||
if self.data.update_next_slot_number(slot):
|
||||
self.video_driver.reload_next_player()
|
||||
else:
|
||||
if self.data.update_next_slot_number(slot, is_current=True):
|
||||
self.video_driver.reload_current_player()
|
||||
|
||||
|
||||
|
||||
def load_slot_0_into_next_player(self):
|
||||
self._load_this_slot_into_next_player(0)
|
||||
@@ -118,7 +129,13 @@ class Actions(object):
|
||||
self._load_this_slot_into_next_player(9)
|
||||
|
||||
def switch_to_next_player(self):
|
||||
self.video_driver.switch_players_and_start_video()
|
||||
if self.data.settings['sampler']['LOOP_TYPE']['value'] == 'seamless':
|
||||
self.video_driver.switch_players_and_start_video()
|
||||
else:
|
||||
self.video_driver.current_player.toggle_show()
|
||||
if self.video_driver.current_player.show_toggle_on == self.video_driver.next_player.show_toggle_on:
|
||||
self.video_driver.next_player.toggle_show()
|
||||
|
||||
|
||||
def cycle_display_mode(self):
|
||||
if self.data.settings['other']['VIDEO_BACKEND']['value'] == 'openframeworks':
|
||||
@@ -315,6 +332,7 @@ class Actions(object):
|
||||
self.change_hdmi_settings(setting_value)
|
||||
elif setting_value == 'composite':
|
||||
self.change_composite_setting(setting_value)
|
||||
self.restart_openframeworks()
|
||||
|
||||
def change_hdmi_settings(self, setting_value):
|
||||
if self.data.settings['video']['OUTPUT']['value'] == 'hdmi':
|
||||
@@ -351,6 +369,7 @@ class Actions(object):
|
||||
if self.data.settings['other']['VIDEO_BACKEND']['value'] == 'openframeworks':
|
||||
self.openframeworks_process = subprocess.Popen(['/home/pi/openFrameworks/apps/myApps/c_o_n_j_u_r/bin/c_o_n_j_u_r'])
|
||||
print('conjur pid is {}'.format(self.openframeworks_process.pid))
|
||||
|
||||
def exit_openframeworks(self):
|
||||
self.video_driver.osc_client.send_message("/exit", True)
|
||||
|
||||
@@ -367,15 +386,15 @@ class Actions(object):
|
||||
|
||||
|
||||
def change_composite_setting(self, setting_value):
|
||||
mode = self.data.settings['video']['COMPOSITE_TYPE']['value']
|
||||
aspect = self.data.settings['video']['COMPOSITE_RATIO']['value']
|
||||
progressive = ''
|
||||
if self.data.settings['video']['COMPOSITE_PROGRESSIVE']['value'] == 'on':
|
||||
progressive = 'p'
|
||||
if setting_value == 'composite':
|
||||
mode = self.data.settings['video']['COMPOSITE_TYPE']['value']
|
||||
aspect = self.data.settings['video']['COMPOSITE_RATIO']['value']
|
||||
progressive = ''
|
||||
if self.data.settings['video']['COMPOSITE_PROGRESSIVE']['value'] == 'on':
|
||||
progressive = 'p'
|
||||
subprocess.call(['tvservice --sdtvon="{} {} {}"'.format(mode, aspect, progressive)],shell=True)
|
||||
self._refresh_frame_buffer()
|
||||
self.persist_composite_setting(mode, progressive, aspect)
|
||||
self.persist_composite_setting(mode, progressive, aspect)
|
||||
|
||||
@staticmethod
|
||||
def _refresh_frame_buffer():
|
||||
@@ -592,6 +611,9 @@ class Actions(object):
|
||||
self.message_handler.set_message('INFO', 'finished compiling!')
|
||||
self.restart_the_program()
|
||||
|
||||
def shutdown_pi(self):
|
||||
subprocess.call(['sudo', 'shutdown', '-h', 'now'])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ class Data(object):
|
||||
|
||||
BANK_DATA_JSON = 'display_data.json'
|
||||
NEXT_BANKSLOT_JSON = 'next_bankslot_number.json'
|
||||
CURRENT_BANKSLOT_JSON = 'current_bankslot_number.json'
|
||||
SETTINGS_JSON = 'settings.json'
|
||||
DEFAULT_SETTINGS_JSON = 'settings_default.json'
|
||||
KEYPAD_MAPPING_JSON = 'keypad_action_mapping.json'
|
||||
@@ -49,6 +50,9 @@ class Data(object):
|
||||
self.next_bankslot = '0-0'
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.NEXT_BANKSLOT_JSON):
|
||||
self.next_bankslot = self._read_json(self.NEXT_BANKSLOT_JSON)
|
||||
self.current_bankslot = '0-0'
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.CURRENT_BANKSLOT_JSON):
|
||||
self.next_bankslot = self._read_json(self.CURRENT_BANKSLOT_JSON)
|
||||
|
||||
self.settings = self._read_json(self.DEFAULT_SETTINGS_JSON)
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON):
|
||||
@@ -117,13 +121,17 @@ class Data(object):
|
||||
self._update_json(self.BANK_DATA_JSON, self.bank_data)
|
||||
self.bank_number = (self.bank_number+amount)%(len(self.bank_data))
|
||||
|
||||
def update_next_slot_number(self, new_value):
|
||||
def update_next_slot_number(self, new_value, is_current=False):
|
||||
if self.bank_data[self.bank_number][new_value]['location'] == '':
|
||||
self.message_handler.set_message('INFO', 'the slot you pressed is empty')
|
||||
return False
|
||||
elif self.is_this_path_broken(self.bank_data[self.bank_number][new_value]['location']):
|
||||
self.message_handler.set_message('INFO', 'no device found for this slot')
|
||||
return False
|
||||
elif is_current:
|
||||
self.current_bankslot = '{}-{}'.format(self.bank_number,new_value)
|
||||
self._update_json(self.CURRENT_BANKSLOT_JSON,self.current_bankslot)
|
||||
return True
|
||||
else:
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,new_value)
|
||||
self._update_json(self.NEXT_BANKSLOT_JSON,self.next_bankslot)
|
||||
@@ -148,9 +156,13 @@ class Data(object):
|
||||
except ValueError:
|
||||
return False , '*'
|
||||
|
||||
def get_next_context(self):
|
||||
def get_next_context(self, is_current=False):
|
||||
######## loads the slot details, uses settings to modify them and then set next slot number ########
|
||||
bank_num , slot_num = self.split_bankslot_number(self.next_bankslot)
|
||||
if is_current:
|
||||
bankslot_number = self.current_bankslot
|
||||
else:
|
||||
bankslot_number = self.next_bankslot
|
||||
bank_num , slot_num = self.split_bankslot_number(bankslot_number)
|
||||
next_slot_details = self.bank_data[bank_num][slot_num]
|
||||
start_value = next_slot_details['start']
|
||||
end_value = next_slot_details['end']
|
||||
@@ -160,9 +172,9 @@ class Data(object):
|
||||
|
||||
context = dict(location=next_slot_details['location'], name=next_slot_details['name'],
|
||||
length=next_slot_details['length'], rate=next_slot_details['rate'], start=start_value, end=end_value,
|
||||
bankslot_number=self.next_bankslot)
|
||||
bankslot_number=bankslot_number)
|
||||
|
||||
self._update_next_bankslot_value(slot_num)
|
||||
self._update_next_bankslot_value(slot_num, is_current)
|
||||
return context
|
||||
|
||||
def _overwrite_values_with_sampler_settings(self, start, end, length):
|
||||
@@ -192,7 +204,7 @@ class Data(object):
|
||||
|
||||
return new_start, new_end
|
||||
|
||||
def _update_next_bankslot_value(self, slot_num):
|
||||
def _update_next_bankslot_value(self, slot_num, is_current=False):
|
||||
next_setting = self.settings['sampler']['LOAD_NEXT']['value']
|
||||
loaded_slots = self._get_list_of_loaded_slots_in_current_bank()
|
||||
if loaded_slots:
|
||||
@@ -203,8 +215,12 @@ class Data(object):
|
||||
else:
|
||||
next_slot = slot_num
|
||||
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
self._update_json(self.NEXT_BANKSLOT_JSON,self.next_bankslot)
|
||||
if is_current:
|
||||
self.current_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
self._update_json(self.CURRENT_BANKSLOT_JSON,self.current_bankslot)
|
||||
else:
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
self._update_json(self.NEXT_BANKSLOT_JSON,self.next_bankslot)
|
||||
|
||||
def _get_list_of_loaded_slots_in_current_bank(self):
|
||||
list_of_loaded_slots = []
|
||||
|
||||
1
json_objects/current_bankslot_number.json
Normal file
1
json_objects/current_bankslot_number.json
Normal file
@@ -0,0 +1 @@
|
||||
"1-1"
|
||||
@@ -102,5 +102,71 @@
|
||||
},
|
||||
"note_on 90": {
|
||||
"DEFAULT": ["load_slot_9_into_next_player","quit_the_program"]
|
||||
},
|
||||
"note_on 0": {
|
||||
"NAV_BROWSER": ["move_browser_selection_up"],
|
||||
"PLAYER": ["seek_back_on_player"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
},
|
||||
"note_on 36": {
|
||||
"NAV_BROWSER": ["move_browser_selection_down"],
|
||||
"PLAYER": ["seek_forward_on_player"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
},
|
||||
"note_on 37": {
|
||||
"NAV_BROWSER": ["enter_on_browser_selection"],
|
||||
"PLAYER": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"NAV_SETTINGS": ["enter_on_settings_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"]
|
||||
},
|
||||
"note_on 38": {
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"]
|
||||
},
|
||||
"note_on 39": {
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"]
|
||||
},
|
||||
"note_on 40": {
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"]
|
||||
},
|
||||
"note_on 41": {
|
||||
"DEFAULT": ["toggle_capture_preview", "toggle_capture_recording"]},
|
||||
"note_on 42": {
|
||||
"DEFAULT": ["cycle_display_mode"]
|
||||
},
|
||||
"note_on 43": {
|
||||
"DEFAULT": ["toggle_function"]
|
||||
},
|
||||
"note_on 44": {
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"]
|
||||
},
|
||||
"note_on 45": {
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"]
|
||||
},
|
||||
"note_on 46": {
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"]
|
||||
},
|
||||
"note_on 47": {
|
||||
"DEFAULT": ["load_slot_3_into_next_player"]
|
||||
},
|
||||
"note_on 48": {
|
||||
"DEFAULT": ["load_slot_4_into_next_player"]
|
||||
},
|
||||
"note_on 49": {
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"]
|
||||
},
|
||||
"note_on 50": {
|
||||
"DEFAULT": ["load_slot_6_into_next_player"]
|
||||
},
|
||||
"note_on 51": {
|
||||
"DEFAULT": ["load_slot_7_into_next_player"]
|
||||
},
|
||||
"note_on 52": {
|
||||
"DEFAULT": ["load_slot_8_into_next_player"]
|
||||
},
|
||||
"note_on 53": {
|
||||
"DEFAULT": ["load_slot_9_into_next_player","quit_the_program"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,11 @@
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"SHUTDOWN_PI": {
|
||||
"action": "shutdown_pi",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"OF_SCREEN_SIZE": {
|
||||
"action": "toggle_of_screen_size",
|
||||
"options": [
|
||||
@@ -198,6 +203,14 @@
|
||||
],
|
||||
"value": "off"
|
||||
},
|
||||
"LOOP_TYPE": {
|
||||
"action": "update_video_settings",
|
||||
"options": [
|
||||
"seamless",
|
||||
"parallel"
|
||||
],
|
||||
"value": "parallel"
|
||||
},
|
||||
"FIXED_LENGTH": {
|
||||
"action": "set_fixed_length",
|
||||
"options": [],
|
||||
|
||||
@@ -37,9 +37,11 @@ class AnalogInput(object):
|
||||
|
||||
def poll_analog_inputs(self):
|
||||
if self.data.settings['other']['ANALOG_INPUT']['value'] == 'enabled':
|
||||
|
||||
for i in range(0,8):
|
||||
if str(i) in self.analog_mappings:
|
||||
this_reading = self.analog_input.read_adc(i)
|
||||
#print(str(this_reading))
|
||||
if abs(this_reading - self.last_readings[i]) > 10:
|
||||
#print('the diff is {}'.format(this_reading - self.last_readings[i]))
|
||||
self.run_action_for_mapped_channel(i, this_reading)
|
||||
|
||||
@@ -62,7 +62,7 @@ class MidiInput(object):
|
||||
## trying to only let through step cc messages to increase response time
|
||||
elif message_dict['type'] == 'control_change':
|
||||
control_number = message_dict['control']
|
||||
#print('control number is {} , cc_dict.keys is {}'.format(control_number, cc_dict.keys() ))
|
||||
print('control number is {} , cc_dict.keys is {}'.format(control_number, cc_dict.keys() ))
|
||||
if not control_number in cc_dict.keys():
|
||||
cc_dict[control_number] = message_dict['value']
|
||||
self.on_midi_message(message_dict)
|
||||
@@ -76,7 +76,8 @@ class MidiInput(object):
|
||||
self.on_midi_message(message_dict)
|
||||
#print(cc_dict)
|
||||
|
||||
else:
|
||||
else:
|
||||
print(message_dict)
|
||||
self.on_midi_message(message_dict)
|
||||
if i > 0:
|
||||
pass
|
||||
|
||||
@@ -17,17 +17,18 @@ class AltVideoPlayer:
|
||||
self.location = ''
|
||||
self.load_attempts = 0
|
||||
self.alpha = 0
|
||||
self.show_toggle_on = True
|
||||
### new stuff
|
||||
self.client = osc_client
|
||||
|
||||
self.position = -1
|
||||
|
||||
|
||||
def try_load(self, layer):
|
||||
def try_load(self, layer, is_current=False):
|
||||
load_attempts = 0
|
||||
while(load_attempts < 2):
|
||||
load_attempts = load_attempts + 1
|
||||
if self.load(layer):
|
||||
if self.load(layer, is_current):
|
||||
print('load success')
|
||||
return True
|
||||
else:
|
||||
@@ -37,8 +38,8 @@ class AltVideoPlayer:
|
||||
return False
|
||||
|
||||
|
||||
def load(self, layer):
|
||||
self.get_context_for_player()
|
||||
def load(self, layer, is_current=False):
|
||||
self.get_context_for_player(is_current)
|
||||
print('the location is {}'.format(self.location))
|
||||
if self.location == '':
|
||||
self.status = 'EMPTY'
|
||||
@@ -75,10 +76,10 @@ class AltVideoPlayer:
|
||||
|
||||
|
||||
|
||||
def reload(self, layer):
|
||||
def reload(self, layer, is_current=False):
|
||||
self.exit()
|
||||
self.player_running = False
|
||||
self.try_load(layer)
|
||||
self.try_load(layer, is_current)
|
||||
|
||||
def is_loaded(self):
|
||||
return self.status == 'LOADED'
|
||||
@@ -86,8 +87,8 @@ class AltVideoPlayer:
|
||||
def is_finished(self):
|
||||
return self.status == 'FINISHED'
|
||||
|
||||
def get_context_for_player(self):
|
||||
next_context = self.data.get_next_context()
|
||||
def get_context_for_player(self, is_current=False):
|
||||
next_context = self.data.get_next_context(is_current)
|
||||
print('the context is {}'.format(next_context))
|
||||
self.location = next_context['location']
|
||||
self.total_length = next_context['length']
|
||||
|
||||
@@ -14,6 +14,7 @@ class VideoDriver(object):
|
||||
self.in_first_load_cycle = False
|
||||
self.in_current_playing_cycle = False
|
||||
self.in_next_load_cycle = False
|
||||
self.in_parallel_cycle = False
|
||||
|
||||
self.layer = self.MAX_LAYER
|
||||
|
||||
@@ -22,13 +23,19 @@ class VideoDriver(object):
|
||||
self.next_player = None
|
||||
self.reset_all_players()
|
||||
|
||||
self.root.after(self.delay, self.begin_playing)
|
||||
self.print_status()
|
||||
self.update_video_settings()
|
||||
if self.loop_parallel:
|
||||
self.in_parallel_cycle = True
|
||||
self.root.after(self.delay, self.begin_playing_parallel)
|
||||
else:
|
||||
self.root.after(self.delay, self.begin_playing)
|
||||
self.print_status()
|
||||
|
||||
|
||||
|
||||
def update_video_settings(self):
|
||||
self.switch_on_finish = self.data.settings['sampler']['ON_FINISH']['value'] == 'switch'
|
||||
self.loop_parallel = self.data.settings['sampler']['LOOP_TYPE']['value'] == 'parallel'
|
||||
|
||||
def get_next_layer_value(self):
|
||||
if self.layer > 0:
|
||||
@@ -39,6 +46,7 @@ class VideoDriver(object):
|
||||
return self.layer
|
||||
|
||||
def print_status(self):
|
||||
print('self.loop_parallel: ', self.loop_parallel, 'self.in_parallel_cycle :', self.in_parallel_cycle)
|
||||
print('l({}):{}, c({}):{}, n({}):{}'.format(self.last_player.name, self.last_player.status, self.current_player.name, \
|
||||
self.current_player.status, self.next_player.name, self.next_player.status,))
|
||||
self.root.after(1000,self.print_status)
|
||||
@@ -59,11 +67,11 @@ class VideoDriver(object):
|
||||
self.root.after(self.delay, self.wait_for_first_load)
|
||||
|
||||
def switch_players_and_start_video(self):
|
||||
self.in_first_load_cycle = False
|
||||
self.in_current_playing_cycle = False
|
||||
self.in_next_load_cycle = True
|
||||
self.in_first_load_cycle = False
|
||||
self.in_current_playing_cycle = False
|
||||
self.in_next_load_cycle = True
|
||||
|
||||
self.switch_players()
|
||||
self.switch_players()
|
||||
|
||||
def switch_players(self):
|
||||
temp_player = self.last_player
|
||||
@@ -88,7 +96,12 @@ class VideoDriver(object):
|
||||
if self.switch_on_finish:
|
||||
self.switch_if_next_is_loaded()
|
||||
else:
|
||||
self.root.after(self.delay, self.wait_for_next_cycle)
|
||||
if self.loop_parallel:
|
||||
self.in_current_playing_cycle = False
|
||||
self.in_parallel_cycle = True
|
||||
self.root.after(self.delay, self.begin_playing_parallel)
|
||||
else:
|
||||
self.root.after(self.delay, self.wait_for_next_cycle)
|
||||
|
||||
def switch_if_next_is_loaded(self):
|
||||
if self.in_next_load_cycle:
|
||||
@@ -135,6 +148,9 @@ class VideoDriver(object):
|
||||
def reload_next_player(self):
|
||||
self.next_player.reload(self.get_next_layer_value())
|
||||
|
||||
def reload_current_player(self):
|
||||
self.current_player.reload(self.get_next_layer_value(), is_current=True)
|
||||
|
||||
def receive_position(self, unused_addr, player_name, args):
|
||||
#print("the position of player {} is set to {}".format(player_name,args))
|
||||
for player in [self.next_player, self.current_player, self.last_player]:
|
||||
@@ -149,4 +165,20 @@ class VideoDriver(object):
|
||||
player.status = args
|
||||
break
|
||||
|
||||
### logic for looping players in parallel
|
||||
|
||||
def begin_playing_parallel(self):
|
||||
if self.in_parallel_cycle:
|
||||
if self.current_player.is_finished():
|
||||
self.current_player.try_load(self.get_next_layer_value(), is_current=True)
|
||||
if self.next_player.is_finished():
|
||||
self.next_player.try_load(self.get_next_layer_value())
|
||||
if self.current_player.is_loaded():
|
||||
self.current_player.start_video()
|
||||
if self.next_player.is_loaded():
|
||||
self.next_player.start_video()
|
||||
if self.loop_parallel:
|
||||
self.root.after(self.delay, self.begin_playing_parallel)
|
||||
else:
|
||||
self.in_parallel_cycle = False
|
||||
self.root.after(self.delay, self.begin_playing)
|
||||
|
||||
@@ -20,11 +20,11 @@ class VideoPlayer:
|
||||
self.alpha = 0
|
||||
|
||||
|
||||
def try_load(self, layer):
|
||||
def try_load(self, layer, is_current=True):
|
||||
load_attempts = 0
|
||||
while(load_attempts < 2):
|
||||
load_attempts = load_attempts + 1
|
||||
if self.load(layer):
|
||||
if self.load(layer, is_current):
|
||||
print('load success')
|
||||
return True
|
||||
else:
|
||||
@@ -34,9 +34,9 @@ class VideoPlayer:
|
||||
return False
|
||||
|
||||
|
||||
def load(self, layer):
|
||||
def load(self, layer, is_current=False):
|
||||
#try:
|
||||
self.get_context_for_player()
|
||||
self.get_context_for_player(is_current)
|
||||
is_dev_mode, first_screen_arg, second_screen_arg = self.set_screen_size_for_dev_mode()
|
||||
arguments = ['--no-osd', '--layer', str(layer), '--adev', 'local', '--alpha', '0', first_screen_arg, second_screen_arg]
|
||||
if not is_dev_mode:
|
||||
@@ -101,10 +101,10 @@ class VideoPlayer:
|
||||
elif(self.omx_running):
|
||||
self.root.after(5, self.pause_at_end)
|
||||
|
||||
def reload(self, layer):
|
||||
def reload(self, layer, is_current=True):
|
||||
self.exit()
|
||||
self.omx_running = False
|
||||
self.try_load(layer)
|
||||
self.try_load(layer, is_current)
|
||||
|
||||
def is_loaded(self):
|
||||
return self.status is 'LOADED'
|
||||
@@ -119,8 +119,8 @@ class VideoPlayer:
|
||||
print('{}: error get_position'.format(self.name))
|
||||
return -1
|
||||
|
||||
def get_context_for_player(self):
|
||||
next_context = self.data.get_next_context()
|
||||
def get_context_for_player(self, is_current=False):
|
||||
next_context = self.data.get_next_context(is_current)
|
||||
self.location = next_context['location']
|
||||
#self.total_length = next_context['length']
|
||||
self.start = next_context['start']
|
||||
|
||||
Reference in New Issue
Block a user