#
Downloading folder
This step is useful if you store profile folders on our servers (this is the behavior when using Surfinite App). Though, you can absolutely skip it if you store your folders locally, e.g. you use your own dedicated server and want to avoid the additional overhead of downloading and uploading folder everytime the profile is used.
#
Folder contents
Browser folder usually contains a lot of files, which might be necessary for your automation:
- Cookies
- Preferences
- Passwords
- Bookmarks & Extensions
- History
- Session, LocalStorage, IndexedDB and other data
Some of it is stored on our servers in archived format, some of it must be fetched using the API (like bookmarks and cookies), and some is downloaded and cached locally (like extensions, so that you do not download them on every launch).
As downloading a folder is such a complicated task, requiring multiple requests, a lot of unzipping and moving files from multiple sources, we implemented all of these operations in our helper tool - aemulari , so you can call it once with the necessary parameters and not care about the details.
#
aemulari
We distribute aemulari in a binary executable form. If you have installed Surfinite App, you would find it in:
- for Windows:
C:\Users\<Username>\.surfinite\aemulari\aemulari.exe
- for macOS:
/Users/<Username>/.surfinite/aemulari/aemulari
- for Linux:
/home/<Username>/.surfinite/aemulari/aemulari
This tool is capable of downloading and uploading profile folders, as well as checking proxies. You can run it
with a --help
argument to get description on how to use it. But, as we are focusing on downloading
a profile folder, the corresponding aemulari
command is create-folder
, and here's its help message:
$ ./aemulari create-folder --help
Create folder usage:
--profile-id TEXT Profile ID to use
--path TEXT Path to the folder. E.g. "path=C:\Profiles\MyProfile".
If the folder does not exist, it will be created. If
omitted, system's default temp dir would be used with
profile id: <temp-dir>\<profile-id>
--api-key TEXT Your API key
Common options:
--verbose Be verbose (enable logging)
You can provide arguments in both forms key=value
and key value
, for example:
./aemulari create-folder --profile-id="my_profile_id" --path=C:\Users\Me\profiles\my_profile_id --api-key MY_API_KEY
If the operation succeeds, the executable will print the profile folder path to stdout
and finish
with an exit code of 0
.
#
Exit codes
You will get the server's error message printed to stdout
in case you get error codes 101-110
. Usually you
should contact support if you get exit codes 1
, 2
or 200
and have no way of fixing it yourself.
#
Use from code
Every programming language has an API for instantiating processes. Usually you provide it with a path to aemulari
and your arguments. However, there's one tricky part. Every process is initialized with three standard pipes:
stdin
for standard inputstdout
for standard outputstderr
for standard error
You can ignore stdin
completely. Though, we recommend putting stdout
to an INHERIT IO
mode, so that
aemulari
inherits standard output stream from the calling process, i.e. your program. That way you will see that
aemulari
outputs to the same place where you print other information in your code. This might be especially useful
if using --verbose
flag.
If you use --verbose
flag and do not set INHERIT IO
nor consume stdout
output manually,
there is a small chance of a deadlock and aemulari
running forever. This happens when the stream buffer
is full and nobody reads it.
This is the only reason we wrote about these standard streams - so you do not make this mistake 🙂
Enough theory, here's an example of how to download profile folder from code:
val pathToAemulari = "/home/myuser/.surfinite/aemulari/aemulari"
val profileId = "my_profile_id"
val apiKey = "MY_API_KEY"
val pathToProfile = "/home/myuser/surfiprofiles/$profileId"
// Start aemulari with our arguments
val process = ProcessBuilder(
listOf(
pathToAemulari,
"create-folder",
"--profile-id", profileId,
"--path", pathToProfile,
"--api-key", apiKey
)
)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start()
// Wait for the process to exit
process.waitFor()
val exitCode = process.exitValue()
if (exitCode == 0) {
println("Success")
} else {
// The error message was already printed, as we called
// redirectOutput(INHERIT), so just print the exit code
println("Got error from aemulari. Code: $exitCode")
}
Feature: local server
In the future, we will add an option to start aemulari
as a local server listening on some port, so that you do not
have to deal with processes (the process API is usually cumbersome). But for now, CLI interface is the only way to go.
And it works great, too.