49 lines
1.7 KiB
Python
49 lines
1.7 KiB
Python
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
|
|
)
|