diff --git a/README.md b/README.md index e8b583d..082aee3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Adding support for the following sources: - getyarn.io - Text To Speech (if prefixed with `speak:`) - TikTok (in beta, works on _most_ videos and **will** break all the time) -- PornHub +- PornHub (search by prefixing with `phsearch:`) - soundgasm ## Lavalink version compatibility diff --git a/application.yml b/application.yml index 57afb8e..d0cb222 100644 --- a/application.yml +++ b/application.yml @@ -38,7 +38,7 @@ plugins: getyarn: false # www.getyarn.io clypit: false # www.clyp.it tts: false # speak:Words to speak - pornhub: false # should be self-explanatory + pornhub: true # should be self-explanatory reddit: false # should be self-explanatory ocremix: true # www.ocremix.org tiktok: true # tiktok.com diff --git a/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PHHelpers.java b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PHHelpers.java new file mode 100644 index 0000000..d07f8c2 --- /dev/null +++ b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PHHelpers.java @@ -0,0 +1,31 @@ +package com.dunctebot.sourcemanagers.pornhub; + +import com.sedmelluq.discord.lavaplayer.tools.Units; +import com.sedmelluq.discord.lavaplayer.track.AudioTrack; +import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; +import org.jsoup.nodes.Element; + +public final class PHHelpers { + public static AudioTrack trackFromSearchElement(Element element, PornHubAudioSourceManager mngr) { + final var info = infoFromSearchElement(element); + return new PornHubAudioTrack(info, mngr); + } + + public static AudioTrackInfo infoFromSearchElement(Element element) { + // TODO: parse min:sec duration + // final var durText = element.getElementsByClass("duration").first().text(); + + // System.out.println(durText); + + final String title = element.getElementsByClass("title").first().text(); + final String author = element.getElementsByClass("usernameWrap").first().text(); + final long duration = Units.CONTENT_LENGTH_UNKNOWN; + final String identifier = element.attr("data-video-vkey"); + final String uri = "https://www.pornhub.com/view_video.php?viewkey=" + identifier; + final String imageUrl = element.getElementsByTag("img").first().attr("src"); + + return new AudioTrackInfo( + title, author, duration, identifier, false, uri, imageUrl, null + ); + } +} diff --git a/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioSourceManager.java b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioSourceManager.java index 00138e4..1a539ef 100644 --- a/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioSourceManager.java +++ b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioSourceManager.java @@ -22,20 +22,20 @@ import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity; import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; -import com.sedmelluq.discord.lavaplayer.track.AudioItem; -import com.sedmelluq.discord.lavaplayer.track.AudioReference; -import com.sedmelluq.discord.lavaplayer.track.AudioTrack; -import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; +import com.sedmelluq.discord.lavaplayer.track.*; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.jsoup.Jsoup; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class PornHubAudioSourceManager extends AbstractDuncteBotHttpSource { private static final String DOMAIN_PATTERN = "https?://([a-z]+\\.)?pornhub\\.(com|net|org)"; @@ -44,6 +44,9 @@ public class PornHubAudioSourceManager extends AbstractDuncteBotHttpSource { public static final Pattern VIDEO_INFO_REGEX = Pattern.compile("var flashvars_\\d+ = (\\{.+})"); private static final Pattern MODEL_INFO_REGEX = Pattern.compile("var MODEL_PROFILE = (\\{.+})"); + private static final String SEARCH_PREFIX = "phsearch"; + private static final String SEARCH_PREFIX_DEFAULT = "phsearch:"; + @Override public String getSourceName() { return "pornhub"; @@ -51,11 +54,11 @@ public String getSourceName() { @Override public AudioItem loadItem(AudioPlayerManager manager, AudioReference reference) { - if (!VIDEO_REGEX.matcher(reference.identifier).matches()) { - return null; - } - try { + if (!VIDEO_REGEX.matcher(reference.identifier).matches()) { + return processAsSearchQuery(reference); + } + return loadItemOnce(reference); } catch (Exception e) { throw ExceptionTools.wrapUnfriendlyExceptions("Something went wrong", Severity.SUSPICIOUS, e); @@ -77,6 +80,56 @@ public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) { return new PornHubAudioTrack(trackInfo, this); } + private AudioItem processAsSearchQuery(AudioReference reference) throws IOException { + if (reference.identifier.startsWith(SEARCH_PREFIX)) { + if (reference.identifier.startsWith(SEARCH_PREFIX_DEFAULT)) { + return attemptSearch(reference.identifier.substring(SEARCH_PREFIX_DEFAULT.length()).trim()); + } + } + + return null; + } + + public AudioItem attemptSearch(String query) throws IOException { + // https://www.pornhub.com/video/search?search=a+few+words + + final String html = loadHtml( + "https://www.pornhub.com/video/search?search=" + + URLEncoder.encode(query, StandardCharsets.UTF_8) + ); + + if (html == null) { + notAvailable(); + } + + // ul#videoSearchResult -> contains results + // li.pcVideoListItem -> contains all videos + + final var document = Jsoup.parse(html); + final var results = document.select("ul#videoSearchResult").first(); + + if (results == null) { + throw new FriendlyException( + "Search result element not found, contact dev", + Severity.SUSPICIOUS, + new IllegalArgumentException("Search result element not found, contact dev") + ); + } + + final var videos = results.select("li.pcVideoListItem"); + + final var phTracks = videos.stream() + .map((it) -> PHHelpers.trackFromSearchElement(it, this)) + .collect(Collectors.toList()); + + return new BasicAudioPlaylist( + "Search results for " + query, + phTracks, + null, + true + ); + } + private AudioItem loadItemOnce(AudioReference reference) throws IOException { final String html = loadHtml(reference.identifier); @@ -163,7 +216,7 @@ private JsonBrowser getModelInfo(String html) throws IOException { private String loadHtml(String url) throws IOException { final HttpGet httpGet = new HttpGet(url); - httpGet.setHeader("Cookie", "platform=pc; age_verified=1"); + httpGet.setHeader("Cookie", "platform=pc; age_verified=1; accessAgeDisclaimerPH=1"); try (final CloseableHttpResponse response = getHttpInterface().execute(httpGet)) { final int statusCode = response.getStatusLine().getStatusCode(); diff --git a/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioTrack.java b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioTrack.java index 0d68162..65056f5 100644 --- a/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioTrack.java +++ b/source-managers/src/main/java/com/dunctebot/sourcemanagers/pornhub/PornHubAudioTrack.java @@ -156,7 +156,7 @@ private String parseJsValueToUrl(String htmlPage, String js) { private String loadMp4Url(String jsonPage, String cookie) throws IOException { final HttpGet mediaGet = new HttpGet(jsonPage); - mediaGet.setHeader("Cookie", cookie + "; platform=pc; age_verified=1"); + mediaGet.setHeader("Cookie", cookie + "; platform=pc; age_verified=1; accessAgeDisclaimerPH=1"); mediaGet.setHeader("Referer", getPlayerPage(this.trackInfo.identifier)); try (final CloseableHttpResponse response = this.getSourceManager().getHttpInterface().execute(mediaGet)) { diff --git a/source-managers/src/test/java/PHSearchTest.java b/source-managers/src/test/java/PHSearchTest.java new file mode 100644 index 0000000..0d8d08d --- /dev/null +++ b/source-managers/src/test/java/PHSearchTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Duncan "duncte123" Sterken + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.dunctebot.sourcemanagers.pornhub.PornHubAudioSourceManager; +import com.dunctebot.sourcemanagers.pornhub.PornHubAudioTrack; +import com.sedmelluq.discord.lavaplayer.track.AudioReference; + +public class PHSearchTest { + public static void main(String[] args) throws Exception { +// final var link = "https://www.pornhub.com/view_video.php?viewkey=ph5fc5ef73cfc87"; + final var link = "https://www.pornhub.com/view_video.php?viewkey=ph6383940fcb8d7"; + final var mngr = new PornHubAudioSourceManager(); + + final var test = mngr.attemptSearch("minecraft bedwards"); + + System.out.println(test); + } +}