Project One-Click: All-in-One Installations of Docker Extensions and CDVR Custom Channels

It works great! Thanks so much.

New OliveTin-for-Channels pushed this morning as bnhf/olivetin:latest (aka bnhf/olivetin:2025.01.27) with updates to several Project One-Click Actions.

The SamsungTVPlus-for-Channels now uses vertical bars in place of commas as a delimiter when selecting multiple regions for guide data:

And, the most important update, in preparation for the Super Bowl -- the Tubi-for-Channels Project One-Click Action now supports fields for your Tubi username and password (required for viewing the SB via Tubi):

If you simply want to update an existing Portainer-Stack for either of these projects, you can use the Docker Compose Examples available in Project One-Click:

screenshot-htpc6-2025.01.27-09_09_04

This will always give the latest recommended Docker Compose, including a version date. These composes are designed to be used without editing, as anything unique to your installation is contained in the env vars. Sample variables are shown under Standard Error.

If you're updating, stop the stack, paste in the latest compose and check if you need to supply any new env vars based on the samples. Brief descriptions of the values required are shown in the compose's comments. Click the Update the stack button, and be sure to use the Re-pull and redeploy slider to be sure you have the latest version of the container from Docker Hub or GitHub.

1 Like

I was wondering, does the Project One Click Tubi installation routine set up two sources in Channels DVR? (gracenote include w/epg and exclude wo/epg)?

Looking at his shell script, it only creates the gracenote include source, not both.
It's not hard to create the exclude w/o epg source yourself.

Not currently, but it could. I have a framework for doing it that way I use for FrndlyTV. Does the container publish unique M3Us for channels with and without Gracenote data?

@jsfullam As @chDVRuser said, I was creating the Custom Channels Source with the Gracenote M3U only. But, I've rectified that now -- both will be created. I'll push a new container with that fix shortly.

New OliveTin-for-Channels Project One-Click bnhf/olivetin:latest (aka bnhf/olivetin:2025.01.30) pushed this morning with improved support for Tubi-for-Channels.

Two Custom Channel Sources are now created along with the Portainer-Stack when using the One-Click Action.

The first source is called Tubi TV and includes all channels that have Gracenote guide data. When specifying a CDVR starting channel number in the Action, that number will be used in the custom sources.

The second source is called Tubi TV-NoEPG and will include those channels without Gracenote data, and will use the XML file in the container for guide data. When specifying a CDVR starting channel number, the channel numbers for this second source will begin at +300 from your specified number, to allow room for the Gracenote channels.

Here's what the auto created Custom Channels Sources will look like:

1 Like

Thanks. That's what I had done a while back. I was just thinking about streamlining the installation for others. :slightly_smiling_face:

2 Likes

HI, I just attempted to use Project One-Click to set up Plex on my back-up server. (Synology NAS) Unfortunately, I received the following error:

JSON response from http://192.168.1.25:9000/api/stacks?type=2&method=string&endpointId=2:
{"Id":4,"Name":"plex-for-channels","Type":2,"EndpointId":2,"SwarmId":"","EntryPoint":"docker-compose.yml","Env":[{"name":"TAG","value":"latest"},{"name":"HOST_PORT","value":"7779"},{"name":"PLEX_PORT","value":"7777"},{"name":"PLEX_CODE","value":"local"}],"ResourceControl":{"Id":4,"ResourceId":"2_plex-for-channels","SubResourceIds":[],"Type":6,"UserAccesses":[],"TeamAccesses":[],"Public":false,"AdministratorsOnly":true,"System":false},"Status":1,"ProjectPath":"/data/compose/4","CreationDate":1739236281,"CreatedBy":"john","UpdateDate":0,"UpdatedBy":"","AdditionalFiles":null,"AutoUpdate":null,"Option":null,"GitConfig":null,"FromAppTemplate":false,"Namespace":"","IsComposeFormat":false}
true
{"error":"invalid source url: Get \"http://192.168.1.25:7779/plex/local/playlist.m3u\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)"}

It looks like there's an extra backslash at the end of the playlist address:
Get "http://192.168.1.25:7779/plex/local/playlist.m3u"
:thinking:

Trying to set this up and get error

failed to deploy a stack: Network olivetin_default Creating Network olivetin_default Created Container olivetin Creating Container static-file-server Creating Container static-file-server Created Container olivetin Created Container static-file-server Starting Container olivetin Starting Error response from daemon: Mounts denied: The path /users/olivetin is not shared from the host and is not known to Docker. You can configure shared paths from Docker -> Preferences... -> Resources -> File Sharing. See Change settings | Docker Docs for more info.
I'm using a M@ Mac Mini

TAG=latest
DOMAIN=local
HOST_PORT=1337
CHANNELS_DVR_HOST=local-server
CHANNELS_DVR_PORT=8089
CHANNELS_DVR2_HOST=another-server
CHANNELS_DVR2_PORT=8089
CHANNELS_CLIENTS=Living Room Dixon Living Room Master Bedroom
ALERT_SMTP_SERVER=smtp.gmail.com:587
ALERT_EMAIL_FROM=--------------@gmail.com
ALERT_EMAIL_PASS=------------
ALERT_EMAIL_TO=------------@gmail.com
UPDATE_YAMLS=true
UPDATE_SCRIPTS=true
TZ=US/Central
HOST_DIR=/users
DVR_SHARE=/Volumes/External Drive/Channels DVR
LOGS_SHARE=/Volumes/External Drive/Channels DVR
TUBEARCHIVIST_SHARE=/Volumes/External Drive/Channels DVR
HOST_SFS_PORT=8080
FOLDER=/web
PORTAINER_TOKEN=ptr_TP3q1aaHghJfhw94AMoa6ulP04EI93wfbogZlsKSb3c=
PORTAINER_HOST=172.17.0.3 (I pulled this from IP address listed on the portainer docker)
PORTAINER_PORT=9443

This needs to be the actual, resolvable hostname of your CDVR server, or its IP address.

Leave these two blank if you don't actually have any additional CDVR servers running

These should either be the actual, resolvable hostnames of your client devices, or their IP addresses. Space separated.

This is explained here, but I believe most use /Users/<your Mac username> (note the capital U):

This is the path to your CDVR executables on your Mac, if you don't know it it's easy enough to figure out once you get OliveTin going. Use the Generate Filtered Log Action and serch the last 100000 lines for [SYS] Start, and then use the path shown without the last /data portion.

Since your CDVR server and Docker host are one in the same, use the same resolvable hostname or IP address as you used for your CHANNELS_DVR_HOST

Those backslashes are just escape characters for the double quotes. I'll check out what's going on tomorrow, but maybe the URL format changed for the M3U?

EDIT: The plex-for-channels stack portion deployed successfully, so you should be able to go to http://192.168.1.25:7779 and see if there's a change in the list of M3U options.

EDIT2: Also, check the Portainer log for this container, as I just noticed this, which could have prevented it from fully spinning-up:

Plex container seems to have deployed successfully and is running.

Plex Playlist v1.22
Last Updated: Jan. 22, 2025

PLEX LOCAL: http://192.168.1.25:7779/plex/local/playlist.m3u
PLEX LOCAL MJH Compatible: http://192.168.1.25:7779/mjh_compatible/plex/local/playlist.m3u
PLEX LOCAL Gracenote Playlist: http://192.168.1.25:7779/plex/local/playlist.m3u?gracenote=include
PLEX LOCAL EPG Only Playlist: http://192.168.1.25:7779/plex/local/playlist.m3u?gracenote=exclude

PLEX LOCAL EPG: http://192.168.1.25:7779/plex/epg/local/epg-local.xml
PLEX LOCAL EPG GZ: http://192.168.1.25:7779/plex/epg/local/epg-local.xml.gz

Plex container log:

[INFO] Device ID Generated
[INFO] Device ID: 479b159a-4283-4bfe-8a43-8393374e1732
⇨ http server started on [::]:7777
[INFO] Starting thread for code local
[INFO] Running EPG Scheduler for local
[INFO] New token for local generated at 2025-02-11 01:11.35 +0000
[INFO] Updating Channel List for local
[INFO] Updating Channel List for local
[INFO] Channel Listing for local Complete
[INFO] Day One Initialization of EPG data
[INFO] Retrieving local EPG data for 2025-02-11 through 2025-02-11
[INFO] Channel Listing for local Complete
[INFO] Continuing to retrive local EPG data....Elapsed time: 31.76 seconds. 150 Channels parsed
[INFO] Continuing to retrive local EPG data....Elapsed time: 72.12 seconds. 300 Channels parsed. Please wait
[INFO] Continuing to retrive local EPG data....Elapsed time: 104.12 seconds. 450 Channels parsed
[INFO] Continuing to retrive local EPG data....Elapsed time: 142.94 seconds. 600 Channels parsed. Please wait
[INFO] Retrieving local EPG data complete. Elapsed time: 144.44 seconds. 615 Channels parsed.
[INFO] EPG Scheduler Complete for local
[INFO] Returning Cached Channel List for local
[INFO] Returning Cached Channel List for local
[INFO] Running EPG Scheduler for local
[INFO] Retrieving local EPG data for 2025-02-12 through 2025-02-12
[INFO] Continuing to retrive local EPG data....Elapsed time: 32.20 seconds. 150 Channels parsed
[INFO] Continuing to retrive local EPG data....Elapsed time: 71.94 seconds. 300 Channels parsed. Please wait
[INFO] Continuing to retrive local EPG data....Elapsed time: 104.06 seconds. 450 Channels parsed
[INFO] Continuing to retrive local EPG data....Elapsed time: 143.97 seconds. 600 Channels parsed. Please wait
[INFO] Retrieving local EPG data complete. Elapsed time: 145.59 seconds. 615 Channels parsed.
[INFO] EPG Scheduler Complete for local
[INFO] Returning Cached Channel List for local

Manually adding the Plex custom channel source using the addresses from the container (as below) is successful.
http://192.168.1.25:7779/plex/local/playlist.m3u
http://192.168.1.25:7779/plex/epg/local/epg-local.xml

It seems that it only failed when running the One-Click action.

Very helpful info, thanks. I'll take a look

Thank you, that worked. However, now when I go to OliveTin this is what I get when I try to setup Tubi for example:

Standard output:JSON response from https://local:8089:9443/api/stacks?type=2&method=string&endpointId=2:

Standard error:exit status 1

+ dvr=local:8089:8089
++ basename /config/tubi-for-channels.sh
+ extension=tubi-for-channels.sh
+ extension=tubi-for-channels
+ cp /config/tubi-for-channels.env /tmp
+ envFile=/tmp/tubi-for-channels.env
+ [[ -n local:8089 ]]
+ extensionURL=local:8089:7778
+ [[ none == \n\o\n\e ]]
+ tubiUser=
+ [[ none == \n\o\n\e ]]
+ tubiPass=
+ [[ # == \# ]]
+ cdvrStartingChannel=
+ [[ -n '' ]]
+ cdvrIgnoreM3UNumbers=
+ [[ -n '' ]]
+ curl -s -o /dev/null http://local:8089:7778
+ envVars=("TAG=$2" "HOST_PORT=$3" "TUBI_PORT=$4" "TUBI_USER=$tubiUser" "TUBI_PASS=$tubiPass" "CDVR_STARTING_CHANNEL=$7")
+ printf '%s\n' TAG=latest HOST_PORT=7778 TUBI_PORT=7777 TUBI_USER= TUBI_PASS= CDVR_STARTING_CHANNEL=#
+ sed -i /=#/d /tmp/tubi-for-channels.env
+ /config/portainerstack.sh tubi-for-channels
+ stackName=tubi-for-channels
+ portainerHost=local:8089
+ [[ -n 9443 ]]
+ portainerPort=9443
+ curl -s -o /dev/null http://local:8089:9000
+ portainerURL='https://local:8089:9443/api/stacks?type=2&method=string&endpointId=2'
+ portainerToken=ptr_TP3q1aaHghJfhw94AMoa6ulP04EI93wfbogZlsKSb3c=
+ cp /config/tubi-for-channels.yaml /tmp
+ stackFile=/tmp/tubi-for-channels.yaml
+ envFile=/tmp/tubi-for-channels.env
++ grep DVR_SHARE= /tmp/tubi-for-channels.env
++ grep -v /
++ awk -F= '{print $2}'
+ dockerVolume=
++ grep VOL_EXTERNAL= /tmp/tubi-for-channels.env
++ grep -v '#'
++ awk -F= '{print $2}'
+ volumeExternal=
++ grep VOL_NAME= /tmp/tubi-for-channels.env
++ grep -v '#'
++ awk -F= '{print $2}'
+ volumeName=
++ grep NETWORK_MODE= /tmp/tubi-for-channels.env
++ grep -v '#'
++ awk -F= '{print $2}'
+ networkMode=
++ grep DEVICES= /tmp/tubi-for-channels.env
++ grep -v '#'
++ awk -F= '{print $2}'
+ transcoderDevice=
++ grep CDVR_CONTAINER= /tmp/tubi-for-channels.env
++ grep -v '#'
++ awk -F= '{print $2}'
+ stackNumber=
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
+ [[ -n '' ]]
++ sed 's/\\/\\\\/g' /tmp/tubi-for-channels.yaml
++ sed 's/"/\\"/g'
++ awk '{printf "%s\\n", $0}'
+ stackContent='version: '\''3.9'\''\nservices:\n  tubi-for-channels:\n    # 2025.01.27\n    # GitHub home for this project with setup instructions: https://github.com/jgomez177/tubi-for-channels\n    # Docker container home for this project: https://ghcr.io/jgomez177/tubi-for-channels\n    image: ghcr.io/jgomez177/tubi-for-channels:${TAG}\n    container_name: tubi-for-channels\n    ports:\n      - ${HOST_PORT}:${TUBI_PORT} # Use the recommended port number, or optionally change it if the port is already in use on your host.\n    environment:\n      - TUBI_PORT=${TUBI_PORT} # Change the port this container uses internally.\n      - TUBI_USER=${TUBI_USER} # Optional variable to sign into Tubi Account.\n      - TUBI_PASS=${TUBI_PASS} # Optional variable to sign into Tubi Account.\n    restart: unless-stopped\n\n  # Default Environment variables can be found below under stderr -- copy and paste into Portainer-Stacks Environment variables section in Advanced mode\n'
+ stackEnvVars='['
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},'
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},'
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},'
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},{"name": "TUBI_USER", "value": ""},'
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},{"name": "TUBI_USER", "value": ""},{"name": "TUBI_PASS", "value": ""},'
+ IFS==
+ read -r key value
+ stackEnvVars='[{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},{"name": "TUBI_USER", "value": ""},{"name": "TUBI_PASS", "value": ""}]'
++ cat
+ stackJSON='{
  "Name": "tubi-for-channels",
  "SwarmID": "",
  "StackFileContent": "version: '\''3.9'\''\nservices:\n  tubi-for-channels:\n    # 2025.01.27\n    # GitHub home for this project with setup instructions: https://github.com/jgomez177/tubi-for-channels\n    # Docker container home for this project: https://ghcr.io/jgomez177/tubi-for-channels\n    image: ghcr.io/jgomez177/tubi-for-channels:${TAG}\n    container_name: tubi-for-channels\n    ports:\n      - ${HOST_PORT}:${TUBI_PORT} # Use the recommended port number, or optionally change it if the port is already in use on your host.\n    environment:\n      - TUBI_PORT=${TUBI_PORT} # Change the port this container uses internally.\n      - TUBI_USER=${TUBI_USER} # Optional variable to sign into Tubi Account.\n      - TUBI_PASS=${TUBI_PASS} # Optional variable to sign into Tubi Account.\n    restart: unless-stopped\n\n  # Default Environment variables can be found below under stderr -- copy and paste into Portainer-Stacks Environment variables section in Advanced mode\n",
  "Env": [{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},{"name": "TUBI_USER", "value": ""},{"name": "TUBI_PASS", "value": ""}]
}'
+ echo 'JSON response from https://local:8089:9443/api/stacks?type=2&method=string&endpointId=2:'
++ curl -k -X POST -H 'Content-Type: application/json' -H 'X-API-Key: ptr_TP3q1aaHghJfhw94AMoa6ulP04EI93wfbogZlsKSb3c=' -d '{
  "Name": "tubi-for-channels",
  "SwarmID": "",
  "StackFileContent": "version: '\''3.9'\''\nservices:\n  tubi-for-channels:\n    # 2025.01.27\n    # GitHub home for this project with setup instructions: https://github.com/jgomez177/tubi-for-channels\n    # Docker container home for this project: https://ghcr.io/jgomez177/tubi-for-channels\n    image: ghcr.io/jgomez177/tubi-for-channels:${TAG}\n    container_name: tubi-for-channels\n    ports:\n      - ${HOST_PORT}:${TUBI_PORT} # Use the recommended port number, or optionally change it if the port is already in use on your host.\n    environment:\n      - TUBI_PORT=${TUBI_PORT} # Change the port this container uses internally.\n      - TUBI_USER=${TUBI_USER} # Optional variable to sign into Tubi Account.\n      - TUBI_PASS=${TUBI_PASS} # Optional variable to sign into Tubi Account.\n    restart: unless-stopped\n\n  # Default Environment variables can be found below under stderr -- copy and paste into Portainer-Stacks Environment variables section in Advanced mode\n",
  "Env": [{"name": "TAG", "value": "latest"},{"name": "HOST_PORT", "value": "7778"},{"name": "TUBI_PORT", "value": "7777"},{"name": "TUBI_USER", "value": ""},{"name": "TUBI_PASS", "value": ""}]
}' 'https://local:8089:9443/api/stacks?type=2&method=string&endpointId=2'
curl: (3) URL using bad/illegal format or missing URL
+ portainerResponse=
+ [[ -z '' ]]
+ exit 1
+ [[ 1 == 1 ]]
+ exit 1

Any idea what I'm doing wrong? It does not show up as a source in Channels and I have no idea how to find the link and epg to add as a source.

Looks like you used local:8089 for the PORTAINER_HOST value. This should be just the resolvable hostname, or the IP address (which is what most people use) for your Mac Mini -- do not include any port number in this value.

You'll need to stop the OliveTin Stack, make this change in the env vars section, and then click the Update button.

So, just for clarification, I would use the Mac IP address?

Yes.

Perfect, that worked. Now I see the Tubi stack, but how do I pull the m3u and the guide link? I appreciate your patience!

It deployed fine for me, and in fact was much faster than last time I tried it, due to improvements made by @joagomez I believe.

I'm wondering if you ran into the same brief outage reported by @chDVRuser when you were spinning it up? Everything seems fine with the Action otherwise, and it's working now.