[ADD] script selenium video support
This commit is contained in:
parent
3f9d243106
commit
e7fe4758d2
4 changed files with 263 additions and 10 deletions
51
script/selenium/combine_video.sh
Executable file
51
script/selenium/combine_video.sh
Executable file
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Get two last .webm file
|
||||||
|
videos=($(ls -t *.webm | head -n 2))
|
||||||
|
|
||||||
|
if [ ${#videos[@]} -ne 2 ]; then
|
||||||
|
echo "Error : you need two files .webm in root directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
timestamp=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
output_name="output_${timestamp}.webm"
|
||||||
|
|
||||||
|
video1="${videos[0]}"
|
||||||
|
video2="${videos[1]}"
|
||||||
|
|
||||||
|
echo "first video ${video1}"
|
||||||
|
echo "second video ${video2}"
|
||||||
|
|
||||||
|
# Obtenez les durées des vidéos
|
||||||
|
duration1=$(ffmpeg -i "$video1" 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | tr -d ,)
|
||||||
|
duration2=$(ffmpeg -i "$video2" 2>&1 | grep 'Duration' | cut -d ' ' -f 4 | tr -d ,)
|
||||||
|
echo "duration 1 : ${duration1}"
|
||||||
|
echo "duration 2 : ${duration2}"
|
||||||
|
|
||||||
|
# Convertir les durées en secondes
|
||||||
|
duration1_sec=$(echo "$duration1" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
|
||||||
|
duration2_sec=$(echo "$duration2" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }')
|
||||||
|
echo "duration 1 sec : ${duration1_sec}"
|
||||||
|
echo "duration 2 sec : ${duration2_sec}"
|
||||||
|
|
||||||
|
# Déterminer la durée la plus courte
|
||||||
|
min_duration=$(echo "$duration1_sec $duration2_sec" | awk '{print ($1 < $2) ? $1 : $2}')
|
||||||
|
echo "Min duration : ${min_duration}"
|
||||||
|
|
||||||
|
# Créez la sortie
|
||||||
|
#output_name="output.mp4"
|
||||||
|
|
||||||
|
# Utiliser ffmpeg pour couper et empiler les vidéos
|
||||||
|
#ffmpeg -i "$video1" -i "$video2" -filter_complex "
|
||||||
|
# [0:v]trim=start=0:end=$min_duration,setpts=PTS-STARTPTS[v0];
|
||||||
|
# [1:v]trim=start=0:end=$min_duration,setpts=PTS-STARTPTS[v1];
|
||||||
|
# [v0][v1]hstack=inputs=2" "$output_name"
|
||||||
|
|
||||||
|
#echo "Les vidéos ont été combinées en $output_name"
|
||||||
|
|
||||||
|
ffmpeg -i "${videos[0]}" -i "${videos[1]}" -filter_complex "hstack=inputs=2" "${output_name}"
|
||||||
|
# with normalize size video
|
||||||
|
#ffmpeg -i "${videos[0]}" -i "${videos[1]}" -filter_complex "[0:v]scale=320:240[v0]; [1:v]scale=320:240[v1]; [v0][v1]hstack=inputs=2" "${output_name}"
|
||||||
|
|
||||||
|
echo "${output_name}"
|
||||||
|
|
@ -8,7 +8,9 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
|
from selenium.common.exceptions import TimeoutException
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.remote.webelement import WebElement
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.firefox.service import Service
|
from selenium.webdriver.firefox.service import Service
|
||||||
|
|
@ -21,7 +23,16 @@ class SeleniumLib(object):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.video_recorder = None
|
self.video_recorder = None
|
||||||
self.filename = "video_" + time.strftime("%Y_%m_%d-%H_%M_%S") + ".webm"
|
if self.config.video_suffix:
|
||||||
|
self.filename = (
|
||||||
|
f"video_{self.config.video_suffix}_"
|
||||||
|
+ time.strftime("%Y_%m_%d-%H_%M_%S")
|
||||||
|
+ ".webm"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.filename = (
|
||||||
|
"video_" + time.strftime("%Y_%m_%d-%H_%M_%S") + ".webm"
|
||||||
|
)
|
||||||
self.driver = None
|
self.driver = None
|
||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
|
|
@ -70,6 +81,7 @@ class SeleniumLib(object):
|
||||||
|
|
||||||
# Ajout de l'enregistrement
|
# Ajout de l'enregistrement
|
||||||
if self.config.record_mode:
|
if self.config.record_mode:
|
||||||
|
# Create recording
|
||||||
new_path = os.path.normpath(
|
new_path = os.path.normpath(
|
||||||
os.path.join(os.path.dirname(__file__), "..", "..")
|
os.path.join(os.path.dirname(__file__), "..", "..")
|
||||||
)
|
)
|
||||||
|
|
@ -164,6 +176,95 @@ class SeleniumLib(object):
|
||||||
|
|
||||||
return ele[0]
|
return ele[0]
|
||||||
|
|
||||||
|
def get_all_element(self, by: str = By.ID, value: str = None, timeout=5):
|
||||||
|
wait = WebDriverWait(self.driver, timeout)
|
||||||
|
ele = wait.until(
|
||||||
|
EC.visibility_of_all_elements_located(
|
||||||
|
(
|
||||||
|
by,
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return ele
|
||||||
|
|
||||||
|
def wait_new_element_and_click(
|
||||||
|
self,
|
||||||
|
by: str = By.ID,
|
||||||
|
value: str = None,
|
||||||
|
delay_wait=1,
|
||||||
|
timeout=5,
|
||||||
|
inject_cursor=False,
|
||||||
|
):
|
||||||
|
new_element = self.wait_new_element(
|
||||||
|
by=by, value=value, delay_wait=delay_wait, timeout=timeout
|
||||||
|
)
|
||||||
|
if new_element:
|
||||||
|
if inject_cursor:
|
||||||
|
self.inject_cursor()
|
||||||
|
self.click_with_mouse_move(element=new_element, no_scroll=True)
|
||||||
|
return new_element
|
||||||
|
|
||||||
|
def wait_new_element(
|
||||||
|
self,
|
||||||
|
by: str = By.ID,
|
||||||
|
value: str = None,
|
||||||
|
delay_wait=1,
|
||||||
|
timeout=5,
|
||||||
|
timeout_wait=None,
|
||||||
|
):
|
||||||
|
element = None
|
||||||
|
# TODO support timeout_wait
|
||||||
|
while element is None:
|
||||||
|
time.sleep(delay_wait)
|
||||||
|
try:
|
||||||
|
element = self.get_element(by, value, timeout)
|
||||||
|
except TimeoutException as e:
|
||||||
|
pass
|
||||||
|
print(f"Waiting {delay_wait} seconds after value '{value}'")
|
||||||
|
return element
|
||||||
|
|
||||||
|
def wait_add_new_element_and_click(
|
||||||
|
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
|
||||||
|
):
|
||||||
|
new_element = self.wait_add_new_element(
|
||||||
|
by=by, value=value, delay_wait=delay_wait, timeout=timeout
|
||||||
|
)
|
||||||
|
self.click_with_mouse_move(element=new_element, no_scroll=True)
|
||||||
|
return new_element
|
||||||
|
|
||||||
|
def wait_add_new_element(
|
||||||
|
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
|
||||||
|
):
|
||||||
|
all_element_init = self.get_all_element(by=by, value=value)
|
||||||
|
len_all_element_init = len(all_element_init)
|
||||||
|
len_all_element = len_all_element_init
|
||||||
|
all_element = []
|
||||||
|
while len_all_element_init == len_all_element:
|
||||||
|
time.sleep(delay_wait)
|
||||||
|
all_element = self.get_all_element(by=by, value=value)
|
||||||
|
len_all_element = len(all_element)
|
||||||
|
print(f"Waiting {delay_wait} seconds after value '{value}'")
|
||||||
|
new_element = all_element[-1]
|
||||||
|
return new_element
|
||||||
|
|
||||||
|
def wait_increment_number_text_and_click(
|
||||||
|
self, by: str = By.ID, value: str = None, delay_wait=1, timeout=5
|
||||||
|
):
|
||||||
|
element_init = self.get_element(by=by, value=value, timeout=timeout)
|
||||||
|
actual_number = int(element_init.text)
|
||||||
|
number_goal = actual_number + 1
|
||||||
|
while actual_number < number_goal:
|
||||||
|
time.sleep(delay_wait)
|
||||||
|
element_goal = self.get_element(
|
||||||
|
by=by, value=value, timeout=timeout
|
||||||
|
)
|
||||||
|
actual_number = int(element_goal.text)
|
||||||
|
print(f"Waiting {delay_wait} seconds after value '{value}'")
|
||||||
|
self.click_with_mouse_move(element=element_init, no_scroll=True)
|
||||||
|
return element_init
|
||||||
|
|
||||||
def click_with_mouse_move(
|
def click_with_mouse_move(
|
||||||
self,
|
self,
|
||||||
by: str = By.ID,
|
by: str = By.ID,
|
||||||
|
|
@ -172,9 +273,13 @@ class SeleniumLib(object):
|
||||||
no_scroll: bool = False,
|
no_scroll: bool = False,
|
||||||
viewport_ele_by: str = By.ID,
|
viewport_ele_by: str = By.ID,
|
||||||
viewport_ele_value: str = None,
|
viewport_ele_value: str = None,
|
||||||
|
element: WebElement = None,
|
||||||
):
|
):
|
||||||
# ele = self.driver.find_element(by, value)
|
# ele = self.driver.find_element(by, value)
|
||||||
ele = self.get_element(by, value, timeout)
|
if element:
|
||||||
|
ele = element
|
||||||
|
else:
|
||||||
|
ele = self.get_element(by, value, timeout)
|
||||||
if not no_scroll:
|
if not no_scroll:
|
||||||
viewport_ele = None
|
viewport_ele = None
|
||||||
if viewport_ele_value:
|
if viewport_ele_value:
|
||||||
|
|
@ -185,14 +290,17 @@ class SeleniumLib(object):
|
||||||
ActionChains(self.driver).move_to_element(ele).perform()
|
ActionChains(self.driver).move_to_element(ele).perform()
|
||||||
time.sleep(self.config.selenium_default_delay)
|
time.sleep(self.config.selenium_default_delay)
|
||||||
wait = WebDriverWait(self.driver, timeout)
|
wait = WebDriverWait(self.driver, timeout)
|
||||||
button = wait.until(
|
if element:
|
||||||
EC.element_to_be_clickable(
|
button = wait.until(EC.element_to_be_clickable(element))
|
||||||
(
|
else:
|
||||||
by,
|
button = wait.until(
|
||||||
value,
|
EC.element_to_be_clickable(
|
||||||
|
(
|
||||||
|
by,
|
||||||
|
value,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
button.click()
|
button.click()
|
||||||
|
|
||||||
def click_canvas_form(
|
def click_canvas_form(
|
||||||
|
|
@ -368,7 +476,50 @@ class SeleniumLib(object):
|
||||||
cursor.style.top = e.pageY - 5 + 'px';
|
cursor.style.top = e.pageY - 5 + 'px';
|
||||||
});
|
});
|
||||||
"""
|
"""
|
||||||
self.driver.execute_script(cursor_script)
|
try:
|
||||||
|
try:
|
||||||
|
self.driver.execute_script(cursor_script)
|
||||||
|
except Exception as e:
|
||||||
|
time.sleep(1)
|
||||||
|
print("Error execute script cursor, wait 1 second")
|
||||||
|
self.driver.execute_script(cursor_script)
|
||||||
|
except Exception as e:
|
||||||
|
time.sleep(3)
|
||||||
|
print("Error execute script cursor, wait 3 second")
|
||||||
|
self.driver.execute_script(cursor_script)
|
||||||
|
|
||||||
|
def scenario_create_new_account_with_random_user(
|
||||||
|
self, show_cursor=False, def_action_before_submit=None
|
||||||
|
):
|
||||||
|
# Click no account
|
||||||
|
if show_cursor:
|
||||||
|
self.inject_cursor()
|
||||||
|
self.click_with_mouse_move(
|
||||||
|
By.XPATH, "/html/body/div[1]/main/div/form/div[3]/div[1]/a[1]"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Trouvez les éléments du formulaire
|
||||||
|
if show_cursor:
|
||||||
|
self.inject_cursor()
|
||||||
|
|
||||||
|
# Remplissez le courriel et le mot de passe
|
||||||
|
first_name = self.get_french_word_no_space_no_accent()
|
||||||
|
password = first_name.lower()
|
||||||
|
second_name = self.get_french_word_no_space_no_accent()
|
||||||
|
full_name = f"{first_name} {second_name}"
|
||||||
|
domain = self.get_french_word_no_space_no_accent().lower()
|
||||||
|
|
||||||
|
self.input_text_with_mouse_move(
|
||||||
|
By.NAME, "login", f"{password}@{domain}.com"
|
||||||
|
)
|
||||||
|
self.input_text_with_mouse_move(By.NAME, "name", full_name)
|
||||||
|
self.input_text_with_mouse_move(By.NAME, "password", password)
|
||||||
|
self.input_text_with_mouse_move(By.NAME, "confirm_password", password)
|
||||||
|
if def_action_before_submit:
|
||||||
|
def_action_before_submit()
|
||||||
|
self.click_with_mouse_move(
|
||||||
|
By.XPATH, "/html/body/div[1]/main/div/form/div[6]/button"
|
||||||
|
)
|
||||||
|
|
||||||
def open_tab(self, url):
|
def open_tab(self, url):
|
||||||
# Open a new window
|
# Open a new window
|
||||||
|
|
@ -389,6 +540,34 @@ class SeleniumLib(object):
|
||||||
def start_record(self):
|
def start_record(self):
|
||||||
# Démarrer l'enregistrement
|
# Démarrer l'enregistrement
|
||||||
if self.config.record_mode:
|
if self.config.record_mode:
|
||||||
|
# Sync before record
|
||||||
|
has_sync_error = False
|
||||||
|
if self.config.sync_file_record_read:
|
||||||
|
if not self.config.sync_file_record_write:
|
||||||
|
print(
|
||||||
|
"Error, cannot sync, missing 'sync_file_record_write'"
|
||||||
|
)
|
||||||
|
has_sync_error = True
|
||||||
|
if self.config.sync_file_record_write:
|
||||||
|
if not self.config.sync_file_record_read:
|
||||||
|
print(
|
||||||
|
"Error, cannot sync, missing 'sync_file_record_read'"
|
||||||
|
)
|
||||||
|
has_sync_error = True
|
||||||
|
if (
|
||||||
|
not has_sync_error
|
||||||
|
and self.config.sync_file_record_read
|
||||||
|
and self.config.sync_file_record_write
|
||||||
|
):
|
||||||
|
# Do sync
|
||||||
|
file_name_1 = f"/tmp/erplibre_sync_temp_file{self.config.sync_file_record_write}"
|
||||||
|
file_name_2 = f"/tmp/erplibre_sync_temp_file{self.config.sync_file_record_read}"
|
||||||
|
with open(file_name_1, "w") as file:
|
||||||
|
file.write(f"Recording {self.filename}\n")
|
||||||
|
while not os.path.exists(file_name_2):
|
||||||
|
var_wait_time = 0.001
|
||||||
|
print(f"File {self.filename} wait {var_wait_time} seconds")
|
||||||
|
time.sleep(var_wait_time)
|
||||||
self.video_recorder.start()
|
self.video_recorder.start()
|
||||||
|
|
||||||
def stop_record(self):
|
def stop_record(self):
|
||||||
|
|
@ -399,6 +578,7 @@ class SeleniumLib(object):
|
||||||
time.sleep(self.config.selenium_video_time_waiting_end)
|
time.sleep(self.config.selenium_video_time_waiting_end)
|
||||||
self.video_recorder.stop()
|
self.video_recorder.stop()
|
||||||
print("End of recording video")
|
print("End of recording video")
|
||||||
|
print(self.filename)
|
||||||
|
|
||||||
if self.config.selenium_video_auto_play_video:
|
if self.config.selenium_video_auto_play_video:
|
||||||
print(f"Play video {self.filename}")
|
print(f"Play video {self.filename}")
|
||||||
|
|
@ -456,6 +636,27 @@ def fill_parser(parser):
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Start recording (not finish to be implemented).",
|
help="Start recording (not finish to be implemented).",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--video_suffix",
|
||||||
|
default="",
|
||||||
|
help="Will modify video name.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--sync_file_record_read",
|
||||||
|
default="",
|
||||||
|
help=(
|
||||||
|
"Will sync and check file from tmp before record, write before"
|
||||||
|
" read."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--sync_file_record_write",
|
||||||
|
default="",
|
||||||
|
help=(
|
||||||
|
"Will sync and check file from tmp before record, write before"
|
||||||
|
" read."
|
||||||
|
),
|
||||||
|
)
|
||||||
group_record.add_argument(
|
group_record.add_argument(
|
||||||
"--selenium_video_time_waiting_end",
|
"--selenium_video_time_waiting_end",
|
||||||
default=5,
|
default=5,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class VideoRecorder(object):
|
||||||
self,
|
self,
|
||||||
driver,
|
driver,
|
||||||
filename="video.webm",
|
filename="video.webm",
|
||||||
framerate="25/1",
|
framerate="33/1",
|
||||||
width=None,
|
width=None,
|
||||||
height=None,
|
height=None,
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@ def run(config, selenium_tool):
|
||||||
if config.open_dashboard:
|
if config.open_dashboard:
|
||||||
selenium_tool.click(
|
selenium_tool.click(
|
||||||
"/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[3]/a",
|
"/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[3]/a",
|
||||||
|
timeout=15,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Open conversation chat
|
# Open conversation chat
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue