Initial version

This commit is contained in:
csd4ni3l
2025-06-09 17:16:48 +02:00
commit 98dafd12f9
8 changed files with 1227 additions and 0 deletions

109
main.py Normal file
View File

@@ -0,0 +1,109 @@
import os, time, argparse, re
from selenium.webdriver import Chrome, ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from zipfile import ZipFile
def parse_args():
parser = argparse.ArgumentParser(description="Download full-res images from a Google Photos album using Selenium.")
parser.add_argument("--album-urls", nargs="+", required=True, help="Google Photos album URL")
parser.add_argument("--output-dir", required=True, help="Directory to save downloaded images")
parser.add_argument("--driver-path", default=None, help="Custom Chrome driver path")
parser.add_argument("--profile-dir", default=None, help="Chrome user data directory for session reuse")
parser.add_argument("--headless", action="store_true", help="Run Chrome headlessly")
return parser.parse_args()
def setup_driver(driver_path=None, profile_dir=None, headless=True):
chrome_options = Options()
if profile_dir:
chrome_options.add_argument(f"--user-data-dir={profile_dir}")
if headless:
chrome_options.add_argument("--headless")
prefs = {
"download.prompt_for_download": False,
"download.default_directory": os.path.join(os.getcwd(), "gp_temp"),
"profile.default_content_setting_values.automatic_downloads": 1
}
chrome_options.add_experimental_option("prefs", prefs)
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--window-size=1920,1080")
if driver_path:
service = ChromeService(executable_path=driver_path)
return Chrome(options=chrome_options, service=service)
else:
return Chrome(options=chrome_options)
def find_zip_file():
for file in os.listdir("gp_temp"):
if file.endswith(".zip"):
return file
def main():
args = parse_args()
driver = setup_driver(profile_dir=args.profile_dir, headless=args.headless)
os.makedirs("gp_temp", exist_ok=True)
if not os.path.exists(args.output_dir) or not os.path.isdir(args.output_dir):
print("ERROR: Invalid output directory.")
return
for album_url in args.album_urls:
if re.match(r'^https?://photos\.app\.goo\.gl/[A-Za-z0-9]+$', album_url) is None:
print(f"Invalid album URL: {album_url}")
continue
print(f"Opening {album_url}")
driver.get(album_url)
print("Waiting for menu button...")
try:
menu_button = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@aria-label="More options"]')))
except TimeoutException:
print("ERROR: Could not find more options button in time.")
print("Continuing with next album.")
continue
print("Clicking menu button...")
menu_button.click()
print("Waiting for download all button...")
try:
download_all_button = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@aria-label="Download all"]')))
except TimeoutException:
print("ERROR: Could not find download all button in time.")
print("Continuing with next album.")
continue
print("Clicking the download all button...")
download_all_button.click()
print("Waiting for a zip file to land in gp_temp...")
zip_file = None
while not zip_file:
zip_file = find_zip_file()
time.sleep(0.1)
print(f"Zip file downloaded, extracting to {args.output_dir}")
with ZipFile(f"gp_temp/{zip_file}") as opened_file:
opened_file.extractall(args.output_dir)
print("Deleting zip file...")
os.remove(f"gp_temp/{zip_file}")
print(f"Succesfully extracted to {args.output_dir}")
os.removedirs("gp_temp")
driver.quit()
if __name__ == '__main__':
main()