[RELEASE] Playlist Manager for Channels (SLM Add-on)

There are a lot of fantastic methods for integrating custom stations into Channels DVR, especially from FAST and similar providers like Pluto, Plex, Tubi, Samsung TV+, ESPN+, NFL+, and plenty more! The problem is, they require a fair bit of maintenance. For instance, there are whole threads and tools dedicated just to keeping track of which stations have been added and removed. And that doesn't even get into the redundancy of when each of these services have the same stations, but you have to decide which one you want to put in your Channel Collection before it inevitably disappears without you knowing it and not realizing you need to put a replacement in its spot. But what if we could make the whole process a little...easier?

Enter Playlist Manager for Channels, a custom add-on for Stream Link (+Files +Playlists) Manager for Channels!

From a high-level perspective, PLM works on the same premise as SLM. The idea is that there is some piece of content that can come from multiple sources that you have legal access to and it will "assign" which one to use based upon a priority that you set. With SLM, it takes a movie or an episode of a TV show and parses through all the streaming services you have set, sees if it is there, and assigns the appropriate Stream Link. Similarly, with PLM, it takes a "parent" station that you define and parses through all the playlists that you have set, sees if there is a matching "child" station, and assigns the appropriate info to m3u and EPG files that can be integrated into Channels DVR or any other similar tool. Still, this is just the beginning of its capabilities!


All installation, usage, and other related matters are available on the Github Repo here:

https://github.com/babsonnexus/stream-link-manager-for-channels#playlist-manager


To see a short demonstration, watch the video here:


Here's my specific use case and how I set things up:

3 Likes

Dude. . . DUDE!!!! dude. Do you sleep. I haven't tested. But im excited.

1 Like

I see the same problem with Samsung TV+ just using it as intended with Channels.
So far this month (updating playlist/xml hourly)

2024/10/05 12:15:09.694490 [ERR] Unexpected m3u response for SamsungTVPlus: 500 Internal Server Error
2024/10/05 13:15:13.406329 [ERR] Failed to request m3u for SamsungTVPlus: Get "http://192.168.1.3:8182/playlist.m3u8": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
2024/10/07 09:15:11.719993 [ERR] Unexpected m3u response for SamsungTVPlus: 500 Internal Server Error
2024/10/24 11:15:14.031980 [ERR] Failed to request m3u for SamsungTVPlus: Get "http://192.168.1.3:8182/playlist.m3u8": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
2024/10/26 03:15:13.382200 [ERR] Failed to request m3u for SamsungTVPlus: Get "http://192.168.1.3:8182/playlist.m3u8": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
2024/10/26 15:15:13.825703 [ERR] Failed to request m3u for SamsungTVPlus: Get "http://192.168.1.3:8182/playlist.m3u8": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

The container takes too long to respond to Channels which leads to an exception in the container
BrokenPipeError: [Errno 32] Broken pipe

The container log usually looks like this
2024-10-26T22:15:21.774874689Z Including channels from regions: ['us']
2024-10-26T22:15:21.775205252Z 192.168.144.1 - - [26/Oct/2024 15:15:21] "GET /playlist.m3u8 HTTP/1.1" 200 -
2024-10-26T22:15:21.777449328Z 192.168.144.1 - - [26/Oct/2024 15:15:21] "GET /playlist.m3u8 HTTP/1.1" 500 -
2024-10-26T22:15:21.777787699Z ----------------------------------------
2024-10-26T22:15:21.777908451Z Exception occurred during processing of request from ('192.168.144.1', 44480)
2024-10-26T22:15:21.779876350Z Traceback (most recent call last):
2024-10-26T22:15:21.780945769Z   File "/usr/src/app/./app.py", line 48, in do_GET
2024-10-26T22:15:21.781204142Z     routes[func]()
2024-10-26T22:15:21.781378660Z   File "/usr/src/app/./app.py", line 96, in _playlist
2024-10-26T22:15:21.781524574Z     self.wfile.write(f'#EXTINF:-1 channel-id="{channel_id}" tvg-id="{key}" tvg-logo="{logo}" group-title="{group}"{chno},{name}\n{url}\n'.encode('utf8'))
2024-10-26T22:15:21.781676874Z   File "/usr/local/lib/python3.12/socketserver.py", line 840, in write
2024-10-26T22:15:21.781803555Z     self._sock.sendall(b)
2024-10-26T22:15:21.781905473Z BrokenPipeError: [Errno 32] Broken pipe
2024-10-26T22:15:21.782009662Z 
2024-10-26T22:15:21.782094622Z During handling of the above exception, another exception occurred:
2024-10-26T22:15:21.782198372Z 
2024-10-26T22:15:21.782279478Z Traceback (most recent call last):
2024-10-26T22:15:21.782994632Z   File "/usr/local/lib/python3.12/socketserver.py", line 692, in process_request_thread
2024-10-26T22:15:21.783216446Z     self.finish_request(request, client_address)
2024-10-26T22:15:21.783465992Z   File "/usr/local/lib/python3.12/socketserver.py", line 362, in finish_request
2024-10-26T22:15:21.783672885Z     self.RequestHandlerClass(request, client_address, self)
2024-10-26T22:15:21.783819644Z   File "/usr/src/app/./app.py", line 23, in __init__
2024-10-26T22:15:21.783932717Z     super().__init__(*args, **kwargs)
2024-10-26T22:15:21.784028259Z   File "/usr/local/lib/python3.12/socketserver.py", line 761, in __init__
2024-10-26T22:15:21.784128136Z     self.handle()
2024-10-26T22:15:21.784226555Z   File "/usr/local/lib/python3.12/http/server.py", line 436, in handle
2024-10-26T22:15:21.784396009Z     self.handle_one_request()
2024-10-26T22:15:21.784528152Z   File "/usr/local/lib/python3.12/http/server.py", line 424, in handle_one_request
2024-10-26T22:15:21.784892417Z     method()
2024-10-26T22:15:21.785022207Z   File "/usr/src/app/./app.py", line 50, in do_GET
2024-10-26T22:15:21.785133745Z     self._error(e)
2024-10-26T22:15:21.785230833Z   File "/usr/src/app/./app.py", line 27, in _error
2024-10-26T22:15:21.785374280Z     self.end_headers()
2024-10-26T22:15:21.785496450Z   File "/usr/local/lib/python3.12/http/server.py", line 538, in end_headers
2024-10-26T22:15:21.785606428Z     self.flush_headers()
2024-10-26T22:15:21.785697012Z   File "/usr/local/lib/python3.12/http/server.py", line 542, in flush_headers
2024-10-26T22:15:21.785800081Z     self.wfile.write(b"".join(self._headers_buffer))
2024-10-26T22:15:21.785899353Z   File "/usr/local/lib/python3.12/socketserver.py", line 840, in write
2024-10-26T22:15:21.785999264Z     self._sock.sendall(b)
2024-10-26T22:15:21.786087818Z BrokenPipeError: [Errno 32] Broken pipe
2024-10-26T22:15:21.786179593Z ----------------------------------------

See my post in Samsung TV Plus for Channels - #35 by chDVRuser

This is... WOW !!

:exploding_head: :astonished:

Oh yes. This is gonna be very, very useful for the FAST sources. A lot of the same channels are found there so I may have a use case for this :rofl::joy:

This seems like it'll be phenomenal!

...That said, I can't for the life of me seem to get it working. Each playlist I try gives me the same message. For example, when I tried the PBS bridge:
WARNING: m3u URL not found for 'PBS'. Skipping...

I've yet to have a single m3u work so far. I'm copying the playlist links directly to add them in, so I'm not quite sure what to do if I want to actually experience any of this. Any help would be greatly appreciated.

I'm getting the same warning message with all the sources I added.

2024-10-29 22:50:17.984083:  Starting combination of playlists...
[DEBUG | 2024-10-29 22:50:17,992] - Starting new HTTP connection (1): 127.0.0.1:8089

2024-10-29 22:50:17.994250:  WARNING: m3u URL not found for 'DirecTV (TVE)'. Skipping...
[DEBUG | 2024-10-29 22:50:17,995] - Starting new HTTP connection (1): 127.0.0.1:8089

2024-10-29 22:50:17.995509:  WARNING: m3u URL not found for 'EPlusTV (Linear Channels)'. Skipping...
[DEBUG | 2024-10-29 22:50:17,995] - Starting new HTTP connection (1): 127.0.0.1:8089

2024-10-29 22:50:17.996207:  WARNING: m3u URL not found for 'Frndly TV (Gracenote EPG)'. Skipping...
[DEBUG | 2024-10-29 22:50:17,997] - Starting new HTTP connection (1): 127.0.0.1:8089

2024-10-29 22:50:17.997295:  WARNING: m3u URL not found for 'Frndly TV (No Gracenote)'. Skipping...
[DEBUG | 2024-10-29 22:50:17,997] - Starting new HTTP connection (1): 127.0.0.1:8089

2024-10-29 22:50:17.997831:  WARNING: m3u URL not found for 'PBS (VLC Bridge)'. Skipping...

2024-10-29 22:50:17.997856:  WARNING: No data to compare, skipping adding and removing rows in PlaylistManager_Combinedm3us.csv.


2024-10-29 22:50:17.997956:  Finished combination of playlists.

FYI, for each source, I got the URLs from Settings > Sources:

Change the first part of the URL to have your local IP, not the remote access one.

That would be http://localhost:port/
or
http://<serverIP>:port/

These are working for me:

This is a phenomenal development. Thanks once again @babsonnexus. You're the real MVP!

2 Likes

This fixed my issue! Turns out I needed to switch from using "localhost" to "[computer-name].local".

First, awesome job!

Second, what would be the best way to handle this scenario. I have 4 HDHR's, TVE, and Frndly. I prefer stations like metv and mvies on Frndly because they are higher quality than OTA. Right now i put all of the ota stations i want to keep and the frndly replacements in a channel collection. The problem (or annoyance) is that the channels go like 4.1, 4.2, 5592, 4.4, 5388 etc. I am loving the prospect of using this tool to renumber some of my channels easily and use the OTA equivalent as a backup. However, I'm not grasping how to use this with OTA and TVE. Would i just need an intermediate Channels server that would house my 4 HDHR's and TVE, then that would feed my main Channels server via m3u? Then the only source in my main DVR would be the PLM playlists?

Im just not grasping how to do this without a middleware DVR (which is no problem)

Another thing that might work is this:

Just use host.docker.internal in place of localhost, the machine name, or IP address. You can also use the the Test button in the Channels URL setting and make sure the link works first. Shouldn't harm anything unless you click Save!

1 Like

For anything that Channels doesn't create innately (i.e., OTA, TVE), I'd just use the direct links you are already putting in. Like for the EPlusTV (Linear Channels), I'd just use the same links in PLM as what you put in Channels; Channels doesn't have to be the middleman for everything.

The end-goal is to only need to put PLM links into Channels, so those other ones should go away. Obviously, I'm not there yet, either, but damn close!

Here's what I'm thinking:

  • Get the 4 HDHR and TVE links from your main Channels setup

  • Get the Frndly links directly from that container

  • Make sure METV and MVIES are not clicked as favorites for the OTA and TVE

  • In PLM, create a Parent for each version you want to keep. So "METV (Frndly)", "METV (TVE)", "METV (4.1)", etc... and assign the children accordingly

  • If you create them in order or preference, by default they will end up with Channel numbers in that order. So, if those I listed above were the 12th, 13th, and 14th parents you created, and you kept the default starting station number of 10000, they would be 10012, 10013, 10014.

  • Conversely, you could manually assign them any number in any order you want

  • Either way, put your favorite in Channels on the PLM Frndly one so it will be the default for recording

  • Also make the PLM source higher priority than all the other sources in Channels.

  • These actions combined together should be able to get you the order you want to see. So you'll end up with something like 10012, 100013, 10014, 4.1, 4.2, 5592, etc...

Just a heads up, too, with Channels-based OTA and TVE stations (or any stations that you pull the playlists from Channels): when you run a PLM station, it will also run the equivalent Channels station. So for me, if I tune to this PLM station:

image

Channels also tunes to the internal HDHR station:

image

For that reason and that reason alone, I might consider setting up an intermediate Channels server so it is not pulling double-duty while watching/recording. I'm also thinking about pulling my HDHR directly and doing the Gracenote mapping function.

I've been messing with PLM...and yeah...I got a lot of channels in there and it's sluggish. Tough to mass ignore a lot of the channels I don't want. I wonder if it's just the limitation of python... :thinking:

I second that, this sound very interesting.

This is an amazing, gamechanger of a utiliity. It will take me awhile to get there, but I'm going through a combined list of about 2000 FAST channels and hope to end up with tight, clean list.

My first question is about channels that I only have a single version of. They are "unassigned" by default, and therefore aren't included in the final output, along with any I've "ignored." So do I need to make each of those a "parent" too (but without any child versions) to include them in the final list? Is there an easier way to handle this? I'm scrolling for duplicates and triplicates but there are also hundreds that are singles so making them all childless parents doesn't seem like the best answer here. Am I wrong?

I also have three "quality of life" improvements that I think would make this curation a lot easier.

  1. with the channels that have descriptions, the field is so small that I can only read the first 4-5 words. A pop-up that reveals the entire description when hovering or clicking, would be greatly appreciated

  2. How about a search/filter field, so we can type a few characters like "21 jump" to quickly see the channels that match?

3). when I am clicking "make parent" it refreshes the page and then brings me back up to the top of the page, then I have to scroll down and find the children to assign. This would be a lot easier if the scroll position persisted we could continue with the assignments, and not have to scroll down all the way down back to where we just were.

Thanks again for this fantastic and thoughtful utility. It’ll supercharge and streamline the Channels interface with our previously-overwhelming list of available channels, allowing us to “clean it up” in a way I hadn't conceived possible. It’ll certainly make selecting what to watch when channel surfing easier too!

This seems pretty awesome, just trying to get my head around it. Would it be accurate to think of "Playlist Manager" as a list of what Channels DVR currently refers to as "Sources?"

Most likely not the Python because it's returning what's necessary in microseconds, but really the html/javascript, or at least how I'm using it. I probably need to invest some time into lazy/partial loading so it doesn't try to render all several thousand cells at once and just does them in batches of 100 or something. There was an earlier version where everything was one page instead of four and it took forever to load. Separating them made the pages without thousands comes back pretty instant. In the background, though, the Python is preparing all the PLM pages at once, so I'm quite confident its on the html-side.

1 Like

Yes, Parents are the the final list of stations, so they need at least one child to show up. When you are going through the list, just choose the Make Parent option on those single ones:

image

The new Parent will be created and the child will be assigned to that Parent. You can do that en masse, so just do all hundreds of them at once and you'll be all set!

Just a heads up that you can click into them and scroll over.

image

But I'll look into being able to put pop-up hovers on all fields like this. Could be good on the SLM side, too!

Maybe... this isn't a table (even though it looks like one), so not as easy, but might be a possibility.

Hmmmm... dynamic anchoring, sounds tricky...

It would be more accurate to say that PLM is a "Custom Channels" "Source" for Channels DVR, but as a replacement from needing to have many disconnected "Sources" to only having one or a few streamlined integrated "Sources".