init
BIN
selenium/__pycache__/pyautogui.cpython-38.pyc
Executable file
37
selenium/autogui.py
Executable file
@@ -0,0 +1,37 @@
|
||||
#! python3
|
||||
import pyautogui
|
||||
#import pygetwindow as gw
|
||||
import sys
|
||||
|
||||
# print('Press Ctrl-C to quit.')
|
||||
# try:
|
||||
# while True:
|
||||
# x, y = pyautogui.position()
|
||||
# positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4)
|
||||
# print(positionStr)
|
||||
|
||||
# except KeyboardInterrupt:
|
||||
# print('\nDone.')
|
||||
|
||||
file = sys.argv[1]
|
||||
print('File received as arg: ', file)
|
||||
|
||||
#fw = pyautogui.getActiveWindow()
|
||||
#print('Active window: ', fw)
|
||||
#print('Maximized: ', fw.isMaximized)
|
||||
#print('Minimized: ', fw.isMinimized)
|
||||
#print('isActive: ', fw.isActive)
|
||||
#print('Title: ', fw.title)
|
||||
|
||||
#fileUploadWindow = gw.getWindowsWithTitle('File Upload')[0]
|
||||
#print('Target window: ', fileUploadWindow)
|
||||
#fileUploadWindow.activate()
|
||||
|
||||
#fileUploadWindow = pyautogui.getWindowsWithTitle('File Upload')[0]
|
||||
#fileUploadWindow.activate()
|
||||
pyautogui.PAUSE = 3
|
||||
pyautogui.hotkey('ctrl', 'l')
|
||||
pyautogui.typewrite(file, interval=0.1)
|
||||
pyautogui.press('enter')
|
||||
print('I am done.')
|
||||
sys.exit(1)
|
||||
BIN
selenium/cat.jpg
Executable file
|
After Width: | Height: | Size: 37 KiB |
BIN
selenium/geckodriver
Executable file
BIN
selenium/geckodriver.exe
Executable file
299
selenium/get_page_source
Executable file
1
selenium/profiles/adobebestprices/db/db_adobebestprices.dat
Executable file
@@ -0,0 +1 @@
|
||||
{"/home/pi/projects/instafeed/selenium/profiles/adobebestprices/images/UJAM Virtual Bassist DANDY v2.1.1 VST AAX [WIN].jpg":{"FILEPATH":"/home/pi/projects/instafeed/selenium/profiles/adobebestprices/images/UJAM Virtual Bassist DANDY v2.1.1 VST AAX [WIN].jpg","TIMESTAMP_UPLOADED":"19000100_000000"}}
|
||||
|
After Width: | Height: | Size: 67 KiB |
BIN
selenium/profiles/adobebestprices/images/Algonaut Atlas v1.4.4 VST WIN.jpg
Executable file
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
BIN
selenium/profiles/adobebestprices/images/Ample Bass P v3.2 WIN & MacOS.jpg
Executable file
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
BIN
selenium/profiles/adobebestprices/images/Ample Guitar LP v3.2 [WIN & MACOSX].jpg
Executable file
|
After Width: | Height: | Size: 43 KiB |
BIN
selenium/profiles/adobebestprices/images/Ample Guitar VC v3.2.0 WIN & MacOSX.jpg
Executable file
|
After Width: | Height: | Size: 42 KiB |
BIN
selenium/profiles/adobebestprices/images/Lindell Plugins Bundle 2020 WIN.jpg
Executable file
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 59 KiB |
BIN
selenium/profiles/adobebestprices/images/StudioLinked Bit Machine VST AU.jpg
Executable file
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 26 KiB |
BIN
selenium/profiles/adobebestprices/images/UJAM Finisher Bundle VST AAX [WIN].jpg
Executable file
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
BIN
selenium/selenium-server-standalone-3.141.59.jar
Executable file
356
selenium/selenium.pl
Executable file
@@ -0,0 +1,356 @@
|
||||
#!usr/bin/perl
|
||||
use feature qw(say);
|
||||
use JSON::XS qw(encode_json decode_json);
|
||||
use Selenium::Firefox::Profile;
|
||||
use Data::Dumper;
|
||||
use HTML::TreeBuilder::XPath;
|
||||
use File::Slurp qw(read_file write_file);
|
||||
use Selenium::Remote::WDKeys;
|
||||
use Selenium::Firefox;
|
||||
use Cwd;
|
||||
use Getopt::Long qw(GetOptions);
|
||||
use File::Basename;
|
||||
use List::Util 'shuffle';
|
||||
use Selenium::Waiter qw/wait_until/;
|
||||
|
||||
my (%config, $driver_profile, $driver);
|
||||
|
||||
sub Init {
|
||||
%config = (
|
||||
'xpath' => {
|
||||
'input_username_classic' => '//*[@id="id_username"]',
|
||||
'input_password_classic' => '//*[@id="id_enc_password"]',
|
||||
'button_login_classic' => '//*[@id="login-form"]/p[3]/input',
|
||||
'button_addtohomescreen' => '/html/body/div[4]/div/div/div/div[3]/button[1][text()="Add to Home screen"]',
|
||||
'button_addtohomescreen_cancel' => '/html/body/div[4]/div/div/div/div[3]/button[2][text()="Cancel"]',
|
||||
'button_newpost' => '//div[contains(@data-testid, "new-post-button")]',
|
||||
'input_newpostupload' => '//*[@id="react-root"]/section/nav[2]/div/div/form/input',
|
||||
'button_expand' => '//span[text()="Expand"]',
|
||||
'button_next' => '//button[text()="Next"]',
|
||||
'button_share' => '//button[text()="Share"]',
|
||||
'textarea_caption' => '//textarea[contains(@placeholder,"Write a caption")]',
|
||||
},
|
||||
'profile' => {
|
||||
'adobebestprices' => {
|
||||
'DBFilepath' => cwd() . '/profiles/adobebestprices/db/db_adobebestprices.dat',
|
||||
'imageDir' => cwd() . '/profiles/adobebestprices/images',
|
||||
'filename_as_title' => 1,
|
||||
'tags' => ['#Beats', '#FLStudio20', '#Producer', '#Ableton', '#Beatmaker', '#Studio', '#ProTools', '#Music', '#DAW', '#LogicPro', '#VST', '#VSTplugins', '#NativeInstruments', '#Drums', '#MIDI', '#Omnisphere', '#Keyscape', '#Trilian', '#Logic', '#Waves', '#Antares', '#AutoTune', '#Kontakt', '#SoundToys', '#FABFilter', '#Arturia', '#Windows', '#MAC'],
|
||||
},
|
||||
},
|
||||
'dumpToFile' => cwd() . '/get_page_source',
|
||||
'pyautogui_cmd' => 'python3 ' . cwd() . '/autogui.py',
|
||||
'profile_selected' => undef,
|
||||
'wipe_after' => 0,
|
||||
);
|
||||
|
||||
&CheckParameter();
|
||||
&UndumpFromFile();
|
||||
&SelectImage();
|
||||
|
||||
$driver_profile = Selenium::Firefox::Profile->new;
|
||||
$driver_profile->set_preference(
|
||||
'intl.accept_languages' => 'en-US',
|
||||
# 'general.useragent.override' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15',
|
||||
'general.useragent.override' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16',
|
||||
);
|
||||
# $driver_profile->set_boolean_preference("options.headless" => 1);
|
||||
$driver = Selenium::Remote::Driver->new( 'browser_name' => 'firefox',
|
||||
'platform' => 'LINUX',
|
||||
'remote_server_addr' => 'localhost',
|
||||
'port' => 4444,
|
||||
'firefox_profile' => $driver_profile,
|
||||
'extra_capabilities' => {
|
||||
'moz:firefoxOptions' => {
|
||||
#"args" => [ "--headless" ],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
# $driver = Selenium::Firefox->new( 'browser_name' => 'firefox',
|
||||
# 'platform' => 'LINUX',
|
||||
# 'binary' => '/usr/local/bin/geckodriver',
|
||||
# 'firefox_profile' => $driver_profile,
|
||||
# 'extra_capabilities' => {
|
||||
# 'moz:firefoxOptions' => {
|
||||
# #"args" => [ "--headless" ],
|
||||
# },
|
||||
# },
|
||||
# );
|
||||
|
||||
$driver->set_implicit_wait_timeout(5000);
|
||||
$driver->set_window_size(812, 375);
|
||||
|
||||
say 'starting...';
|
||||
say $driver->get_title();
|
||||
say $driver->get_user_agent();
|
||||
}
|
||||
|
||||
|
||||
&Init();
|
||||
&Login();
|
||||
&NewPost();
|
||||
&Finish();
|
||||
|
||||
sub RndHashtagString {
|
||||
my $i = shift;
|
||||
|
||||
my $RndArr = &RndArrItems($config{profile}{'adobebestprices'}{tags}, $i);
|
||||
my $hastags = join(' ', @$RndArr[0..$#{$RndArr}]);
|
||||
|
||||
return $hastags;
|
||||
}
|
||||
|
||||
sub RndArrItems {
|
||||
my ($arr_ref, $num_picks) = @_;
|
||||
|
||||
# Shuffled list of indexes into @deck
|
||||
my @shuffled_indexes = shuffle(0..$#{$arr_ref});
|
||||
|
||||
# Get just N of them.
|
||||
my @pick_indexes = @shuffled_indexes[ 0 .. $num_picks - 1 ];
|
||||
|
||||
# Pick cards from @deck
|
||||
my @picks;
|
||||
for my $index (@pick_indexes) {
|
||||
push(@picks, $$arr_ref[$index]);
|
||||
}
|
||||
|
||||
return \@picks;
|
||||
}
|
||||
|
||||
sub SelectImage {
|
||||
&DirectoryListing();
|
||||
&FindNewDataset();
|
||||
$config{image_backslash} = $config{image_selected};
|
||||
# $config{image_backslash} =~ s/\//\\/g;
|
||||
print Dumper \%config;
|
||||
}
|
||||
|
||||
sub Login {
|
||||
say 'Logging in...';
|
||||
|
||||
$driver->get('https://instagram.com/accounts/login/?force_classic_login');
|
||||
my $tree = HTML::TreeBuilder::XPath->new_from_content($driver->get_page_source());
|
||||
&Rndwait(1,3);
|
||||
|
||||
if( $tree->exists($config{xpath}{input_username_classic}) && $tree->exists($config{xpath}{input_password_classic}) ) {
|
||||
say 'Found: Login elements.';
|
||||
|
||||
# Username & password
|
||||
$driver->find_element($config{xpath}{input_username_classic})->click;
|
||||
$driver->send_keys_to_active_element('adobebestprices');
|
||||
&Rndwait(1,3);
|
||||
$driver->find_element($config{xpath}{input_password_classic})->click();
|
||||
$driver->send_keys_to_active_element('vst#1337');
|
||||
&Rndwait(1,3);
|
||||
|
||||
# Click login button
|
||||
$driver->find_element($config{xpath}{button_login_classic})->click();
|
||||
}
|
||||
else {
|
||||
die 'Not found: Login elements.';
|
||||
}
|
||||
}
|
||||
|
||||
sub NewPost {
|
||||
&Rndwait(5,10); # We see the home screen
|
||||
|
||||
my $tree = HTML::TreeBuilder::XPath->new_from_content($driver->get_page_source());
|
||||
|
||||
# add to homescreen popup?
|
||||
&ClickXpath($config{xpath}{button_addtohomescreen}, $tree, 0); # click cancel button
|
||||
|
||||
&ClickXpath($config{xpath}{button_newpost}, $tree); # click new post button
|
||||
&Rndwait(5,10);
|
||||
|
||||
# Bypass file dialog using pyautogui
|
||||
my $cmd = $config{pyautogui_cmd} . ' "' . $config{image_backslash} . '"'; # image path needs to use backslash for python
|
||||
my $ret = system($cmd) or die "system $cmd failed: $?";
|
||||
# print `$cmd`;
|
||||
# system($cmd);
|
||||
# &Rndwait(30,45);
|
||||
|
||||
&FillNewPost();
|
||||
}
|
||||
|
||||
sub FillNewPost {
|
||||
# Wait for page reload after selecting an image
|
||||
wait_until { $driver->find_element($config{xpath}{button_next}) };
|
||||
|
||||
my $tree = HTML::TreeBuilder::XPath->new_from_content($driver->get_page_source());
|
||||
|
||||
# Expand button
|
||||
&ClickXpath($config{xpath}{button_expand}, $tree, 0); # click expand button to show image in full size
|
||||
&Rndwait(2,4);
|
||||
|
||||
&ClickXpath($config{xpath}{button_next}, $tree); # click next button
|
||||
&Rndwait(5,10);
|
||||
|
||||
# Caption
|
||||
my $tree2 = HTML::TreeBuilder::XPath->new_from_content($driver->get_page_source());
|
||||
my $profile = $config{profile_selected};
|
||||
my $caption;
|
||||
my $hastags = &RndHashtagString(9);
|
||||
if ($config{profile}{$profile}{'filename_as_title'}) {
|
||||
my $filename = basename($config{image_selected});
|
||||
$filename =~ s/(NO INSTALL)|(SymLink Installer)//g;
|
||||
$filename =~ s/( , )|(\.[^.]+$)//g;
|
||||
$caption = "$filename\n\n" . $hastags;
|
||||
}
|
||||
&ClickXpath($config{xpath}{textarea_caption}, $tree2);
|
||||
&Rndwait(5,6);
|
||||
$driver->send_keys_to_active_element($caption);
|
||||
&Rndwait(5,10);
|
||||
|
||||
# Share
|
||||
&ClickXpath($config{xpath}{button_share}, $tree2);
|
||||
&Rndwait(20,40);
|
||||
}
|
||||
|
||||
sub Finish {
|
||||
say 'Sleep and then quit';
|
||||
&Rndwait(5,5);
|
||||
|
||||
&AddImageToDB();
|
||||
&WipeImage() if $config{wipe_after};
|
||||
$driver->quit();
|
||||
}
|
||||
|
||||
sub ClickXpath {
|
||||
my $die = 1;
|
||||
my ($xpath, $tree, $die) = @_;
|
||||
|
||||
if( $tree->exists($xpath) ) {
|
||||
say "Found: $xpath";
|
||||
$driver->find_element($xpath)->click();
|
||||
}
|
||||
else {
|
||||
die "Not found: $xpath" if $die;
|
||||
warn "Not found: $xpath" if !$die;
|
||||
}
|
||||
}
|
||||
|
||||
sub Rndwait {
|
||||
my ($minimum, $maximum) = @_;
|
||||
|
||||
# $seconds = $minimum + int(rand($maximum - $minimum));
|
||||
$seconds = $minimum + rand($maximum - $minimum);
|
||||
$milliseconds = int($seconds * 1000);
|
||||
|
||||
say "Sleeping for ${milliseconds}ms now...";
|
||||
$driver->pause($milliseconds);
|
||||
}
|
||||
|
||||
sub dumpToFile {
|
||||
# &Delimiter((caller(0))[3]);
|
||||
my ($filepath, $ref) = @_;
|
||||
|
||||
# DATA DUMPER UTF8 HACK
|
||||
# no warnings 'redefine';
|
||||
local *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
|
||||
local $Data::Dumper::Useperl = 1;
|
||||
|
||||
open my $FILE, '>:encoding(UTF-8)', $filepath;
|
||||
print $FILE Dumper $ref;
|
||||
close $FILE;
|
||||
}
|
||||
|
||||
sub Delimiter {
|
||||
my $SubName = shift;
|
||||
print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n";
|
||||
}
|
||||
|
||||
sub CheckParameter {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
GetOptions ('profile=s' => \$config{'profile_selected'}) or die &PrintUsage();
|
||||
die &PrintUsage() if !$config{'profile_selected'};
|
||||
|
||||
my $profile = $config{'profile_selected'};
|
||||
if (!exists $config{'profile'}{$profile}) {
|
||||
print "Profile '$config{'profile'}' does not exist.\n";
|
||||
&PrintUsage();
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
sub UndumpFromFile {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
my $profile = $config{profile_selected};
|
||||
if (-e $config{profile}{$profile}{'DBFilepath'}) {
|
||||
my $json = read_file($config{profile}{$profile}{'DBFilepath'}, { binmode => ':raw' });
|
||||
if (!$json) {
|
||||
warn "DB file $config{'profile'}{$profile}{'DBFilepath'} is empty.\n";
|
||||
return;
|
||||
}
|
||||
%{$config{db}} = %{ decode_json $json };
|
||||
print "INFO: $config{'profile'}{$profile}{'DBFilepath'} has " . scalar(keys(%{$config{db}})) . " keys.\n";
|
||||
}
|
||||
elsif ( !-e $config{'profile'}{$profile}{'DBFilepath'} ) {
|
||||
print "INFO: NO DB file found at $config{'profile'}{$profile}{'DBFilepath'}. Creating now... ";
|
||||
write_file( $config{'profile'}{$profile}{'DBFilepath'}, '' );
|
||||
&UndumpFromFile();
|
||||
}
|
||||
}
|
||||
|
||||
sub PrintUsage {
|
||||
print "Usage: $0 --profile *name*\n";
|
||||
print "Following profiles are available:\n";
|
||||
print "* $_\n" for keys( %{$config{'profile'}} );
|
||||
}
|
||||
|
||||
sub DirectoryListing {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
my $profile = $config{profile_selected};
|
||||
my @files = glob ( "$config{profile}{$profile}{imageDir}/*" );
|
||||
%{$config{images_available}} = map { $_ => { 'FILEPATH' => "$_" } } @files;
|
||||
}
|
||||
|
||||
sub FindNewDataset {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
my $i = 0;
|
||||
for my $key (keys %{$config{images_available}}) {
|
||||
if (exists $config{db}{$key}) {
|
||||
print "OLD: $key\n";
|
||||
}
|
||||
elsif (!exists $config{db}{$key}) {
|
||||
print "NEW: $key\n";
|
||||
$config{image_selected} = $key;
|
||||
last;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($i == scalar(keys(%{$config{images_available}}))) {
|
||||
die "\nNO NEW FILES AVAILABLE.\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub AddImageToDB {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
my $profile = $config{profile_selected};
|
||||
my $selected = $config{image_selected};
|
||||
$config{'images_available'}{$selected}{'TIMESTAMP_UPLOADED'} = sprintf ( "%04d%02d%02d_%02d%02d%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec);
|
||||
$config{db}{$selected} = $config{images_available}{$selected};
|
||||
my $json = encode_json(\%{$config{db}});
|
||||
write_file($config{profile}{$profile}{'DBFilepath'}, { binmode => ':raw' }, $json);
|
||||
}
|
||||
|
||||
sub WipeImage {
|
||||
&Delimiter((caller(0))[3]);
|
||||
|
||||
my $image = $config{image_selected};
|
||||
print "Deleting $image...";
|
||||
unlink($image) or die "Could not delete $image!\n";
|
||||
print " done.\n";
|
||||
}
|
||||
|
||||
|
||||
# SERVER:
|
||||
# java -Dwebdriver.gecko.driver=C:\Users\vstbestprices\Seafile\INSTAFEED_SYNC\selenium\geckodriver.exe -jar C:\Users\vstbestprices\Seafile\INSTAFEED_SYNC\selenium\selenium-server-standalone-3.141.59.jar
|
||||
# java -jar /home/pi/projects/instafeed/selenium/selenium-server-standalone-3.141.59.jar
|
||||