|
SageTV Studio Discussion related to the SageTV Studio application produced by SageTV. Questions, issues, problems, suggestions, etc. relating to the Studio software application should be posted here. |
|
Thread Tools | Search this Thread | Display Modes |
#1
|
|||
|
|||
A "groovy" way to write plugins...
I've just installed my first PoC build of a plugin completely written in Groovy. This may not excite many, but it sure excited me. While it's all fresh in my mind, I'm going to dump a few notes here about it. A complete wiki doc explaining all the details (and there really aren't many, actually) on how to do this is forthcoming.
Motivation: Be able to write plugins quickly, easily. Why Groovy? It's Java with a lot of the boilerplate code removed and a lot of "groovy" features added. Requirements:
And that's really it. From there, you just write Groovy code and compile it into bytecode when your done. Once your groovy scripts are in bytecode form, it's really just Java as far as anything else (i.e. the Sage core) is concerned. Why I wanted to try this: Combined with Sean's AbstractPlugin class from the sagex-api plugin, I can write plugin implementations in but a few lines of code. Here is the entire Groovy script of the plugin I'm working on. Code:
package com.google.code.sagetvaddons.sop import sage.SageTVPlugin import sage.SageTVPluginRegistry import sagex.api.Global import sagex.plugin.AbstractPlugin import sagex.plugin.SageEvent final class Plugin extends AbstractPlugin { static final String PROP_PREFIX = 'sop_plugin/' static final String PROP_CLEAN_FREQ = PROP_PREFIX + 'clean_freq' private Timer timer Plugin(SageTVPluginRegistry reg) { super(reg) addProperty(SageTVPlugin.CONFIG_INTEGER, PROP_CLEAN_FREQ, '360', 'Cleaner Frequency (mins)', 'How often, in minutes, the cleaner thread will cleanup properties for non-existant objects. Changes to this value require a plugin restart.') } @Override void start() { super.start() if(timer) timer.cancel() timer = new Timer(true) def freq = getConfigValue(PROP_CLEAN_FREQ).toInteger() * 60000 timer.scheduleAtFixedRate(new TimerTask() { @Override void run() { DataStore.get().cleanup() } }, freq, freq) } @Override void stop() { super.stop() if(timer) timer.cancel() timer = null } @SageEvent("SystemMessagePosted") void handleEvent(String name, Map args) { Global.DebugLog("Event handled the Groovy way!") } } This is my entire DataStore class, written in Groovy. Why I love Groovy for things like this:
Code:
package com.google.code.sagetvaddons.sop import groovy.sql.Sql import java.sql.Connection import java.sql.PreparedStatement import java.sql.SQLException import sagex.api.AiringAPI import sagex.api.ShowAPI @PackageScope final class DataStore { static private final DataStore INSTANCE = new DataStore() static final DataStore get() { return INSTANCE } Sql sql private DataStore() { initDb() } private void initDb() { sql = Sql.newInstance('jdbc:h2:tcp://localhost/plugins/sop/sop', 'admin', 'admin', 'org.h2.Driver') sql.execute 'CREATE TABLE IF NOT EXISTS airings (id INTEGER NOT NULL, key VARCHAR(255) NOT NULL, val LONGVARCHAR, PRIMARY KEY(id, key))' sql.execute 'CREATE TABLE IF NOT EXISTS shows (id VARCHAR(255) NOT NULL, key VARCHAR(255) NOT NULL, val LONGVARCHAR, PRIMARY KEY(id, key))' } boolean setAiringProperty(def airing, def key, def val) { def id = AiringAPI.GetAiringID(airing) sql.withTransaction { sql.execute "DELETE FROM airings WHERE id = $id" sql.execute "INSERT INTO airings (id, key, val) VALUES ($id, ${key.toString()}, ${val.toString()})" } } boolean setShowProperty(def show, def key, def val) { def id = ShowAPI.GetShowExternalID(show) sql.withTransaction { sql.execute "DELETE FROM shows WHERE id = $id" sql.execute "INSERT INTO shows (id, key, val) VALUES ($id, ${key.toString()}, ${val.toString()})" } } String getAiringProperty(def airing, def key, def defVal) { try { def row = sql.firstRow("SELECT val FROM airings WHERE id = ${AiringAPI.GetAiringID(airing)} AND key = ${key.toString()}") return row ? row[0].toString() : String.valueOf(defVal) } catch(SQLException e) { return String.valueOf(defVal) } } String getShowProperty(def show, def key, def defVal) { try { def row = sql.firstRow("SELECT val FROM shows WHERE id = ${ShowAPI.GetShowExternalID(show)} AND key = ${key.toString()}") return row ? row[0].toString() : String.valueOf(defVal) } catch(SQLException e) { return String.valueOf(defVal) } } void cleanup() { sql.withTransaction { sql.eachRow('SELECT id FROM airings') { if(!AiringAPI.GetAiringForID(it[0])) sql.execute("DELETE FROM airings WHERE id = ${it[0]}") } } sql.withTransaction { sql.eachRow('SELECT id FROM shows') { if(!ShowAPI.GetShowForExternalID(it[0])) sql.execute("DELETE FROM shows WHERE id = ${it[0]}") } } } } As I say, I've actually installed this plugin on my test server and it's running happily so SageTV plugins written in Groovy is definitely a possibility! A wiki doc with a few more details is in the works and I'll post the link when it's done. EDIT: Entire source code for plugin is here.
__________________
Twitter: @ddb_db Server: Intel i5-4570 Quad Core, 16GB RAM, 1 x 128GB OS SSD (Win7 Pro x64 SP1), 1 x 2TB media drive Capture: 2 x Colossus STB Controller: 1 x USB-UIRT Software:Java 1.7.0_71; SageTV 7.1.9 Clients: 1 x HD300, 2 x HD200, 1 x SageClient, 1 x PlaceShifter Plugins: Too many to list now... Last edited by Slugger; 04-12-2011 at 08:06 PM. Reason: Add link to plugin src |
#2
|
|||
|
|||
Oh thanks for sharing definately peaked my interest.
|
#3
|
||||
|
||||
Well, of coarse, you had me at 'scripting'
Groovy's got some pretty robust webscraping libraries, I may have to play around with this
__________________
Clients: 1xHD200 Connected to 50" TH-50PZ750U Plasma Server : Shuttle SFF SSH55J2 w/ Win7 Home, SageTV v7, Core i3 540, 2GB RAM, 30GB SSD for OS, 1.5TB+2x1TB WDGP for Recordings, BluRay, 2xHDHR, 1xFirewire SageTV : PlayOn, SJQ, MediaShrink, Comskip, Jetty, Web Client, BMT Having a problem? Don't forget to include a log! (Instructions for: PlayOn For SageTV v1.5, MediaShrink) |
#4
|
||||
|
||||
That's very cool.
__________________
Batch Metadata Tools (User Guides) - SageTV App (Android) - SageTV Plex Channel - My Other Android Apps - sagex-api wrappers - Google+ - Phoenix Renamer Downloads SageTV V9 | Android MiniClient |
#5
|
||||
|
||||
I'm up and running with Groovy + Eclipse, any tips you want to share before I dive in?
__________________
Clients: 1xHD200 Connected to 50" TH-50PZ750U Plasma Server : Shuttle SFF SSH55J2 w/ Win7 Home, SageTV v7, Core i3 540, 2GB RAM, 30GB SSD for OS, 1.5TB+2x1TB WDGP for Recordings, BluRay, 2xHDHR, 1xFirewire SageTV : PlayOn, SJQ, MediaShrink, Comskip, Jetty, Web Client, BMT Having a problem? Don't forget to include a log! (Instructions for: PlayOn For SageTV v1.5, MediaShrink) |
#6
|
|||
|
|||
Quote:
Two things I love that come to the top of my head as I write this: Java: Code:
// Check for string equality if("FOO".equals(myvar)) { // do whatever } Code:
if("FOO" == myvar) { // do whatever } #2 Groovy thing off the top of my head: switch statements that work on any type: Code:
def val = 'groovy' switch(val) { case 'x': // whatever case 'y': default: } Anyway, my point is that once you're comfortable with Groovy and learn some groovy shortcuts then I have no doubt plugin dev will be much, much quicker than doing the equivalent in Java, but there is some learning to do. Note: Don't forget to depend on the groovy plugin to ensure your plugin users download and install the groovy runtime needed to run your groovy code. The current groovy plugin is at version 1.7.10. I intend to keep it there until I return to coding in the fall and then I'll probably upgrade to the recently released 1.8.0, which has a very groovy feature that finally allows the timing out of scripts automatically, but 1.7 -> 1.8 looks like too much of a change for me to be tackling over the summer.
__________________
Twitter: @ddb_db Server: Intel i5-4570 Quad Core, 16GB RAM, 1 x 128GB OS SSD (Win7 Pro x64 SP1), 1 x 2TB media drive Capture: 2 x Colossus STB Controller: 1 x USB-UIRT Software:Java 1.7.0_71; SageTV 7.1.9 Clients: 1 x HD300, 2 x HD200, 1 x SageClient, 1 x PlaceShifter Plugins: Too many to list now... Last edited by Slugger; 05-11-2011 at 10:38 PM. |
#7
|
||||
|
||||
Groovy seems very very similar to JavaScript. . . and btw couldn't you do the same thing
with JavaScript itself?? You could just use the Rhino runtime, and then call various apis and what not. . . what does groovy give you specifically (besides just scripting, which I don't want to downplay. . is TOTALLY Cool).
__________________
AMD Ryzen 9 3900XT 12 Core+HT, 64GB DDR5, GeForce 1060, MSI Prestige x570 Creation Mobo, SIIG 4 port Serial PCIe Card, Win10, 1TB M.2 SSD OS HDD, 1 URay HDMI Network Encoder, 3 HD-PVR, 4 DirecTV STB serial tuned |
#8
|
|||
|
|||
Groovy can be compiled to Java bytecode, which means that Sage can directly load Groovy code and run it as a plugin implementation class.
To use other languages, such as JavaScript, as a Sage plugin requires one of two things: 1) The language in use must compile directly to Java bytecode to be used directly by Sage as a plugin implementation class. To my knowledge, only Groovy supports this. 2) If your language of choice doesn't support #1 then you must write a Java wrapper that creates a JSR-223 script engine (or equivalent, depending on which language you want to use) in Java that then calls your scripts at runtime. This is certainly possible, but seems like a lot of overhead given that Groovy relieves you of all of these necessities. In a nutshell, Groovy scripts can be compiled to Java bytecode and once they are then Sage sees them as nothing more than Java code that depends on the Groovy runtime jar file. This, to me, makes it the perfect candidate if you're looking at scripting for Sage plugins.
__________________
Twitter: @ddb_db Server: Intel i5-4570 Quad Core, 16GB RAM, 1 x 128GB OS SSD (Win7 Pro x64 SP1), 1 x 2TB media drive Capture: 2 x Colossus STB Controller: 1 x USB-UIRT Software:Java 1.7.0_71; SageTV 7.1.9 Clients: 1 x HD300, 2 x HD200, 1 x SageClient, 1 x PlaceShifter Plugins: Too many to list now... Last edited by Slugger; 05-15-2011 at 09:14 PM. |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
The "What's your favorite plugins?" thread (screenshots welcome) | mkanet | SageTV v7 Customizations | 15 | 12-27-2010 01:29 PM |
Are plugins: "Ortus MQ" & "SageTV H2 Mobile Database" needed? | mkanet | SageTV Customizations | 2 | 12-06-2010 06:56 AM |
"Backdrops" "SageTV" "Covers" folders - what's creating them | mp328 | Sage My Movies | 4 | 09-20-2010 05:31 PM |
"Set defaults" for Series forgets "Keep"/"Auto-delete" setting | maxpower | SageMC Custom Interface | 9 | 05-14-2008 09:44 PM |
Installing/Uninstalling "Got All Media" caused Sage to report "No capture device" | VorpalBlade | Hardware Support | 3 | 10-15-2005 01:30 AM |