APL Tablo Client

That’s in the Docs that I haven’t written yet…but Sch is Scheduled…it can have N=New, A=All, or if you have more than one Tablo, and one is set to New, and the other is set to All, for the same show, it’ll be B=Both. The Number after it is the number of recordings that are currently scheduled…if you have a conflict, you will also get another one showing the conflict…so, as an example, if you are recording new episodes, and there are 2 on your schedule, but one of them is in conflict, you would get

N-1-1

If however it is a Movie, which doesn’t offer series recording options, or you have chosen not to record either new or all, and you still have something scheduled, it’ll just show you the number of recordings that are scheduled.

1 Like

I got my first Tablo about 4 years ago…before I wrote this app, I used to check out the ‘new’ and ‘premiering’ section of the Tablo section all of the time, and then scanned the Movies section to try and see if there were any new movies (new to me of course)…this got tiresome

I don’t have 900 per Tablo, I’m in a bit smaller market than you I guess :)…on initial load, this is the timings

2019-08-19 13:44:00,960 [INFO ] Starting: Morris shows list
2019-08-19 13:44:01,185 [DEBUG] Shows on Morris: 502
2019-08-19 13:44:06,181 [INFO ] Starting: Tahoma shows list
2019-08-19 13:44:06,437 [DEBUG] Shows on Tahoma: 626
2019-08-19 13:44:12,856 [INFO ] Starting Show DB Updates
2019-08-19 13:44:13,119 [INFO ] Finished shows list (631)

Roughly 12 seconds to get all of the shows from both Tablos and create all the necessary cache, I then go over to my db that has my blacklist configured

2019-08-19 13:48:30,242 [INFO ] Starting: Morris shows list
2019-08-19 13:48:30,451 [DEBUG] Shows on Morris: 502
2019-08-19 13:48:35,347 [INFO ] Starting: Tahoma shows list
2019-08-19 13:48:35,603 [DEBUG] Shows on Tahoma: 626
2019-08-19 13:48:41,651 [INFO ] Finished shows list (41)

took…a few seconds less time, but only shows me the 41 I haven’t already discarded, which in this case is the ones that I’m recording :slight_smile:

So…I guess the ‘filter’ I was looking for on shows is the blacklist, which Tablo themselves haven’t given me :slight_smile:

I just started playing with this after work today. First, thanks for doing this. I was a bit confused with the README file was because I think it was more specific to a Linux environment, I finally realized after a while that I do not need to separately install JavaFX for my Windows 10 implementation. Is that true or am I possibly using the wrong Java implementation (Oracle JRE)? Third, I made a shortcut to launch the app that others may find useful. Replace the file paths as appropriate:


Target: “C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe” -jar APLTablo.jar
Start in: C:\Users\bbaor_000\Documents\Installs\APLTablo
Fourth, and finally, the “Shows” and “Scheduled” buttons work fine but when I try the “Recordings” I get an error, possibly because I have hundreds (or more likely many thousands) of recordings. Should I turn on debug or is the following enough to go on:
2019-08-19 19:58:04,197 [INFO ] Loaded 1 Tablos with 4 tuners
2019-08-19 19:58:18,865 [INFO ] Starting: TeeVee recording list
Exception in thread “JavaFX Application Thread” java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:410)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$3(WinApplication.java:177)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
… 48 more
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(Unknown Source)
at java.lang.AbstractStringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at com.apl.Tablo.showRecorded(Tablo.java:432)
at com.apl.view.RecordingsController.getRecordings(RecordingsController.java:103)
at com.apl.view.MainOverviewController.handleRecorded(MainOverviewController.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)

Yes, it’s your number of recordings, you’ll need to give it more memory, you can give it as much memory as you need, if I remember correctly you need -Xmx 2048m or something like that 2048m being 2gig, but you can increase as needed.

Regarding the JavaFX, you’ll need that only if you are running a jre that doesn’t include it, which I think is everything past 8, I didn’t need it when I was using 8, but moving to 11, I need to now, so I don’t think it’s so much a Windows/Linux thing.

Interesting, I just installed on my Linux (Lubuntu) box that is on very old hardware and APLTablo is working fine so far, including the “Recorded” button. I might need to try a different Java version on my Windows box.

2 Likes

Wow - this has already helped me find some old, partial shows so I could clean them up - thanks!
Now to the feature requests:

  1. Can all three lists have season/episode in their own separate/sortable field or fields, if that is easier. I especially would like to see this on the Shows list so it can help me find pilot episodes, i.e. Season 1 - Episode 1.
  2. Can you add an export to csv function, especially for the Recorded list?
1 Like

for 1, the problem is this…There are 3 different ‘titles’…one is the title of the Movie…one is the title of the series, the 3rd is the title of the episode…I put all of those into a single title to allow for proper sorting…if your intent is to find new/premiering/etc…I have an idea how I can incorporate that into the GUI to allow you to easily filter on that stuff…give me a bit of time

for 2, yes, I’ve done that with other tools and is relatively easy, I can to that for all 3 of the windows :slight_smile:

1 Like

Requests?!! I was just suggesting cleaning up what you’ve got working now… add more, oh boy, give me tablo’s episode ID for recorded shows :slight_smile: !

Episode ID?

Maybe that’s the actual name for number, just what it’s sometimes referenced as. Maybe it’s just a nameless reference number.

object_id

"object_id": 114201,
  "path": "/recordings/series/episodes/114201",
  "series_path": "/recordings/series/65150",
  "season_path": "/recordings/series/seasons/65185",

If you just want to play/watch an episode in your media player or for whatever reason use ffmpeg with own settings. works with smplayer or vlc, probably most, use in a script/bat file
note: these are just sniplets - I can/will provide more info later.

smptablo XXXX
or
vlctablo XXXX
incorporate this command to suite your OS

smplayer http://tablo.lan:18080/pvr/$1/pl/playlist.m3u8

or

vlc http://tablo.lan:18080/pvr/$1/pl/playlist.m3u8

Or if you want to do something specific for some reason with ffmpeg

tablo2mp4 XXXX "Show Filename"

`ffmpeg -loglevel warning -i "$1" -bsf:a aac_adtstoasc -vcodec copy -acodec copy -crf 18 "${2}".mp4`

So, you aren’t actually looking for the object ID, you want the URL that you can stream the file from…I’m already collecting that, so it shouldn’t be that hard to just give it to you :slight_smile:

I want the number

so I can concatenate my own url

Ok, just posted .08, it has a few enhancements, including the ability to get the streaming URL.

I would advise against using the 18080 port as it’s not a long term solution (Tablo will stop listening on that port eventually)…the Streaming URL I provide with the right click on the recording is the Tablo approved method going forward to get the stream.

…that’s why I’m apprehensive with firmware upgrades. What’s it really going to do?

I understand, this isn’t how tablo is intended to be used, or has since changed it’s methodology moving forward. But it works for me… for now.

So…based on the discussions I’ve seen, the old 18080 url is ‘insecure’, in that if you are somewhere, someone could hack and drain your Tablo…the new method gives essentially a key that is good for a certain period, but not good if someone did a man in the middle attach to come back later and grab your content. Either way, with access to the API docs, the URL I’m providing with the right click is the ‘approved’ method to stream.

In the next version I’ve added ‘Copy Tablo URL’ to all of the screens, this will give you the path you can use to access the JSON on the server, and as such, collect any information the Tablo has available.

1 Like

Actually I was “asking” in reference to firmware upgrades. We’re swaying off topic some.

I believe the security issues is a big picture thing. Me, it’s just my closed NAT local network. But is everything ever completely secure full time? Hopefully, but the only way to find out is too late.

and it limits access to files stored on the drive, for what ever limited use there is for some.
I have noticed a couple of sites I’ve found from 4-5yrs old with tablo info, are no long available.

Installed this today - great work so far!

Very cool to see the detailed info available on my Tablo.

2 Likes

I think you have it backwards! If your non-windows OS works so great as-is… maybe you have what you need already :wink:

I like how you implemented the tags for New and Premiering @LJ_LongWing. Is it possible to add a tag for “Pilot” to indicate Season 1 - Episode 1, even if the show is old. This information is so hard to find and would be helpful for those of us who want to record entire series from the beginning.