import sys import re from pathlib import Path # Add parent workspace directory to path to import generic_downloader sys.path.append(str(Path(__file__).resolve().parents[1])) import generic_downloader def is_video_link(url): # Match standard xHamster video pages: contains /videos/ and doesn't point to user grids/channels return ( "/videos/" in url and not "/users/" in url and not "/channels/" in url and not "/creators/" in url ) if __name__ == "__main__": # JS evaluated in browser context to get the clean uploader name uploader_eval = """() => { // 1. Try channel link const channelLink = document.querySelector('a[href*="/channels/"]'); if (channelLink && channelLink.innerText.trim()) { return channelLink.innerText.trim().replace(/\\n/g, ' ').trim(); } // 2. Try user link const userLink = document.querySelector('.entity-author-container__name, a[href*="/users/"]'); if (userLink && userLink.innerText.trim()) { return userLink.innerText.trim(); } // 3. Try creator logo or class const creatorLink = document.querySelector('a[href*="/creators/"] .video-uploader__name, .video-uploader__name'); if (creatorLink && creatorLink.innerText.trim()) { return creatorLink.innerText.trim(); } return 'unknown'; }""" generic_downloader.run( site_name="xHamster", is_video_link_fn=is_video_link, next_page_selector="a:has-text('Next'), .next, a[rel='next']", video_selector="video", uploader_eval_js=uploader_eval )