MasterPuppeteer

Initial commit

# built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
.idea
*.iml
.gradle
build/
*/build/
/gradle
# Internal staging area
*/extra/
This diff is collapsed. Click to expand it.
# Puppets
Java interfaces that define puppets for the PuppetMaster.TV Android TV app and sample Groovy implementations.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
apply plugin: 'java'
dependencies {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
compile fileTree(dir: 'libs', include: ['*.jar'])
}
\ No newline at end of file
package tv.puppetmaster.data;
import java.util.List;
import java.util.Set;
import tv.puppetmaster.data.i.ParentPuppet;
import tv.puppetmaster.data.i.SettingsPuppet;
public class PreferenceSettingsPuppet implements SettingsPuppet {
private String mXml;
private Storage mStorage;
private ParentPuppet mParent;
private String mName;
private String mShortDescription;
private String mImageUrl;
private String mBackgroundImageUrl;
public PreferenceSettingsPuppet(ParentPuppet parent, String name, String shortDescription, String imageUrl, String backgroundImageUrl, String xml) {
mParent = parent;
mName = name;
mShortDescription = shortDescription;
mImageUrl = imageUrl;
mBackgroundImageUrl = backgroundImageUrl;
mXml = xml;
}
@Override
public String getDefinition() {
return mXml;
}
@Override
public Storage getStorage() {
return mStorage;
}
@Override
public void setStorage(Storage storage) {
mStorage = storage;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return mStorage == null? defValue : mStorage.getBoolean(key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return mStorage == null? defValue : mStorage.getFloat(key, defValue);
}
@Override
public int getInt(String key, int defValue) {
return mStorage == null? defValue : mStorage.getInt(key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return mStorage == null? defValue : mStorage.getLong(key, defValue);
}
@Override
public String getString(String key, String defValue) {
return mStorage == null? defValue : mStorage.getString(key, defValue);
}
@Override
public List<String> getStringList(String key, List<String> defValue) {
return mStorage == null? defValue : mStorage.getStringList(key, defValue);
}
@Override
public Set<String> getStringSet(String key, Set<String> defValue) {
return mStorage == null? defValue : mStorage.getStringSet(key, defValue);
}
@Override
public String getName() {
return mName;
}
@Override
public String getCategory() {
return null;
}
@Override
public String getShortDescription() {
return mShortDescription;
}
@Override
public String getImageUrl() {
return mImageUrl;
}
@Override
public String getBackgroundImageUrl() {
return mBackgroundImageUrl;
}
@Override
public boolean isUnavailableIn(String region) {
return false;
}
@Override
public String getPreferredRegion() {
return null;
}
@Override
public int getShieldLevel() {
return 0;
}
@Override
public ParentPuppet getParent() {
return mParent;
}
@Override
public PuppetIterator getRelated() {
return null;
}
}
\ No newline at end of file
package tv.puppetmaster.data.i;
import java.util.List;
import java.util.Map;
public interface InstallablePuppet extends ParentPuppet {
/*
* The version code implies which API features are supported:
* Live channels >= 2
* Branding >= 2
* Region handling >= 4
* Settings >= 5
* User-Agent >= 6
*/
int getVersionCode();
/*
* If the puppet provides the ability to search, this provider is passed the input
*/
SearchesPuppet getSearchProvider();
/*
* Exposes settings that may be optional or required to retrieve content
*/
SettingsPuppet getSettingsProvider();
/*
* Custom branding for this puppet, specifies the fastlane background color
*/
int getFastlaneBackgroundColor();
/*
* Custom branding for this puppet, specifies the search affordance background color
*/
int getSearchAffordanceBackgroundColor();
/*
* Custom branding for this puppet, specifies the selected item's background color
*/
int getSelectedBackgroundColor();
/*
* Custom branding for this puppet, specifies the player background color
*/
int getPlayerBackgroundColor();
/*
* Expose live channels through a list of maps, each declaring at least: {name, url}
* Optionally declare {description, logo}
*/
List<Map<String, String>> getLiveChannelsMetaData();
}
\ No newline at end of file
package tv.puppetmaster.data.i;
public interface ParentPuppet extends Puppet {
/*
* A parent puppet acts as a directory and getChildren lists its items
*/
PuppetIterator getChildren();
/*
* If top-level, it gets its own row in the main screen with its children listed immediately,
* otherwise it appears within an existing row and children are exposed when clicked upon
*/
boolean isTopLevel();
}
\ No newline at end of file
package tv.puppetmaster.data.i;
import java.io.Serializable;
import java.util.Iterator;
public interface Puppet extends Serializable {
/*
* The name of this puppet, usually used to display the title of the content
*/
String getName();
/*
* The broader category that describes the content represented by this puppet
*/
String getCategory();
/*
* A brief description of the content
*/
String getShortDescription();
/*
* Url for a thumbnail preferably a 300px^2 .png
*/
String getImageUrl();
/*
* Url for a large image to display as a background, preferably in a compressed format
*/
String getBackgroundImageUrl();
/*
* If the content is unavailable in the given region
* @param region Usually a 2 character lower-case country code (ISO 3166 alpha-2)
* @return If the content represented by this puppet is available in the given region
*/
boolean isUnavailableIn(String region);
/*
* @return A region best supporting access to the content or null if all regions are supported
*/
String getPreferredRegion();
/*
* @return An integer between 0-9 indicating the level of defense employed by the source
* 0 == none
* 9 == impenetrable
*/
int getShieldLevel();
/*
* The parent of this puppet or null if it is a top-level item
*/
ParentPuppet getParent();
/*
* Media related to this puppet
*/
PuppetIterator getRelated();
/*
* Whatever describes this puppet best, usually an internal String representation in the form
* of: top-level < first descendant < ... < current item
*/
String toString();
abstract class PuppetIterator implements Iterator<Puppet>, Serializable {
public abstract void add(Puppet puppet);
}
}
\ No newline at end of file
package tv.puppetmaster.data.i;
public interface SearchesPuppet extends ParentPuppet {
/*
* Children are results that match the search query
*/
void setSearchQuery(String searchQuery);
}
\ No newline at end of file
package tv.puppetmaster.data.i;
import java.util.List;
import java.util.Set;
public interface SettingsPuppet extends Puppet {
String getDefinition();
Storage getStorage();
void setStorage(Storage storage);
boolean getBoolean(String key, boolean defValue);
float getFloat(String key, float defValue);
int getInt(String key, int defValue);
long getLong(String key, long defValue);
String getString(String key, String defValue);
List<String> getStringList(String key, List<String> defValue);
Set<String> getStringSet(String key, Set<String> defValue);
interface Storage {
boolean getBoolean(String key, boolean defValue);
void setBoolean(String key, boolean value);
float getFloat(String key, float defValue);
void setFloat(String key, float value);
int getInt(String key, int defValue);
void setInt(String key, int value);
long getLong(String key, long defValue);
void setLong(String key, long value);
String getString(String key, String defValue);
void setString(String key, String value);
List<String> getStringList(String key, List<String> defValue);
void setStringList(String key, List<String> value);
Set<String> getStringSet(String key, Set<String> defValue);
void setStringSet(String key, Set<String> value);
}
}
\ No newline at end of file
package tv.puppetmaster.data.i;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public interface SourcesPuppet extends Puppet {
/*
* The date of publication for this content
*/
String getPublicationDate();
/*
* Its duration in milliseconds
*/
long getDuration();
/*
* An iterator listing the actual sources of this content
*/
SourceIterator getSources();
/*
* If it is live
*/
boolean isLive();
/*
* Uri => Description
*/
List<SubtitleDescription> getSubtitles();
/*
* We want to lazy-load sources in case the implementation requires network access to parse each
*/
interface SourceIterator extends Iterator<SourceDescription>, Serializable {
}
class SourceDescription implements Serializable {
public String url;
public long duration;
public String height;
public String width;
public String rating;
public boolean isAudioOnly;
public long bitrate;
public String userAgent;
public String referer;
public Map<String, String> requestHeaders;
public String drmScheme;
public String drmLicenseUrl;
}
class SubtitleDescription implements Serializable {
public String locale = Locale.ENGLISH.getLanguage();
public String mime = "text/srt";
public String url;
}
}
\ No newline at end of file
apply plugin: 'java'
apply plugin: 'groovy'
def ANDROID_SDK_HOME = System.properties['user.home'] + '/Library/Android/sdk/build-tools/24.0.0'
task jarEach << {
delete fileTree(dir: 'build/libs/' , include: '*.jar')
def paths = []
fileTree("src").visit { FileVisitDetails details ->
if (details.file.path.contains("/puppetimpl/src/main/groovy/") && details.file.path.endsWith(".groovy")) {
paths << details.file.path.split("/puppetimpl/src/main/groovy/")[1].replace(".groovy", "*.class")
}
}
paths.each { path ->
def className = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("*.class"))
def fullyQualifiedClassName = path.replace("/", ".").replace("*.class", "")
def jarIt = tasks.create(name: "jar${className}", type: Jar) {
baseName = fullyQualifiedClassName
manifest {
attributes("Module-Class": fullyQualifiedClassName)
}
from(sourceSets.main.output) {
include path
}
}
def dexIt = tasks.create(name: "dex${className}", type: Exec) {
commandLine "${ANDROID_SDK_HOME}/dx", "--dex", "--output=classes.dex", "build/libs/${fullyQualifiedClassName}.jar"
}
def aaptIt = tasks.create(name: "aapt${className}", type: Exec) {
commandLine "${ANDROID_SDK_HOME}/aapt", "add", "build/libs/${fullyQualifiedClassName}.jar", "classes.dex"
}
def cleanIt = tasks.create(name: "clean${className}", type: Exec) {
commandLine "rm", "classes.dex"
}
[jarIt, dexIt, aaptIt, cleanIt].each {
it.execute()
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.jsoup:jsoup:1.9.2'
compile 'org.json:json:20151123'
compile 'org.codehaus.groovy:groovy:2.4.7'
compile project(':puppet')
}
\ No newline at end of file
import tv.puppetmaster.data.i.InstallablePuppet
import tv.puppetmaster.data.i.ParentPuppet
import tv.puppetmaster.data.i.Puppet
import tv.puppetmaster.data.i.SourcesPuppet
import static groovy.io.FileType.FILES
def puppetClassNames = []
def puppetFullyQualifiedClassNames = []
new File('./puppetimpl/src/main/groovy/').eachFileRecurse(FILES) {
if (it.name.endsWith('Puppet.groovy')) {
puppetClassNames << it.name.replaceFirst(~/\.[^\.]+$/, '')
puppetFullyQualifiedClassNames << it.path.replace('./puppetimpl/src/main/groovy/', '').replace('/', '.').replace('.groovy', '')
}
}
if (puppetClassNames.size() > 0) {
puppetClassNames.eachWithIndex { it, i ->
println "${i}) ${it}"
}
def selection = inputNumber("Select a puppet", 0, puppetClassNames.size() - 1)
def selectedPuppet = (InstallablePuppet) this.class.classLoader.loadClass(puppetFullyQualifiedClassNames[selection])?.newInstance()
processPuppet(selectedPuppet, 0)
} else {
System.err.println "No puppets found to test, double-check your setup."
}
def int inputNumber(String text, int low, int high) {
def selection = -1
while (selection < low || selection >= high) {
print "\n${text} [${low}-${high}]: "
try {
selection = System.in.newReader().readLine() as int
if (selection >= low && selection <= high) {
break
}
} catch (ignore) {
}
selection = -1
System.err.println "Invalid input, try again."
}
return selection
}
def processPuppet(Puppet selectedPuppet, int level) {
println "-".multiply(80)
println "${selectedPuppet.name} (${selectedPuppet instanceof SourcesPuppet ? "SourcesPuppet" : "ParentPuppet"})"
if (selectedPuppet instanceof SourcesPuppet) {
def children = selectedPuppet.getSources()
def i = 0
while (children != null && children.hasNext()) {
def next = children.next()
println "\t".multiply(level + 1) + i++ + ") ${next.url}"
}
} else if (selectedPuppet instanceof ParentPuppet) {
def children = selectedPuppet.getChildren()
def i = 0
def childPuppets = []
while (children != null && children.hasNext()) {
def next = children.next()
childPuppets << next
println "\t".multiply(level + 1) + i++ + ") ${next.name} (${next instanceof SourcesPuppet ? "SourcesPuppet" : "ParentPuppet"})"
}
def selection = inputNumber("Select a puppet", 0, childPuppets.size() - 1)
processPuppet(childPuppets[selection] as Puppet, level + 1)
}
println "-".multiply(80)
}
\ No newline at end of file
include ':puppet', ':puppetimpl'
\ No newline at end of file