[RELEASE] Playlist Manager for Channels [Streaming Library Manager Extension]

As a matter of fact, PLM doesn't care what the extension is at all (or if there even is an extension) when it's parsing through source playlists. All it needs is a compatible format inside the file.

Same.

Yep, nothing can be done with info provided thus far. Everything described is already supported out of the box.

Ok good to know, I dont know why this turned into a whole conspiracy theory, but I regress. Have a good day everyone

I came back to this thread to search for info on channel numbering after seeing how easy PLM made it, and found this. I’ve always wanted an easy way to renumber channels, and PLM could be it (although I know that’s not your intended purpose of PLM). Is there a way to reorder the parents after creation?

1 Like

PLM is whatever you need it to be!

Anyway, you can either just change the starting station number, which will renumber every station (but keep their relative position to each other at time of creation); or just put in whatever number you want (including with decimal places):

I do that for some, but not all, so a nice mix depending upon needs and desires.

2 Likes

In the next release, there will be a way to filter this and pretty much every (with one minor exception) drop-down list not just in PLM, but in all the extensions!

2 Likes

Need a way to add YouTube Live Streams to Channels and are having issues with other methods? Want a simple way to add streaming live videos from websites but have no idea how to make your own custom m3u? Well, no matter your use case or how you want to go about it technically, there's good news for you in the latest release of Streaming Library Manager!

3 Likes

@babsonnexus For the creation of the custom source in Channels DVR, you could do it automatically from SLM by sending the right request to the server. :slightly_smiling_face:

Interesting... do you know what those requests are (push, put, etc...)? This definitely sounds like something MTM could do!

Maybe @bnhf could share examples that he implemented in OliveTin with project One-Click. :slightly_smiling_face:

Internal Server Error...
Hi All, first time posting.
I just updated to the latest V2025.01.16.1518 but I also saw it in the previous version.
BTW, thanks for this latest update, as it helps a lot when I'm assigning the channels due to the new search function when selected the Parent Station field.

Anyway, here is my issue. I am assigning my channels, and I then save them. I do about 10 at a time.
After it saves, then I click the button to Update the m3u and xml.
It works (spins) for a bit and then it gives me the internal server error.
I'm not sure if it actually crashes, as I just reload the main web page and I'm back up and running.
None of the container logs shows there was an issue, only the screen and the log in SLM (via Controls, Logs).

Below is a part of the SLM log that's of interest.
Note that the only time I get the error is when I try and Update the m3u.
Also note that it does indeed gives me a new m3u as when I load it in VLC, I actually see the increase in the number of channels I had added/assigned.

**************Start of log portion **************
[INFO | 2025-01-16 15:25:00,371] - 10.89.2.16 - - [16/Jan/2025 15:25:00] "e[36mGET /static/assets/js/core/bootstrap.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-16 15:25:00,392] - 10.89.2.16 - - [16/Jan/2025 15:25:00] "e[36mGET /static/assets/js/plugins/smooth-scrollbar.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-16 15:25:00,422] - 10.89.2.16 - - [16/Jan/2025 15:25:00] "e[36mGET /static/assets/js/material-dashboard.min.js?v=3.0.0 HTTP/1.1e[0m" 304 -
[INFO | 2025-01-16 15:25:00,499] - 10.89.2.16 - - [16/Jan/2025 15:25:00] "e[36mGET /static/assets/img/slm_flavicon.png HTTP/1.1e[0m" 304 -
Deleted: plm_epg_hls_m3u_01.m3u
Created: plm_epg_hls_m3u_01.m3u
[DEBUG | 2025-01-16 15:25:09,869] - Starting new HTTPS connection (1): bit.ly:443
[DEBUG | 2025-01-16 15:25:10,153] - https://bit.ly:443 "GET /moj-epg-gz HTTP/1.1" 301 128
[DEBUG | 2025-01-16 15:25:10,154] - Starting new HTTPS connection (1): github.com:443
[DEBUG | 2025-01-16 15:25:10,667] - https://github.com:443 "GET /dtankdempse/moveonjoy-m3u/raw/refs/heads/main/epg.xml.gz HTTP/1.1" 302 0
[DEBUG | 2025-01-16 15:25:10,671] - Starting new HTTPS connection (1): raw.githubusercontent.com:443
[DEBUG | 2025-01-16 15:25:11,164] - https://raw.githubusercontent.com:443 "GET /dtankdempse/moveonjoy-m3u/refs/heads/main/epg.xml.gz HTTP/1.1" 200 1585998
[ERROR | 2025-01-16 15:25:11,432] - Exception on /playlists/plm_main [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1511, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 917, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/slm.py", line 2662, in webpage_playlists
get_final_m3us_epgs()
File "/app/slm.py", line 3247, in get_final_m3us_epgs
get_epgs_for_m3us()
File "/app/slm.py", line 3390, in get_epgs_for_m3us
response_text = response.content.decode('utf-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
2025-01-16 15:25:11.435368: ERROR: Webpage responded... 500 INTERNAL SERVER ERROR
Client IP: 10.89.2.16
Method: POST
URL: http://10.0.0.3:5000/playlists/plm_main
Headers:
Host: 10.0.0.3:5000
Connection: keep-alive
Content-Length: 39301
Cache-Control: max-age=0

**************End of log portion **************

I just want to say how much I appreciate the detailed documentation you produce for your projects. First rate!

Welcome to the community, @Waffles!

Okay, I see the issue. Your link to the XML Guide Data is a shortened URL to a compressed (.gz) file and PLM is expecting a direct link, looking for the link to end in .gz. This will be fixed in the next release (already tested a solution). In the meantime, you could either:

  • Use the non-compressed/"XML Format" link available at your provider

  • If you are concerned about the size and download time of the guide data, you could use the direct link to the compressed version by getting the "raw" link

  • If you are using Gracenote matching with the Parents, though, you could just remove the link to the XML Guide Data entirely as it is unnecessary

Sounds like a working version of hlstube on steroids.

1 Like

@babsonnexus - Thanks for the reply and the tip. I was able to get it to work using the uncompressed format. It took 4 minutes to download the xml file though.

The reply will be in two posts...one for the update on pulling the raw xml file... the second post will be a follow-up on the raw .gz file...

Part 1: Here is a portion with the other .gz files for comparison:
[DEBUG | 2025-01-17 09:04:10,558] - https://raw.githubusercontent.com:443 "GET /matthuisman/i.mjh.nz/d69fdfa30e5e12561c0d7c961476f7680589e95e/SamsungTVPlus/us.xml.gz HTTP/1.1" 200 353079
[DEBUG | 2025-01-17 09:04:10,747] - Starting new HTTPS connection (1): i.mjh.nz:443
[DEBUG | 2025-01-17 09:04:11,049] - https://i.mjh.nz:443 "GET /Stirr/all.xml.gz HTTP/1.1" 302 None
[DEBUG | 2025-01-17 09:04:11,051] - Starting new HTTPS connection (1): raw.githubusercontent.com:443
[DEBUG | 2025-01-17 09:04:11,422] - https://raw.githubusercontent.com:443 "GET /matthuisman/i.mjh.nz/d69fdfa30e5e12561c0d7c961476f7680589e95e/Stirr/all.xml.gz HTTP/1.1" 200 152423
[DEBUG | 2025-01-17 09:04:11,546] - Starting new HTTP connection (1): 10.0.0.3:8179
[DEBUG | 2025-01-17 09:04:11,548] - http://10.0.0.3:8179 "GET /epg HTTP/1.1" 302 None
[DEBUG | 2025-01-17 09:04:11,549] - Starting new HTTPS connection (1): raw.githubusercontent.com:443
[DEBUG | 2025-01-17 09:04:11,904] - https://raw.githubusercontent.com:443 "GET /dtankdempse/thetvapp-m3u/refs/heads/main/guide/epg.xml HTTP/1.1" 200 1510043
[DEBUG | 2025-01-17 09:04:12,234] - Starting new HTTPS connection (1): bit.ly:443
[DEBUG | 2025-01-17 09:04:12,613] - https://bit.ly:443 "GET /tubi-epg HTTP/1.1" 301 139
[DEBUG | 2025-01-17 09:04:12,617] - Starting new HTTPS connection (1): raw.githubusercontent.com:443
[DEBUG | 2025-01-17 09:04:12,980] - https://raw.githubusercontent.com:443 "GET /dtankdempse/tubi-m3u/refs/heads/main/tubi_epg_us.xml HTTP/1.1" 200 470300
[INFO | 2025-01-17 09:08:17,050] - 10.89.2.20 - - [17/Jan/2025 09:08:17] "GET /logs HTTP/1.1" 200 -

The issue is while I don't get the error, the screen never changes from Processing - Please wait while the process completes...
I waited over 10 minutes and then finally just reloaded the page...

@babsonnexus
This is part 2... this is just a fyi in case you see something that either I missed or if something else needs a review.

So, first I tried to use the raw .gz link to the file...
Unfortunately, it still gave me the 500 error...
Here's that part of the log:

[DEBUG | 2025-01-17 09:01:00,292] - Starting new HTTPS connection (1): github.com:443
[DEBUG | 2025-01-17 09:01:00,915] - https://github.com:443 "GET /dtankdempse/moveonjoy-m3u/blob/b44582e3f4c3797107f695da7244a1f3b79d9abf/epg.xml.gz HTTP/1.1" 200 None
[ERROR | 2025-01-17 09:01:01,017] - Exception on /playlists/plm_main [POST]
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 1511, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 917, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/flask/app.py", line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/slm.py", line 2662, in webpage_playlists
get_final_m3us_epgs()
File "/app/slm.py", line 3247, in get_final_m3us_epgs
get_epgs_for_m3us()
File "/app/slm.py", line 3388, in get_epgs_for_m3us
response_text = gz.read().decode('utf-8')
^^^^^^^^^
File "/usr/local/lib/python3.12/gzip.py", line 324, in read
return self._buffer.read(size)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/_compression.py", line 118, in readall
while data := self.read(sys.maxsize):
^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/gzip.py", line 527, in read
if not self._read_gzip_header():
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/gzip.py", line 496, in _read_gzip_header
last_mtime = _read_gzip_header(self._fp)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/gzip.py", line 456, in _read_gzip_header
raise BadGzipFile('Not a gzipped file (%r)' % magic)
gzip.BadGzipFile: Not a gzipped file (b'\n\n')
2025-01-17 09:01:01.066522: ERROR: Webpage responded... 500 INTERNAL SERVER ERROR
Client IP: 10.89.2.20

---- I guess his .gz file is messed up as PLM is able to download other .gz files

Follow up- I guess I didn't wait long enough... I redid the update xml link and left the computer to do something else.. when I returned, I had the console back and below is the log. I guess the previous entry wasn't 10 minutes.

[INFO | 2025-01-17 09:34:28,218] - 10.89.2.20 - - [17/Jan/2025 09:34:28] "e[35me[1mGET /playlists/files/plm_epg_hls_m3u_01.m3u HTTP/1.1e[0m" 206 -
Deleted: plm_epg_hls_m3u_01.xml
Created: plm_epg_hls_m3u_01.xml

2025-01-17 09:42:39.982077: Finished generation of final m3u(s) and XML EPG(s).
[INFO | 2025-01-17 09:42:40,005] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "POST /playlists/plm_main HTTP/1.1" 200 -
[INFO | 2025-01-17 09:42:40,042] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/img/slm_navicon.png HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,044] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/css/nucleo-icons.css HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,049] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/css/material-dashboard.css?v=3.0.0 HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,050] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/css/nucleo-svg.css HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,088] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/js/plugins/perfect-scrollbar.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,089] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/js/core/popper.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,089] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/js/core/bootstrap.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,093] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/js/plugins/smooth-scrollbar.min.js HTTP/1.1e[0m" 304 -
[INFO | 2025-01-17 09:42:40,109] - 10.89.2.20 - - [17/Jan/2025 09:42:40] "e[36mGET /static/assets/js/material-dashboard.min.js?v=3.0.0 HTTP/1.1e[0m" 304 -

The link in your log to the .gz file isn't the right raw link; it's the Github location link (i.e., how Github renders). You'd need to click on the file and then select "Raw" to get the raw link.

FYI, I also recommend you just turning on MTM Automation instead of continually running updates in the foreground.

@babsonnexus Got it on both tips...
I found the other Raw link and updated my playlist...
I had already set up the automation, set it to run every 6 hours...
I was just doing the manual pull to test the process and trying to make it work.

Thanks again for the assist.

PUT http://<channels DVR IP>:8089/providers/m3u/sources/<name of source without spaces>

Example with IP = 127.0.0.1 and source name = "Test":

PUT http://127.0.0.1:8089/providers/m3u/sources/Test

JSON payload:

{
  "name": "Test",
  "type": "HLS",
  "source": "URL",
  "url": "http://localhost:5000/playlists/uploads/plmss_hls_m3u_01.m3u",
  "text": "",
  "refresh": "24",
  "limit": "",
  "satip": "",
  "numbering": "",
  "logos": "",
  "xmltv_url": "",
  "xmltv_refresh": ""
}

This will create a custom source named Test on the Channels DVR server located at 127.0.0.1:8089 and it will look like your screenshot on your Github:

This is good info, thank! Thus, it will be done:

2 Likes