fxJobFinder / app.py
orrzxz's picture
Added Proper Description
d8534b6
import os
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
import gradio as gr
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.options import Options
# Suppress unnecessary logs
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
os.environ['WDM_LOG'] = '0'
# List of companies to search
companies = [
{'url': 'https://www.distilleryvfx.com/careers', 'name': 'Distillery VFX', 'action': None},
{'url': 'https://barnstormvfx.bamboohr.com/careers', 'name': 'Barnstorm VFX', 'action': None},
{'url': 'https://careers.theembassyvfx.com/#jobs', 'name': 'The Embassy VFX', 'action': None},
{'url': 'https://www.cosavfx.com/opportunities/', 'name': 'Cosa VFX', 'action': None},
{'url': 'https://www.hydraulx.com/careers/', 'name': 'Hydraulx', 'action': None},
{'url': 'https://globalus242.dayforcehcm.com/CandidatePortal/en-US/craftyapes', 'name': 'Crafty Apes', 'action': None},
{'url': 'https://www.belofx.com/careers', 'name': 'BElofx', 'action': None},
{'url': 'https://image-engine.com/jobs/#jobs', 'name': 'Image Engine', 'action': None},
{'url': 'https://www.zoicstudios.com/join', 'name': 'Zoic Studios', 'action': None},
{'url': 'https://animallogic.com/careers/jobs/', 'name': 'Animal Logic', 'action': None},
{'url': 'https://cinesite.com/job-vacancies/?location=vancouver&division=!feature%20animation', 'name': 'Cinesite', 'action': None},
{'url': 'http://careers.digitaldomain.com/', 'name': 'Digital Domain', 'action': 'select_vancouver'},
{'url': 'https://www.company3.com/careers/', 'name': 'Company 3', 'action': None},
{'url': 'https://recruitment.phantom-fx.com/jobs/Careers', 'name': 'Phantom FX', 'action': None},
{'url': 'https://scanlinevfx.com/careers/', 'name': 'Scanline VFX', 'action': None},
{'url': 'https://www.pixomondo.com/careers', 'name': 'Pixomondo', 'action': None},
{'url': 'https://www.dneg.com/join-us/open-positions', 'name': 'DNEG', 'action': 'select_vancouver'},
{'url': 'https://jobs.disneycareers.com/search-jobs?orgIds=391-28648&ascf=[%7B%22key%22:%22custom_fields.IndustryCustomField%22,%22value%22:%22Industrial%20Light%20%26%20Magic%22%7D]', 'name': 'ILM', 'action': None},
{'url': 'https://www.imageworks.com/job-postings', 'name': 'Sony Pictures Imageworks', 'action': None}
]
target_jobs = ['FX Artist', 'FX Technical Director', 'FX TD']
MAX_WORKERS = 4 # Number of parallel browsers
# Force ChromeDriverManager to download a version compatible with Chromium 133.
# Passing the version as a positional argument.
driver_path = ChromeDriverManager("133.0.6943.41").install()
def check_company(company, chrome_options):
driver = webdriver.Chrome(
service=Service(driver_path),
options=chrome_options
)
driver.implicitly_wait(5)
try:
url = company['url']
driver.get(url)
# If a specific action is required (like selecting Vancouver), perform it.
if company['action'] == 'select_vancouver':
try:
if 'digitaldomain' in url:
dropdown = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.ID, "current-openings-select"))
)
Select(dropdown).select_by_visible_text("Vancouver")
WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".job-listings"))
)
elif 'dneg' in url:
dropdown = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "select[data-test-id='select-location']"))
)
Select(dropdown).select_by_visible_text("Vancouver")
except TimeoutException:
return None
# Check if any of the target job titles appear in the page source.
page_text = driver.page_source.lower()
found = any(job.lower() in page_text for job in target_jobs)
if found:
return (company['name'], company['url'])
except Exception:
return None
finally:
driver.quit()
return None
def run_job_search():
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--ignore-certificate-errors")
chrome_options.add_argument("--log-level=3")
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.page_load_strategy = 'eager'
# Set the binary location for Chromium.
chrome_options.binary_location = "/usr/bin/chromium"
results = []
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = []
for company in companies:
futures.append(executor.submit(check_company, company, chrome_options))
for future in as_completed(futures):
result = future.result()
if result:
results.append(result)
# Build an HTML output with clickable links.
if results:
html = "<h3>Companies with open positions:</h3><ul>"
for name, url in sorted(results, key=lambda x: x[0]):
html += f'<li><a href="{url}" target="_blank">{name}</a></li>'
html += "</ul>"
else:
html = "<p>No matching job listings found.</p>"
return html
# Define a simple Gradio interface.
demo = gr.Interface(
fn=run_job_search,
inputs=[],
outputs=gr.HTML(label="Job Search Results"),
title="FX Artist/FX TD Vancouver Job Search",
description="Click the button below to search for FX job openings across multiple companies, all located in Vancouver."
)
if __name__ == "__main__":
# To create a public link, set share=True.
demo.launch(share=True)