Compare commits
No commits in common. "dev" and "master" have entirely different histories.
11 changed files with 882 additions and 95 deletions
|
@ -21,7 +21,7 @@ public abstract class Extractor {
|
||||||
* cleaning/accepting/get id from urls).
|
* cleaning/accepting/get id from urls).
|
||||||
*/
|
*/
|
||||||
private final StreamingService service;
|
private final StreamingService service;
|
||||||
private final LinkHandler linkHandler;
|
public final LinkHandler linkHandler;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Localization forcedLocalization = null;
|
private Localization forcedLocalization = null;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.schabi.newpipe.extractor.services.media_ccc.MediaCCCService;
|
||||||
import org.schabi.newpipe.extractor.services.peertube.PeertubeService;
|
import org.schabi.newpipe.extractor.services.peertube.PeertubeService;
|
||||||
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudService;
|
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudService;
|
||||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeService;
|
import org.schabi.newpipe.extractor.services.youtube.YoutubeService;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.XhService;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -42,6 +43,7 @@ public final class ServiceList {
|
||||||
public static final MediaCCCService MediaCCC;
|
public static final MediaCCCService MediaCCC;
|
||||||
public static final PeertubeService PeerTube;
|
public static final PeertubeService PeerTube;
|
||||||
public static final BandcampService Bandcamp;
|
public static final BandcampService Bandcamp;
|
||||||
|
public static final XhService Xh;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When creating a new service, put this service in the end of this list,
|
* When creating a new service, put this service in the end of this list,
|
||||||
|
@ -51,9 +53,10 @@ public final class ServiceList {
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
YouTube = new YoutubeService(0),
|
YouTube = new YoutubeService(0),
|
||||||
SoundCloud = new SoundcloudService(1),
|
SoundCloud = new SoundcloudService(1),
|
||||||
MediaCCC = new MediaCCCService(2),
|
Bandcamp = new BandcampService(2),
|
||||||
PeerTube = new PeertubeService(3),
|
MediaCCC = new MediaCCCService(3),
|
||||||
Bandcamp = new BandcampService(4)
|
PeerTube = new PeertubeService(4),
|
||||||
|
Xh = new XhService(5)
|
||||||
));
|
));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
package org.schabi.newpipe.extractor.services.xh;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.comments.CommentsExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.kiosk.KioskList;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.extractors.XhSearchExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.extractors.XhStreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.linkHandler.XhSearchQueryHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.linkHandler.XhStreamLinkHandlerFactory;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
|
||||||
|
|
||||||
|
public class XhService extends StreamingService {
|
||||||
|
|
||||||
|
// name
|
||||||
|
final static String NAME = "Xhamsters";
|
||||||
|
|
||||||
|
// host
|
||||||
|
public final static String HOST = "xhamster19.desi";
|
||||||
|
public final static String BASE_URL = "https://" + HOST + "/";
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
public XhService(final int id) {
|
||||||
|
super(id, NAME, Arrays.asList(VIDEO));
|
||||||
|
}
|
||||||
|
|
||||||
|
public XhService(int id, String name, List<ServiceInfo.MediaCapability> capabilities) {
|
||||||
|
super(id, NAME, Arrays.asList(VIDEO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return BASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchExtractor getSearchExtractor(SearchQueryHandler query) {
|
||||||
|
return new XhSearchExtractor(this, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LinkHandlerFactory getStreamLHFactory() {
|
||||||
|
return new XhStreamLinkHandlerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListLinkHandlerFactory getChannelLHFactory() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListLinkHandlerFactory getPlaylistLHFactory() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchQueryHandlerFactory getSearchQHFactory() {
|
||||||
|
return XhSearchQueryHandlerFactory.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListLinkHandlerFactory getCommentsLHFactory() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionExtractor getSuggestionExtractor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SubscriptionExtractor getSubscriptionExtractor() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KioskList getKioskList() throws ExtractionException {
|
||||||
|
// Return Empty List
|
||||||
|
// patch: if (kioskId == null) { return "" }
|
||||||
|
KioskList list = new KioskList(this);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelExtractor getChannelExtractor(ListLinkHandler linkHandler) throws ExtractionException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler) throws ExtractionException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamExtractor getStreamExtractor(LinkHandler linkHandler) throws ExtractionException {
|
||||||
|
return new XhStreamExtractor(this, linkHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommentsExtractor getCommentsExtractor(ListLinkHandler linkHandler) throws ExtractionException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.extractors;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonArray;
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.schabi.newpipe.extractor.InfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.MetaInfo;
|
||||||
|
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
|
||||||
|
import org.schabi.newpipe.extractor.Page;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
|
||||||
|
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.XhService;
|
||||||
|
import static org.schabi.newpipe.extractor.services.xh.XhService.HOST;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.extractors.items.XhVideo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rakesh
|
||||||
|
*/
|
||||||
|
public class XhSearchExtractor extends SearchExtractor {
|
||||||
|
|
||||||
|
public XhSearchExtractor(final StreamingService service, final SearchQueryHandler query) {
|
||||||
|
super(service, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getSearchSuggestion() throws ParsingException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCorrectedSearch() throws ParsingException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MetaInfo> getMetaInfo() throws ParsingException {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||||
|
|
||||||
|
Page p = new Page(this.linkHandler.getUrl());
|
||||||
|
return getPage(p);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InfoItemsPage<InfoItem> getPage(Page page) throws IOException, ExtractionException {
|
||||||
|
// Collector
|
||||||
|
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
|
||||||
|
|
||||||
|
// String
|
||||||
|
String url = page.getUrl();
|
||||||
|
|
||||||
|
// HTML
|
||||||
|
Document content = Jsoup.connect(url).get();
|
||||||
|
|
||||||
|
// JS
|
||||||
|
final String js = content.getElementById("initials-script").html();
|
||||||
|
|
||||||
|
// Parse
|
||||||
|
String info_cdn = js.substring(0, js.length() - 1).replace("window.initials=", "");
|
||||||
|
System.out.println(info_cdn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// parse
|
||||||
|
final JsonObject info = JsonParser.object().from(info_cdn);
|
||||||
|
|
||||||
|
// list
|
||||||
|
final JsonArray list = info.getObject("searchResult").getArray("models");
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
|
||||||
|
final JsonObject v = list.getObject(i);
|
||||||
|
collector.commit(new XhStreamInfoItemExtractor(
|
||||||
|
new XhVideo(
|
||||||
|
v.getInt("id"),
|
||||||
|
v.getString("title"),
|
||||||
|
"",
|
||||||
|
v.getInt("duration"),
|
||||||
|
v.getInt("created"),
|
||||||
|
v.getString("pageURL"),
|
||||||
|
v.getString("thumbURL"),
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
new HashMap<String, String>()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
// log
|
||||||
|
System.out.println(v.getString("title"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// split url
|
||||||
|
int l = url.split("/").length;
|
||||||
|
|
||||||
|
int currentPage = Integer.parseInt(
|
||||||
|
page.getUrl().split("/")[l - 1]
|
||||||
|
);
|
||||||
|
|
||||||
|
// if there is next page
|
||||||
|
String nextPageUrl = "";
|
||||||
|
if (currentPage < list.size()) {
|
||||||
|
for(int i=0; i < l-1; i++){
|
||||||
|
nextPageUrl = nextPageUrl + page.getUrl().split("/")[i] + "/";
|
||||||
|
}
|
||||||
|
nextPageUrl = nextPageUrl + String.valueOf(currentPage + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InfoItemsPage<>(collector, new Page(nextPageUrl));
|
||||||
|
} catch (JsonParserException ex) {
|
||||||
|
Logger.getLogger(XhSearchExtractor.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.extractors;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonArray;
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
||||||
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
|
|
||||||
|
// Parse
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.schabi.newpipe.extractor.localization.Localization;
|
||||||
|
import static org.schabi.newpipe.extractor.services.xh.XhService.HOST;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.extractors.items.XhVideo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class XhStreamExtractor extends StreamExtractor {
|
||||||
|
|
||||||
|
// baseurl
|
||||||
|
String baseUrl;
|
||||||
|
|
||||||
|
// Object
|
||||||
|
XhVideo video;
|
||||||
|
|
||||||
|
public XhStreamExtractor(StreamingService service, LinkHandler linkHandler) {
|
||||||
|
super(service, linkHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getThumbnailUrl() throws ParsingException {
|
||||||
|
return this.video.getThumbURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderUrl() throws ParsingException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderName() throws ParsingException {
|
||||||
|
return this.video.getAuthorName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AudioStream> getAudioStreams() throws IOException, ExtractionException {
|
||||||
|
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<VideoStream> getVideoStreams() throws IOException, ExtractionException {
|
||||||
|
// video was not parsed / no video streams
|
||||||
|
if (video.getSources().isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// new list
|
||||||
|
final List<VideoStream> listStreams = new ArrayList<>();
|
||||||
|
// iterate hashmap for key values
|
||||||
|
for (Map.Entry<String, String> source: video.getSources().entrySet()) {
|
||||||
|
System.out.println(source.getKey());
|
||||||
|
System.out.println(source.getValue());
|
||||||
|
|
||||||
|
MediaFormat format = MediaFormat.MPEG_4;
|
||||||
|
if (source.getValue().endsWith(".webm")) {
|
||||||
|
format = MediaFormat.WEBM;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeliveryMethod deliver = DeliveryMethod.PROGRESSIVE_HTTP;
|
||||||
|
if (source.getValue().endsWith(".m3u8")) {
|
||||||
|
deliver = DeliveryMethod.HLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add source to list
|
||||||
|
listStreams.add(
|
||||||
|
new VideoStream.Builder()
|
||||||
|
.setId(String.valueOf(video.getId()) + "-" + source.getKey())
|
||||||
|
.setContent(source.getValue(), true)
|
||||||
|
.setIsVideoOnly(false)
|
||||||
|
.setDeliveryMethod(deliver)
|
||||||
|
.setMediaFormat(format)
|
||||||
|
.setResolution(source.getKey())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// return sources
|
||||||
|
return listStreams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<VideoStream> getVideoOnlyStreams() throws IOException, ExtractionException {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamType getStreamType() throws ParsingException {
|
||||||
|
return StreamType.VIDEO_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
|
||||||
|
// get baseurl using predefined method getBaseUrl()
|
||||||
|
// perform regex to replace the url host
|
||||||
|
String url = getLinkHandler().getUrl();
|
||||||
|
url = url.replaceAll("http?s\\:\\/\\/(xhamster?.+)\\/videos\\/", "https://" + HOST + "/videos/");
|
||||||
|
|
||||||
|
this.baseUrl = url;
|
||||||
|
|
||||||
|
final String html_ = downloader.get(url).responseBody();
|
||||||
|
|
||||||
|
// parse video info
|
||||||
|
try {
|
||||||
|
Document html = Jsoup.parse(html_);
|
||||||
|
|
||||||
|
// Parse Content
|
||||||
|
final Element e = html.getElementById("initials-script");
|
||||||
|
final String content = e.html();
|
||||||
|
|
||||||
|
String info_cdn = content.substring(0, content.length() - 1).replace("window.initials=", "");
|
||||||
|
|
||||||
|
// Use NanoJSON to convert to VideoItem
|
||||||
|
JsonObject json = JsonParser.object().from(info_cdn);
|
||||||
|
final JsonObject videoModel = json.getObject("videoModel");
|
||||||
|
|
||||||
|
// Video Info
|
||||||
|
final int id = videoModel.getInt("id");
|
||||||
|
final String title = videoModel.getString("title");
|
||||||
|
final String description = videoModel.getString("description");
|
||||||
|
final String thumbURL = videoModel.getString("thumbURL");
|
||||||
|
final int duration = videoModel.getInt("created");
|
||||||
|
final String pageURL = videoModel.getString("pageURL");
|
||||||
|
final int created = videoModel.getInt("created");
|
||||||
|
// Author
|
||||||
|
final String authorName = videoModel.getObject("author").getString("name");
|
||||||
|
final int authorID = videoModel.getObject("author").getInt("id");
|
||||||
|
final String authorThumb = videoModel.getObject("author").getString("thumbURL");
|
||||||
|
final String authorPageURL = videoModel.getObject("author").getString("pageURL");
|
||||||
|
|
||||||
|
HashMap<String, String> streams = new HashMap<>();
|
||||||
|
|
||||||
|
// iterate urls
|
||||||
|
JsonArray mp4 = json.getObject("xplayerSettings").getObject("sources").getObject("standard").getArray("mp4");
|
||||||
|
if (!mp4.isEmpty()) {
|
||||||
|
|
||||||
|
// headers
|
||||||
|
Map<String, List<String>> headers = new HashMap<>();
|
||||||
|
headers.put("Referer", Arrays.asList(baseUrl));
|
||||||
|
|
||||||
|
for (int i=0; i < mp4.size(); i++) {
|
||||||
|
JsonObject j = mp4.getObject(i);
|
||||||
|
|
||||||
|
String key = j.getString("quality");
|
||||||
|
String value = j.getString("url");
|
||||||
|
|
||||||
|
int responseCode = downloader.head(value, headers).responseCode();
|
||||||
|
System.out.println(responseCode);
|
||||||
|
|
||||||
|
streams.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the video object
|
||||||
|
this.video = new XhVideo(id, title, description, duration, created, pageURL, thumbURL, authorID, authorName, authorThumb, authorPageURL, streams);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ExtractionException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() throws ParsingException {
|
||||||
|
return video.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.extractors;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||||
|
import org.schabi.newpipe.extractor.services.xh.extractors.items.XhVideo;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rakesh
|
||||||
|
*/
|
||||||
|
public class XhStreamInfoItemExtractor implements StreamInfoItemExtractor {
|
||||||
|
|
||||||
|
XhVideo video;
|
||||||
|
|
||||||
|
public XhStreamInfoItemExtractor(XhVideo video) {
|
||||||
|
this.video = video;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamType getStreamType() throws ParsingException {
|
||||||
|
return StreamType.VIDEO_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAd() throws ParsingException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() throws ParsingException {
|
||||||
|
return video.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getViewCount() throws ParsingException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderName() throws ParsingException {
|
||||||
|
return video.getAuthorName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderUrl() throws ParsingException {
|
||||||
|
return video.getAuthorPageURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUploaderAvatarUrl() throws ParsingException {
|
||||||
|
return video.getAuthorThumb();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUploaderVerified() throws ParsingException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTextualUploadDate() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateWrapper getUploadDate() throws ParsingException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() throws ParsingException {
|
||||||
|
return this.video.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrl() throws ParsingException {
|
||||||
|
return this.video.getPageURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getThumbnailUrl() throws ParsingException {
|
||||||
|
return this.video.getThumbURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.extractors.items;
|
||||||
|
|
||||||
|
import com.grack.nanojson.JsonObject;
|
||||||
|
import com.grack.nanojson.JsonParser;
|
||||||
|
import com.grack.nanojson.JsonParserException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rakesh
|
||||||
|
*/
|
||||||
|
public class XhVideo {
|
||||||
|
|
||||||
|
int id;
|
||||||
|
|
||||||
|
String title;
|
||||||
|
String description;
|
||||||
|
|
||||||
|
int duration;
|
||||||
|
int created;
|
||||||
|
|
||||||
|
String pageURL;
|
||||||
|
String thumbURL;
|
||||||
|
|
||||||
|
int authorID;
|
||||||
|
String authorName;
|
||||||
|
String authorThumb;
|
||||||
|
String authorPageURL;
|
||||||
|
|
||||||
|
Map<String, String> sources = null;
|
||||||
|
|
||||||
|
public XhVideo(int id, String title, String description, int duration, int created, String pageURL, String thumbURL, int authorID, String authorName, String authorThumb, String authorPageURL, HashMap<String, String> streams) {
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.description = description;
|
||||||
|
|
||||||
|
this.duration = duration;
|
||||||
|
this.created = created;
|
||||||
|
|
||||||
|
this.pageURL = pageURL;
|
||||||
|
this.thumbURL = thumbURL;
|
||||||
|
|
||||||
|
this.authorID = authorID;
|
||||||
|
this.authorName = authorName;
|
||||||
|
this.authorThumb = authorThumb;
|
||||||
|
this.authorPageURL = authorPageURL;
|
||||||
|
|
||||||
|
this.sources = streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPageURL() {
|
||||||
|
return pageURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getThumbURL() {
|
||||||
|
return thumbURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAuthorID() {
|
||||||
|
return authorID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorName() {
|
||||||
|
return authorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorThumb() {
|
||||||
|
return authorThumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthorPageURL() {
|
||||||
|
return authorPageURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getSourceURLs() throws IOException, JsonParserException {
|
||||||
|
/*
|
||||||
|
Parse
|
||||||
|
*/
|
||||||
|
Document html = Jsoup.connect(this.pageURL).get();
|
||||||
|
// Parse Content
|
||||||
|
String content = html.selectFirst("#initials-script").html();
|
||||||
|
String info_cdn = content.substring(0, content.length() - 1).replace("window.initials=", "");
|
||||||
|
|
||||||
|
// Use NanoJSON to convert to VideoItem
|
||||||
|
final JsonObject json = JsonParser.object().from(info_cdn);
|
||||||
|
final JsonObject videoModel = json.getObject("videoModel");
|
||||||
|
|
||||||
|
HashMap<String, String> streams = new HashMap<>();
|
||||||
|
|
||||||
|
// iterate urls
|
||||||
|
JsonObject mp4 = videoModel.getObject("sources").getObject("mp4");
|
||||||
|
if (!mp4.isEmpty()) {
|
||||||
|
final Object keys[] = mp4.keySet().toArray();
|
||||||
|
for (int i = 0; i < keys.length; i++) {
|
||||||
|
final String key = keys[i].toString();
|
||||||
|
System.out.println(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getSources() {
|
||||||
|
// if (sources.isEmpty() || sources == null) {
|
||||||
|
// // check if pageurl is set
|
||||||
|
// if (!pageURL.isBlank() || !pageURL.isEmpty()) {
|
||||||
|
// // parse the page
|
||||||
|
// try {
|
||||||
|
// this.sources = getSourceURLs();
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.linkHandler;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
|
||||||
|
import static org.schabi.newpipe.extractor.services.xh.XhService.BASE_URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rakesh
|
||||||
|
*/
|
||||||
|
public class XhSearchQueryHandlerFactory extends SearchQueryHandlerFactory {
|
||||||
|
|
||||||
|
public static XhSearchQueryHandlerFactory getInstance() {
|
||||||
|
return new XhSearchQueryHandlerFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrl(final String query,
|
||||||
|
final List<String> contentFilter,
|
||||||
|
final String sortFilter) throws ParsingException {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// log
|
||||||
|
System.out.println(BASE_URL + "/search/" + URLEncoder.encode(query, "UTF-8") + "/1");
|
||||||
|
|
||||||
|
return BASE_URL + "/search/" + URLEncoder.encode(query, "UTF-8") + "/1";
|
||||||
|
} catch (final UnsupportedEncodingException e) {
|
||||||
|
throw new ParsingException("query \"" + query + "\" could not be encoded", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.schabi.newpipe.extractor.services.xh.linkHandler;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
||||||
|
import static org.schabi.newpipe.extractor.services.xh.XhService.HOST;
|
||||||
|
import org.schabi.newpipe.extractor.utils.Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Rakesh
|
||||||
|
*/
|
||||||
|
public class XhStreamLinkHandlerFactory extends LinkHandlerFactory {
|
||||||
|
|
||||||
|
private static final String PATTERN = "http?s\\:\\/\\/(xhamster?.+)\\/videos\\/([a-zA-Z0-9-_\\.]+)";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId(String url) throws ParsingException {
|
||||||
|
String streamId = null;
|
||||||
|
try {
|
||||||
|
streamId = Parser.matchGroup(PATTERN, url, 2);
|
||||||
|
} catch (final Parser.RegexException ignored) {}
|
||||||
|
|
||||||
|
// return id
|
||||||
|
return streamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrl(String id) throws ParsingException {
|
||||||
|
return "https://" + HOST + "/videos/" + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onAcceptUrl(String url) throws ParsingException {
|
||||||
|
try {
|
||||||
|
return getId(url) != null;
|
||||||
|
} catch (final ParsingException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
0
gradlew
vendored
Executable file → Normal file
0
gradlew
vendored
Executable file → Normal file
Loading…
Reference in a new issue