From 36e79096586c4e72f53a222d0067d96128856b76 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Mon, 24 Nov 2025 15:06:20 +0100 Subject: [PATCH] Neu: xHamster User Bridge --- XHamsterUserBridge.php | 138 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 XHamsterUserBridge.php diff --git a/XHamsterUserBridge.php b/XHamsterUserBridge.php new file mode 100644 index 0000000..bd45e69 --- /dev/null +++ b/XHamsterUserBridge.php @@ -0,0 +1,138 @@ + [ + 'name' => 'Username', + 'type' => 'text', + 'required' => true, + 'exampleValue' => 'lolliepopxxx' + ], + 'limit' => [ + 'name' => 'Limit', + 'type' => 'number', + 'defaultValue' => 10, + 'required' => false + ] + ] + ]; + + private $profileName = null; + + public function getIcon() + { + return 'https://www.google.com/s2/favicons?domain=xhamster.com&sz=32'; + } + + public function getName() + { + $username = $this->getInput('username'); + if ($username) { + $name = $this->profileName ?? $username; + return $name . ' - xHamster'; + } + return self::NAME; + } + + public function getURI() + { + $username = $this->getInput('username'); + if ($username) { + return self::URI . '/users/' . $username . '/videos'; + } + return self::URI; + } + + public function collectData() + { + $username = $this->getInput('username'); + $limit = $this->getInput('limit') ?: 10; + + $url = self::URI . '/users/' . $username . '/videos'; + + // xHamster requires specific headers to bypass Cloudflare + $headers = [ + 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language: en-US,en;q=0.5' + ]; + + $html = getSimpleHTMLDOM($url, $headers); + if (!$html) { + returnServerError('Could not fetch page: ' . $url); + } + + // Try to get profile name from page title + $titleElement = $html->find('title', 0); + if ($titleElement) { + $pageTitle = html_entity_decode($titleElement->plaintext, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + // Extract name from title like "Lolliepopxxx's videos" + if (preg_match('/^(.+?)\'s\s+videos/i', $pageTitle, $matches)) { + $this->profileName = trim($matches[1]); + } + } + + $videos = $html->find('.video-thumb'); + $count = 0; + + foreach ($videos as $video) { + if ($count >= $limit) { + break; + } + + $videoId = $video->getAttribute('data-video-id'); + if (!$videoId) { + continue; + } + + // Get the link element + $linkElement = $video->find('a', 0); + $href = $linkElement ? $linkElement->getAttribute('href') : ''; + if (!$href) { + continue; + } + + // Get title and thumbnail from img + $imgElement = $video->find('img', 0); + $title = ''; + $thumbnail = ''; + if ($imgElement) { + $title = $imgElement->getAttribute('alt') ?: ''; + $thumbnail = $imgElement->getAttribute('src'); + if (!$thumbnail || strpos($thumbnail, 'data:') === 0) { + $thumbnail = $imgElement->getAttribute('data-src') ?: ''; + } + } + + if (!$title) { + $title = 'Video ' . $videoId; + } + + // Build content (just the thumbnail) + $content = ''; + if ($thumbnail) { + $content = ''; + } + + $item = [ + 'title' => $title, + 'uri' => $href, + 'content' => $content, + 'author' => $this->profileName ?? $username, + 'uid' => 'xhamster-' . $videoId + ]; + + $this->items[] = $item; + $count++; + } + } +}