Selenium is wired to the settings page and has been modified to allow screenshot to be taken at any time

This commit is contained in:
2023-04-26 14:04:39 +02:00
parent d935a07076
commit e0d5f82467
7 changed files with 336 additions and 0 deletions

201
Test_Merge/F1TVEmulator.cs Normal file
View File

@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.UI;
namespace Test_Merge
{
public class F1TVEmulator
{
public const string COOKIE_HOST = ".formula1.com";
public const string PYTHON_COOKIE_RETRIEVAL_FILENAME = "recoverCookiesCSV.py";
public const string GECKODRIVER_FILENAME = @"geckodriver-v0.27.0-win32\geckodriver.exe";
//BE CAREFULL IF YOU CHANGE IT HERE YOU NEED TO CHANGE IT IN THE PYTHON SCRIPT TOO
public const string COOKIES_CSV_FILENAME = "cookies.csv";
private FirefoxDriver Driver;
private bool _ready;
private string _grandPrixUrl;
public string GrandPrixUrl { get => _grandPrixUrl; private set => _grandPrixUrl = value; }
public bool Ready { get => _ready; set => _ready = value; }
public F1TVEmulator(string grandPrixUrl)
{
GrandPrixUrl = grandPrixUrl;
Ready = false;
}
private void StartCookieRecovering()
{
string scriptPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), PYTHON_COOKIE_RETRIEVAL_FILENAME);
Process process = new Process();
process.StartInfo.FileName = "python.exe";
process.StartInfo.Arguments = scriptPath;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
}
public async Task<int> Start()
{
//SETUP
//Those values can be changed to alter the way that the selenium driver looks and behaves in general
Ready = false;
string loginCookieName = "login";
string loginSessionCookieName = "login-session";
string loginCookieValue = GetCookie(COOKIE_HOST, loginCookieName);
string loginSessionValue = GetCookie(COOKIE_HOST, loginSessionCookieName);
int windowWidth = 1920;
int windowHeight = 1080;
var service = FirefoxDriverService.CreateDefaultService(AppDomain.CurrentDomain.BaseDirectory + GECKODRIVER_FILENAME);
service.Host = "127.0.0.1";
service.Port = 5555;
FirefoxOptions options = new FirefoxOptions();
options.AddArgument("--window-size=" + windowWidth + "," + windowHeight + "");
options.AddArgument("--disable-web-security");
options.AddArgument("--same-site-none-secure");
options.AcceptInsecureCertificates = true;
//Headless part
options.AddArgument("--headless");
options.AddArgument("--silent-launch");
options.AddArgument("--no-startup-window");
//ACTUAL STARTUP
Driver = new FirefoxDriver(service, options);
var loginCookie = new Cookie(loginCookieName, loginCookieValue, COOKIE_HOST, "/", DateTime.Now.AddDays(5));
var loginSessionCookie = new Cookie(loginSessionCookieName, loginSessionValue, COOKIE_HOST, "/", DateTime.Now.AddDays(5));
Actions actions = new Actions(Driver);
Driver.Navigate().GoToUrl("https://f1tv.formula1.com/");
Driver.Manage().Cookies.AddCookie(loginCookie);
Driver.Manage().Cookies.AddCookie(loginSessionCookie);
Driver.Navigate().GoToUrl("https://f1tv.formula1.com/detail/1000006436/2023-saudi-arabian-grand-prix?action=play");
//Waits for the page to fully load
Driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(30);
//Removes the cookie prompt
IWebElement conscentButton = Driver.FindElement(By.Id("truste-consent-button"));
conscentButton.Click();
//Again waits for the page to fully load (when you accept cookies it takes a little time for the page to load)
//Cannot use The timeout because the feed loading is not really loading so there is not event or anything
Thread.Sleep(5000);
//Switches to the Data channel
try
{
IWebElement dataChannelButton = Driver.FindElement(By.ClassName("data-button"));
dataChannelButton.Click();
}
catch (OpenQA.Selenium.NoSuchElementException error)
{
//If the data button does not exists its because the user is not connected
MessageBox.Show("Could not connect to the F1TV. Please connect to it using your account on chrome and if you already did please wait the process can take a few minuts");
Driver.Dispose();
return 1;
}
//Open settings
// Press the space key, this should make the setting button visible
// It does not matter if the feed is paused because when changing channel it autoplays
actions.SendKeys(OpenQA.Selenium.Keys.Space).Perform();
Thread.Sleep(1000);
//Clicks on the settings Icon
IWebElement settingsButton = Driver.FindElement(By.ClassName("bmpui-ui-settingstogglebutton"));
settingsButton.Click();
IWebElement selectElement = Driver.FindElement(By.ClassName("bmpui-ui-videoqualityselectbox"));
SelectElement select = new SelectElement(selectElement);
IWebElement selectOption = selectElement.FindElement(By.CssSelector("option[value^='1080_']"));
selectOption.Click();
//Makes the feed fullscreen
WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(10));
IWebElement fullScreenButton = Driver.FindElement(By.ClassName("bmpui-ui-fullscreentogglebutton"));
fullScreenButton.Click();
//STARTUP FINISHED READY TO SCREENSHOT
Ready = true;
return 0;
}
public void Stop()
{
Ready = false;
Driver.Dispose();
}
public Bitmap Screenshot()
{
Bitmap result = new Bitmap(100,100);
if (Ready)
{
Screenshot scrsht = ((ITakesScreenshot)Driver).GetScreenshot();
// Get the raw bytes of the screenshot
byte[] screenshotBytes = Convert.FromBase64String(scrsht.AsBase64EncodedString);
// Create a new MemoryStream from the bytes
MemoryStream stream = new MemoryStream(screenshotBytes);
// Create a new Bitmap object from the MemoryStream
result = new Bitmap(stream);
}
return result;
}
public string GetCookie(string host, string name)
{
StartCookieRecovering();
string value = "";
List<Cookie> cookies = new List<Cookie>();
using (var reader = new StreamReader(COOKIES_CSV_FILENAME))
{
// Read the header row and validate column order
string header = reader.ReadLine();
string[] expectedColumns = { "host_key", "name", "value", "path", "expires_utc", "is_secure", "is_httponly" };
string[] actualColumns = header.Split(',');
for (int i = 0; i < expectedColumns.Length; i++)
{
if (expectedColumns[i] != actualColumns[i])
{
throw new InvalidOperationException($"Expected column '{expectedColumns[i]}' at index {i} but found '{actualColumns[i]}'");
}
}
// Read each data row and parse values into a Cookie object
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split(',');
string hostname = fields[0];
string cookieName = fields[1];
if (hostname == host && cookieName == name)
{
value = fields[2];
}
}
}
return value;
}
}
}

View File

@@ -221,6 +221,7 @@
this.btnRefresh.TabIndex = 1;
this.btnRefresh.Text = "Refresh";
this.btnRefresh.UseVisualStyleBackColor = true;
this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
//
// pbxMain
//

View File

@@ -19,6 +19,8 @@ namespace Test_Merge
private int _grandPrixYear = 2000;
private List<string> _driverList = new List<string>();
private F1TVEmulator Emulator = null;
private bool CreatingZone = false;
private Point ZoneP1;
private Point ZoneP2;
@@ -256,5 +258,29 @@ namespace Test_Merge
}
return new Rectangle(newP1.X, newP1.Y,newP2.X-newP1.X,newP2.Y -newP2.Y);
}
private async void btnRefresh_Click(object sender, EventArgs e)
{
if (Emulator == null)
{
Emulator = new F1TVEmulator(tbxGpUrl.Text);
}
if (!Emulator.Ready)
{
if (await Emulator.Start() != 0)
{
MessageBox.Show("Could not start the emulator");
}
else
{
pbxMain.Image = Emulator.Screenshot();
}
}
else
{
pbxMain.Image = Emulator.Screenshot();
}
}
}
}

View File

@@ -12,6 +12,8 @@
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -44,8 +46,15 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WebDriver, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.WebDriver.4.8.2\lib\net47\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Selenium.Support.4.8.2\lib\net47\WebDriver.Support.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="F1TVEmulator.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
@@ -75,6 +84,7 @@
<EmbeddedResource Include="Settings.resx">
<DependentUpon>Settings.cs</DependentUpon>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -89,4 +99,13 @@
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Selenium.Firefox.WebDriver.0.27.0\build\Selenium.Firefox.WebDriver.targets" Condition="Exists('..\packages\Selenium.Firefox.WebDriver.0.27.0\build\Selenium.Firefox.WebDriver.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Selenium.Firefox.WebDriver.0.27.0\build\Selenium.Firefox.WebDriver.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Selenium.Firefox.WebDriver.0.27.0\build\Selenium.Firefox.WebDriver.targets'))" />
<Error Condition="!Exists('..\packages\Selenium.WebDriver.4.8.2\build\Selenium.WebDriver.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Selenium.WebDriver.4.8.2\build\Selenium.WebDriver.targets'))" />
</Target>
<Import Project="..\packages\Selenium.WebDriver.4.8.2\build\Selenium.WebDriver.targets" Condition="Exists('..\packages\Selenium.WebDriver.4.8.2\build\Selenium.WebDriver.targets')" />
</Project>

Binary file not shown.

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Selenium.Firefox.WebDriver" version="0.27.0" targetFramework="net472" />
<package id="Selenium.Support" version="4.8.2" targetFramework="net472" />
<package id="Selenium.WebDriver" version="4.8.2" targetFramework="net472" />
</packages>

View File

@@ -0,0 +1,83 @@
# Rohmer Maxime
# RecoverCookies.py
# Little script that recovers the cookies stored in the chrome sqlite database and then decrypts them using the key stored in the chrome files
# This script has been created to be used by an other programm or for the data to not be used directly. This is why it stores all the decoded cookies in a csv. (Btw could be smart for the end programm to delete the csv after using it)
# Parts of this cript have been created with the help of ChatGPT
import os
import json
import base64
import sqlite3
import win32crypt
from Cryptodome.Cipher import AES
from pathlib import Path
import csv
def get_master_key():
with open(
os.getenv("localappdata") + "\\Google\\Chrome\\User Data\\Local State", "r"
) as f:
local_state = f.read()
local_state = json.loads(local_state)
master_key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
master_key = master_key[5:] # removing DPAPI
master_key = win32crypt.CryptUnprotectData(master_key, None, None, None, 0)[1]
print("MASTER KEY :")
print(master_key)
print(len(master_key))
return master_key
def decrypt_payload(cipher, payload):
return cipher.decrypt(payload)
def generate_cipher(aes_key, iv):
return AES.new(aes_key, AES.MODE_GCM, iv)
def decrypt_password(buff, master_key):
try:
iv = buff[3:15]
payload = buff[15:]
cipher = generate_cipher(master_key, iv)
decrypted_pass = decrypt_payload(cipher, payload)
decrypted_pass = decrypted_pass[:-16].decode() # remove suffix bytes
return decrypted_pass
except Exception:
# print("Probably saved password from Chrome version older than v80\n")
# print(str(e))
return "Chrome < 80"
master_key = get_master_key()
cookies_path = Path(
os.getenv("localappdata") + "\\Google\\Chrome\\User Data\\Default\\Network\\Cookies"
)
if not cookies_path.exists():
raise ValueError("Cookies file not found")
with sqlite3.connect(cookies_path) as connection:
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
cursor.execute("SELECT * FROM cookies")
with open('cookies.csv', 'a', newline='') as csvfile:
fieldnames = ['host_key', 'name', 'value', 'path', 'expires_utc', 'is_secure', 'is_httponly']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
if csvfile.tell() == 0:
writer.writeheader()
for row in cursor.fetchall():
decrypted_value = decrypt_password(row["encrypted_value"], master_key)
writer.writerow({
'host_key': row["host_key"],
'name': row["name"],
'value': decrypted_value,
'path': row["path"],
'expires_utc': row["expires_utc"],
'is_secure': row["is_secure"],
'is_httponly': row["is_httponly"]
})
print("Finished CSV")