MasterPuppeteer

Updates fix broken issues.

......@@ -40,8 +40,8 @@ task jarEach << {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.jsoup:jsoup:1.9.2'
compile 'org.json:json:20151123'
compile 'org.jsoup:jsoup:1.10.3'
compile 'org.json:json:20160212'
compile 'org.codehaus.groovy:groovy:2.4.7'
compile project(':puppet')
}
\ No newline at end of file
......
......@@ -56,7 +56,7 @@ public class PasteyPuppet implements InstallablePuppet {
name = "Untitled"
}
} else {
pid = "http://pastebin.com/raw/${pid}"
pid = "https://pastebin.com/raw/${pid}"
}
return new PasteyM3U8Puppet(parent, true, name, pid)
}
......
......@@ -25,13 +25,14 @@
package tv.puppetmaster.uk
import org.json.JSONArray
import org.json.JSONObject
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import tv.puppetmaster.data.i.*
import tv.puppetmaster.data.i.Puppet.PuppetIterator
import javax.xml.parsers.DocumentBuilderFactory
import java.util.regex.Matcher
class BBCiPlayerPuppet implements InstallablePuppet {
......@@ -83,8 +84,8 @@ class BBCiPlayerPuppet implements InstallablePuppet {
genres: "FAMILY_KIDS",
description: "For children aged seven and above.",
url: "http://www.bbc.co.uk/tv/cbbc/a-z",
imageUrl: "http://www.astra2sat.com/wp-content/uploads/2016/03/CBBC-Logo.jpg",
backgroundImageUrl: "http://www.redbeecreative.com/storage/imagecache/poster_medium/work_block_carousel/cbbc-logo1-15040.jpg",
imageUrl: "https://pbs.twimg.com/profile_images/709260325082820608/fKscOiDr_400x400.jpg",
backgroundImageUrl: "https://www.standard.co.uk/s3fs-public/thumbnails/image/2016/03/14/17/cbbc-logo.jpg",
preferredRegion: "uk",
liveStreamUrl: "http://a.files.bbci.co.uk/media/live/manifesto/audio_video/simulcast/hls/uk/abr_hdtv/llnw/cbbc_hd.m3u8",
],
......@@ -139,7 +140,7 @@ class BBCiPlayerPuppet implements InstallablePuppet {
genres: "NEWS",
description: "A part-time Scottish Gaelic channel.",
url: "http://www.bbc.co.uk/tv/bbcalba/a-z",
imageUrl: "https://freebets.uk/img/channels/300x300/BBC-ALBA.png",
imageUrl: "https://pbs.twimg.com/profile_images/583295704451403777/xfu7ocvP.jpg",
backgroundImageUrl: "https://upload.wikimedia.org/wikipedia/en/thumb/a/a5/BBC_Alba.svg/1280px-BBC_Alba.svg.png",
preferredRegion: "uk",
liveStreamUrl: "http://a.files.bbci.co.uk/media/live/manifesto/audio_video/simulcast/hls/uk/hls_tablet/llnw/bbc_alba.m3u8",
......@@ -396,7 +397,7 @@ class BBCiPlayerPuppet implements InstallablePuppet {
}
}
(1..24).each {
def final String imageUrl = "https://pbs.twimg.com/profile_images/581471317272211456/bmZn02Sz.png"
def final String imageUrl = "https://pbs.twimg.com/profile_images/875650343320735744/IgVA7tKZ_400x400.jpg"
list << [
name : "BBC Red Button " + it,
genres : "SPORTS",
......@@ -444,7 +445,7 @@ class BBCiPlayerPuppet implements InstallablePuppet {
ParentPuppet redButtonPuppet = new BBCiPlayerPuppet(this, true, "\tRed Button", "Interactive television services", null, null, null)
PuppetIterator redButtonChildren = new BBCiPlayerPuppetIterator()
(1..24).each {
def final String imageUrl = "https://pbs.twimg.com/profile_images/581471317272211456/bmZn02Sz.png"
def final String imageUrl = "https://pbs.twimg.com/profile_images/875650343320735744/IgVA7tKZ_400x400.jpg"
def final String backgroundImageUrl = "http://studiomh.co.uk/wp-content/uploads/2015/06/bbc_red_button21.jpg"
redButtonChildren.add(new BBCiPlayerSourcesPuppet(redButtonPuppet, "Red Button " + it, "Red Button", null, imageUrl, backgroundImageUrl, true, sprintf("http://a.files.bbci.co.uk/media/live/manifesto/audio_video/webcast/hls/uk/abr_hdtv/llnw/sport_stream_%02d.m3u8", it)))
redButtonChildren.add(new BBCiPlayerSourcesPuppet(redButtonPuppet, "Red Button " + it + "b", "Red Button", null, imageUrl, backgroundImageUrl, true, sprintf("http://a.files.bbci.co.uk/media/live/manifesto/audio_video/webcast/hls/uk/abr_hdtv/llnw/sport_stream_%02db.m3u8", it)))
......@@ -465,10 +466,10 @@ class BBCiPlayerPuppet implements InstallablePuppet {
if (mUrl != null) {
Document document = Jsoup.connect(mUrl).get()
for (Element item : document.select(".list-item:not(.unavailable),.grid__item .single-item,.grid__item .thumbnail-item,.view-more-container")) {
if (item.hasClass("view-more-container") || (item.hasAttr("data-object-type") && item.attr("data-object-type") == "editorial-promo")) {
def Element relatedVideo = item.parent().firstElementSibling()
def String title = relatedVideo.select(".top-title,.thumbnail-item__title").first().text()
def String description = relatedVideo.select(".master-brand .medium,.thumbnail-item__subtitle").first().text()
if (item.select(".list-item__episodes-button") || item.hasClass("view-more-container") || (item.hasAttr("data-object-type") && item.attr("data-object-type") == "editorial-promo")) {
def Element relatedVideo = item.select(".list-item__episodes-button") ? item : item.parent().firstElementSibling()
def String title = relatedVideo.select(".list-item__title,.top-title,.thumbnail-item__title").first().text()
def String description = relatedVideo.select(".list-item__programme-info,.master-brand .medium,.thumbnail-item__subtitle").first().text()
def String imageUrl = null
if (item.select(".r-image")) {
imageUrl = item.select(".r-image").first().attr("data-ip-src")
......@@ -481,10 +482,10 @@ class BBCiPlayerPuppet implements InstallablePuppet {
imageUrl = "http://ichef.bbci.co.uk/images/ic/304x304/" + imageId + ".jpg"
backgroundImageUrl = "http://ichef.bbci.co.uk/images/ic/1248x702/" + imageId + ".jpg"
}
mChildren.add(new BBCiPlayerPuppet(this, false, title, description, imageUrl, backgroundImageUrl, item.absUrl("href")))
mChildren.add(new BBCiPlayerPuppet(this, false, title, description, imageUrl, backgroundImageUrl, item.select(".list-item__episodes-button") ? item.select(".list-item__episodes-button a").first().absUrl("href") : item.absUrl("href")))
} else if (item.hasClass("single-item") || item.hasClass("thumbnail-item")) {
mChildren.add(new BBCiPlayerSourcesPuppet(this, item.parent()))
} else if (item.select(".view-more-container").first() == null) {
} else if (item.select(".list-item__episodes-button,.view-more-container").first() == null) {
mChildren.add(new BBCiPlayerSourcesPuppet(this, item))
}
}
......@@ -641,13 +642,18 @@ class BBCiPlayerPuppet implements InstallablePuppet {
BBCiPlayerSourcesPuppet(parent, Element item) {
mParent = parent
mUrl = item.select("a.list-item-link,a.single-item,a.thumbnail-item").first().absUrl("href")
def link = item.select("a.list-item__main-link,a.list-item-link,a.single-item,a.thumbnail-item").first()
mUrl = link.absUrl("href")
mName = item.select(".title.top-title,.single-item__title,.thumbnail-item__title").first().text().trim()
try {
def String subtitle = item.select(".subtitle,.single-item__subtitle,.thumbnail-item__subtitle").first().text().trim()
mName += ": " + subtitle
} catch (ignore) {
if (link.hasAttr("title")) {
mName = link.attr("title")
} else {
mName = item.select(".list-item__title,.title.top-title,.single-item__title,.thumbnail-item__title").first().text().trim()
try {
def String subtitle = item.select(".list-item__programme-info__subtitle,.subtitle,.single-item__subtitle,.thumbnail-item__subtitle").first().text().trim()
mName += ": " + subtitle
} catch (ignore) {
}
}
try {
......@@ -656,8 +662,8 @@ class BBCiPlayerPuppet implements InstallablePuppet {
}
try {
if (item.select(".synopsis,.overlay__text")) {
mDescription = item.select(".synopsis,.overlay__text").first().text().trim()
if (item.select(".list-item__programme-info__synopsis,.synopsis,.overlay__text")) {
mDescription = item.select(".list-item__programme-info__synopsis,.synopsis,.overlay__text").first().text().trim()
} else {
mDescription = item.select(".single-item__desc__label,.thumbnail-item__desc__label").first().text().trim()
}
......@@ -688,8 +694,8 @@ class BBCiPlayerPuppet implements InstallablePuppet {
def String duration = item.select("a.list-item-link,a.single-item,a.thumbnail-item").first().attr("data-duration")
if (!duration.isEmpty()) {
mDuration = Long.parseLong(duration) * 1000
} else if (item.select(".duration")) {
duration = item.select(".duration").first().text()
} else if (item.select(".metadata__item--last,.duration")) {
duration = item.select(".metadata__item--last,.duration").first().text()
def Matcher matcher = duration =~ /(\d+) min/
if (matcher.find()) {
mDuration = Long.parseLong(matcher.group(1)) * 60000
......@@ -792,7 +798,10 @@ class BBCiPlayerPuppet implements InstallablePuppet {
def class BBCiPlayerSourceIterator implements SourcesPuppet.SourceIterator {
static final String URL_TEMPLATE = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/"
static final String PLAYLIST_TEMPLATE = "http://www.bbc.co.uk/programmes/%s/playlist.json"
//static final String URL_TEMPLATE = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/%s"
static final String PROXY = "http://www.hidemeproxy.net/hide.php?b=0&f=norefer&u="
static final String URL_TEMPLATE = "http://open.live.bbc.co.uk/mediaselector/6/select/version/2.0/mediaset/pc/vpid/%s/format/json"
def ArrayList<SourcesPuppet.SourceDescription> mSources = null
def int currentIndex = 0
......@@ -801,59 +810,87 @@ class BBCiPlayerPuppet implements InstallablePuppet {
boolean hasNext() {
if (mSources == null) {
mSources = new ArrayList<>()
def ArrayList<SourcesPuppet.SourceDescription> akamaiSources = new ArrayList<>()
def ArrayList<SourcesPuppet.SourceDescription> nonAkamaiSources = new ArrayList<>()
if (mDirectUrl) {
def SourcesPuppet.SourceDescription source = new SourcesPuppet.SourceDescription()
source.url = BBCiPlayerSourcesPuppet.this.mDirectUrl
mSources.add(source)
} else {
def Matcher matcher
if (mUrl.contains("|")) {
def String vpid
matcher = mUrl =~ /\/([bp]0[a-z0-9]{6})/
if (matcher.find()) {
def String pid = matcher.group(1)
vpid = new JSONObject(new URL(sprintf(PLAYLIST_TEMPLATE, pid)).getText()).getJSONObject("defaultAvailableVersion").getString("pid")
} else if (mUrl.contains("|")) {
def String page = new URL("http://www.bbc.co.uk/iplayer/episode/" + mUrl.split("\\|").last()).getText()
matcher = page =~ /"vpid":"(.+?)"/
vpid = matcher.group(1)
} else {
def String page = new URL(mUrl).getText()
matcher = page =~ /"vpid":"(.+?)"/
vpid = matcher.group(1)
}
if (matcher.find()) {
def String url = URL_TEMPLATE + matcher.group(1)
def org.w3c.dom.NodeList media
try {
def org.w3c.dom.Document root = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(url)
media = root.getElementsByTagName("media")
} catch (Exception ex) {
return false
if (vpid) {
def String url = sprintf(URL_TEMPLATE, vpid)
def String page
if (PROXY) {
page = new URL(PROXY + URLEncoder.encode(url, "UTF-8")).getText(requestProperties: [
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
'Referer': PROXY,
])
} else {
page = new URL(url).getText()
}
def urls = []
for (int i = 0; i < media.getLength(); i++) {
def org.w3c.dom.Element node = (org.w3c.dom.Element) media.item(i)
def org.w3c.dom.NodeList elements = node.getElementsByTagName("connection")
for (int j = 0; j < elements.getLength(); j++) {
def org.w3c.dom.Element element = (org.w3c.dom.Element) elements.item(j)
if (element.getAttribute("transferFormat") == "hls") {
def String stream = element.getAttribute("href")
if (!urls.contains(stream)) {
def JSONArray medium = new JSONObject(page).getJSONArray("media")
for (int i = 0; i < medium.length(); i++) {
def JSONObject media = medium.getJSONObject(i)
if (media.getString("kind") == "video") {
def JSONArray items = media.getJSONArray("connection")
for (int j = 0; j < items.length(); j++) {
def JSONObject item = items.getJSONObject(j)
if (item.getString("transferFormat") == "dash") {
def SourcesPuppet.SourceDescription source = new SourcesPuppet.SourceDescription()
source.url = stream
source.bitrate = Long.parseLong(node.getAttribute("bitrate"))
source.width = Integer.parseInt(node.getAttribute("width"))
source.height = Integer.parseInt(node.getAttribute("height"))
mSources.add(source)
if (element.getAttribute("supplier").contains("akamai")) {
mSources << source
source.url = item.getString("href")
source.bitrate = Long.parseLong(media.getString("bitrate"))
source.width = Long.parseLong(media.getString("width"))
source.height = Long.parseLong(media.getString("height"))
if (item.getString("supplier").contains("akamai") && !source.url.contains("akamaized")) {
akamaiSources << source
} else {
mSources.add(0, source) // Prioritize non-akamai links
nonAkamaiSources << source
}
}
} else if (node.getAttribute("service") == "captions") {
}
} else if (media.getString("kind") == "captions") {
def JSONArray items = media.getJSONArray("connection")
for (int j = 0; j < items.length(); j++) {
def JSONObject item = items.getJSONObject(j)
SourcesPuppet.SubtitleDescription subtitle = new SourcesPuppet.SubtitleDescription()
subtitle.url = element.getAttribute("href")
subtitle.mime = node.getAttribute("type")
subtitle.url = item.getString("href")
subtitle.mime = "text/xml"
BBCiPlayerSourcesPuppet.this.mSubtitles.add(subtitle)
}
}
}
}
}
[nonAkamaiSources, akamaiSources].each { sources ->
Collections.sort(sources, new Comparator<SourcesPuppet.SourceDescription>() {
@Override
public int compare(SourcesPuppet.SourceDescription lhs, SourcesPuppet.SourceDescription rhs) {
// Reverse sort so higher quality is tried first
return Long.compare(rhs.bitrate, lhs.bitrate)
}
})
sources.each {
mSources << it
}
}
}
return currentIndex < mSources.size()
}
......
package tv.puppetmaster.uk
import org.json.JSONArray
import org.json.JSONObject
import tv.puppetmaster.data.i.*
import tv.puppetmaster.data.i.Puppet.PuppetIterator
import tv.puppetmaster.data.i.SourcesPuppet.SourceDescription
public class WatchAllChannelsPuppet implements InstallablePuppet {
static final int VERSION_CODE = 5
static final SOURCES = [
"Entertainment": [
[
name: "Channel 4",
description: "Channel 4 Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/2?protocol=hls",
image: "https://pbs.twimg.com/profile_images/700000517485957125/lmuvsuVE.jpg",
background: "http://www.piksel.com/wp-content/uploads/2015/04/All-4-logo.jpeg",
],
[
name: "Channel 5",
description: "Channel 5 Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/22?protocol=hls",
image: "https://s-media-cache-ak0.pinimg.com/originals/04/9b/a6/049ba62942635fe778e605f3a90e61e1.png",
background: "https://s3-eu-central-1.amazonaws.com/centaur-wp/creativereview/prod/content/uploads/2016/02/C5_Pink_Blue-e1455043595606.jpg",
],
[
name: "E4 TV",
description: "E4 TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/65?protocol=hls",
image: "https://i1.sndcdn.com/artworks-000101978749-aitv4x-t500x500.jpg",
background: "http://www.channel4.com/media/images/CorporatePortal/Benji/Logos/E4--(None).jpg",
],
[
name: "Film 4 TV",
description: "Film 4 TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/13?protocol=hls",
image: "https://i.vimeocdn.com/portrait/10364974_300x300",
background: "https://fabrikmedia.blob.core.windows.net/ryh/e6f74f1029c698ab.jpg",
],
[
name: "5 Star",
description: "5 Star Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/851?protocol=hls",
image: "https://pbs.twimg.com/profile_images/3060994626/ee1508ee28b0bb010590c3e9f4a4c5d6_400x400.png",
background: "https://i.vimeocdn.com/video/551443446.jpg",
],
[
name: "Channel 5 USA",
description: "Channel 5 USA Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/845?protocol=hls",
image: "https://www.besttvchoice.net/wp-content/uploads/2014/10/ukchannel-list18.png",
background: "https://s3-eu-central-1.amazonaws.com/centaur-wp/creativereview/prod/content/uploads/2016/02/5USA_Grey_Blue1.jpg",
],
[
name: "CBS Reality TV",
description: "CBS Reality TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1808?protocol=hls",
image: "http://oklivetv.com/wp-content/uploads/2015/04/extra_big_logo191.png",
background: "http://2.bp.blogspot.com/-WNHJNg05Ctc/VGixLHRXGbI/AAAAAAAAL_8/xnoo6vB7PkU/s1600/cbs%2Breality.jpg",
],
[
name: "Really TV",
description: "Really TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/372?protocol=hls",
image: "https://uktv-static.s3.amazonaws.com/live/really/img/default_image.jpg",
background: "http://res.cloudinary.com/uktv/image/upload/v1487773187/qktxedogfref7pcvxf0s.jpg",
],
[
name: "Pick TV",
description: "Pick TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/371?protocol=hls",
image: "https://pbs.twimg.com/profile_images/745916761653002240/wG8-zB25_400x400.jpg",
background: "http://www.watchallchannels.com/wp-content/uploads/2015/02/Pick-TV.jpg",
],
[
name: "More 4",
description: "More 4 Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/97?protocol=hls",
image: "https://s-media-cache-ak0.pinimg.com/736x/8a/09/6e/8a096ef3af0d0c06cdaf06dad838682c.jpg",
background: "http://hub.tv-ark.org.uk/images/otherchannels/more4_images/more4_ident_t971_2010a.jpg",
],
[
name: "Tru TV",
description: "Tru TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/3707?protocol=hls",
image: "https://pbs.twimg.com/profile_images/832686108228087808/UHT79sqd.jpg",
background: "http://www.troika.tv/wp-content/uploads/truTV_PR_stills_09.jpg",
],
[
name: "Yesterday TV",
description: "Yesterday TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1039?protocol=hls",
image: "https://static.filmon.com/assets/channels/1039/extra_big_logo.png",
background: "https://i.ytimg.com/vi/MqZdLkQ0wQg/maxresdefault.jpg",
],
[
name: "CBS Drama",
description: "CBS Drama Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/2?protocol=hls",
image: "https://scryptyd.files.wordpress.com/2013/02/cbs-drama.jpg",
background: "https://theident.gallery/cbsdrama/2011/CBSD-2011-ID-FORENSIC-1-6.jpg",
],
[
name: "CBS Action",
description: "CBS Action Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1805?protocol=hls",
image: "https://pbs.twimg.com/profile_images/476321313431093248/b3AvSxrZ_400x400.jpeg",
background: "http://tvlivechannel.com/wp-content/uploads/2016/08/cbs_action_promo_2011a.jpg",
],
],
"Children": [
[
name: "Pop TV",
description: "Pop TV Live.",
genres: "FAMILY_KIDS",
url: "http://www.filmon.tv/api-v2/channel/320?protocol=hls",
image: "http://d136swi17h0tnq.cloudfront.net/Upload/2015-12/1f2f7882-6b4f-479b-93fe-15a9fafa7f89.jpg",
background: null,
],
[
name: "Tiny Pop TV",
description: "Tiny Pop TV Live.",
genres: "FAMILY_KIDS",
url: "http://www.filmon.tv/api-v2/channel/318?protocol=hls",
image: "https://lh3.googleusercontent.com/6xveC8c3hC24vP6rXmzQ39gxho64vHeLU5-SO51deC7nkDHGf70yosD3mFXqKQnZGFo=w300",
background: "http://tvlivechannel.com/wp-content/uploads/2016/08/2-tiny-pop.jpg",
],
],
"News": [
[
name: "Russia 24 News TV",
description: "Russia 24 News TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1744?protocol=hls",
image: "http://mediasat.info/wp-content/uploads/2015/05/rtr_24__.jpg",
background: "http://cdn-st1.rtr-vesti.ru/p/xw_461461.jpg",
],
],
"France TV": [
[
name: "Arte TV Français",
description: "Arte TV Français Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1729?protocol=hls",
image: "http://www.expeditio.org/images/stories/arte%20tv.jpg",
background: "http://www.dreamline-entertainment.com/elements/ARTE.jpg",
],
[
name: "France 2 TV",
description: "France 2 TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1717?protocol=hls",
image: "https://staticftv-a.akamaihd.net/arches/france2/default/img/og-image.jpg",
background: "http://www.watchallchannels.com/wp-content/uploads/2015/02/france-2.jpg",
],
[
name: "France 5 TV",
description: "France 5 TV Live.",
genres: "ENTERTAINMENT",
url: "http://www.filmon.tv/api-v2/channel/1720?protocol=hls",
image: "https://pbs.twimg.com/profile_images/755381190878789633/-_yIGsd2.jpg",
background: "http://images.telerama.fr/medias/2015/12/media_135767/nathalie-darrigrand-devrait-succeder-a-michel-field-a-la-direction-de-france-5,M285512.jpg",
],
],
]
def ParentPuppet mParent
def boolean mIsTopLevel
def String mName
def String mDescription
def mSection = []
static String[] getStreams(String url) {
def streams = []
def JSONArray json = new JSONObject(new URL(url).getText()).getJSONObject("data").getJSONArray("streams")
for (int i = 0; i < json.length(); i++) {
streams.add(json.getJSONObject(i).get("url"))
}
return streams as String[]
}
WatchAllChannelsPuppet() {
this(null, true, "Watch All Channels", "Live TV online free from all over the world.", null)
}
WatchAllChannelsPuppet(ParentPuppet parent, boolean isTopLevel, String name, String description, def section) {
mParent = parent
mIsTopLevel = isTopLevel
mName = name
mDescription = description
mSection = section
}
@Override
PuppetIterator getChildren() {
PuppetIterator children = new WatchAllChannelsPuppetIterator()
if (!mSection) {
SOURCES.each { k, section ->
if (k == "Entertainment") {
section.each { source ->
WatchAllChannelsSourcesPuppet sourcesPuppet = new WatchAllChannelsSourcesPuppet(
this,
source.url,
source.name,
source.description,
source.image,
source.background,
source.containsKey("preferredRegion") ? source.preferredRegion : null,
)
children.add(sourcesPuppet)
}
} else {
children.add(new WatchAllChannelsPuppet(this, true, k, null, section))
}
}
} else {
mSection.each { source ->
WatchAllChannelsSourcesPuppet sourcesPuppet = new WatchAllChannelsSourcesPuppet(
this,
source.url,
source.name,
source.description,
source.image,
source.background,
source.containsKey("preferredRegion") ? source.preferredRegion : null,
)
children.add(sourcesPuppet)
}
}
return children
}
@Override
boolean isTopLevel() {
return mIsTopLevel
}
@Override
String getName() {
return mName
}
@Override
String getCategory() {
return "United Kingdom"
}
@Override
String getShortDescription() {
return mDescription
}
@Override
String getImageUrl() {
return "https://pbs.twimg.com/profile_images/816750709332918277/lI41fgsk_400x400.jpg"
}
@Override
String getBackgroundImageUrl() {
return "http://cdn1.alphr.com/sites/alphr/files/2016/11/how_to_install_apollo_kodi_2016_uk.jpg"
}
@Override
boolean isUnavailableIn(String region) {
return false
}
@Override
String getPreferredRegion() {
return null
}
@Override
int getShieldLevel() {
return 0
}
@Override
ParentPuppet getParent() {
return mParent
}
@Override
SearchesPuppet getSearchProvider() {
return null
}
@Override
SettingsPuppet getSettingsProvider() {
return null
}
@Override
int getFastlaneBackgroundColor() {
return 0xFF26272B
}
@Override
int getSearchAffordanceBackgroundColor() {
return 0xFF3B8DBD
}
@Override
int getSelectedBackgroundColor() {
return 0xFF3B8DBD
}
@Override
int getPlayerBackgroundColor() {
return 0xFF26272B
}
@Override
List<Map<String, String>> getLiveChannelsMetaData() {
return null
}
@Override
PuppetIterator getRelated() {
return null
}
@Override
public String toString() {
return mParent == null ? getName() : mParent.toString() + " < " + getName()
}
@Override
int getVersionCode() {
return VERSION_CODE
}
def class WatchAllChannelsPuppetIterator extends PuppetIterator {
def ArrayList<Puppet> mPuppets = new ArrayList<>()
def int currentIndex = 0
@Override
boolean hasNext() {
return currentIndex < mPuppets.size()
}
@Override
void add(Puppet puppet) {
mPuppets.add(puppet)
}
@Override
Puppet next() {
return mPuppets.get(currentIndex++)
}
@Override
void remove() {
}
}
def static class WatchAllChannelsSourcesPuppet implements SourcesPuppet {
def ParentPuppet mParent
def mUrl
def String mName
def String mDescription
def String mImageUrl
def String mBackgroundImageUrl
def String mPreferredRegion
def List<SourcesPuppet.SubtitleDescription> mSubtitles = new ArrayList<SourcesPuppet.SubtitleDescription>()
WatchAllChannelsSourcesPuppet(ParentPuppet parent, String url, String name, String description, String imageUrl, String backgroundImageUrl, String preferredRegion) {
mParent = parent
mUrl = url
mName = name
mDescription = description
mImageUrl = imageUrl
mBackgroundImageUrl = backgroundImageUrl
mPreferredRegion = preferredRegion
}
@Override
String getPublicationDate() {
return null
}
@Override
long getDuration() {
return -1
}
@Override
SourcesPuppet.SourceIterator getSources() {
return new WatchAllChannelsSourceIterator()
}
@Override
boolean isLive() {
return true
}
@Override
List<SourcesPuppet.SubtitleDescription> getSubtitles() {
return mSubtitles
}
@Override
String getName() {
return mName
}
@Override
String getCategory() {
return null
}
@Override
String getShortDescription() {
return mDescription
}
@Override
String getImageUrl() {
return mImageUrl
}
@Override
String getBackgroundImageUrl() {
return mBackgroundImageUrl
}
@Override
boolean isUnavailableIn(String region) {
return mPreferredRegion != null && region != mPreferredRegion
}
@Override
String getPreferredRegion() {
return mPreferredRegion
}
@Override
int getShieldLevel() {
return 0
}
@Override
ParentPuppet getParent() {
return mParent
}
@Override
PuppetIterator getRelated() {
return mParent.getChildren()
}
@Override
public String toString() {
return mParent == null ? getName() : mParent.toString() + " < " + getName()
}
def class WatchAllChannelsSourceIterator implements SourcesPuppet.SourceIterator {
def List<SourceDescription> mSources = null
def int currentIndex = 0
@Override
boolean hasNext() {
if (mSources == null) {
mSources = new ArrayList<SourceDescription>()
for (String url : getStreams(mUrl)) {
SourceDescription source = new SourceDescription()
source.url = url
mSources.add(source)
}
}
return currentIndex < mSources.size()
}
@Override
SourceDescription next() {
return mSources.get(currentIndex++)
}
@Override
void remove() {
}
}
}
}
\ No newline at end of file
......@@ -30,7 +30,6 @@ import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import tv.puppetmaster.data.i.*
import java.text.SimpleDateFormat
import java.util.regex.Matcher
class DiscoveryChannelsPuppet implements InstallablePuppet {
......@@ -41,7 +40,7 @@ class DiscoveryChannelsPuppet implements InstallablePuppet {
[
name: "Discovery Channel",