OliveTin for Channels: An Interface for Misc Channels DVR Scripts & Tricks

Yup, that was it. Added root permissions in the compose. Here is my compose now that is working. Thanks for the help.

version: '3.9'
services:
  olivetin:
    image: jamesread/olivetin:latest
    container_name: olivetin
    user: root
    ports:
      - 1337:1337
    environment:
      - CHANNELS_DVR=${CHANNELS_DVR}
    volumes:
      - /volume1/data/olivetin:/config # replace host path or volume as needed
    restart: unless-stopped

EDIT: Well the container starts but I'm getting an error that it cannot write to the destination. Where is the CURL output going?

exit status 127

/config/listcomskipignore.sh: line 4: jq: command not found
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
curl: (23) Failure writing output to destination

Another EDIT: I see the message that jq cannot be found. I'm able to execute the listcomskipignore.sh successfully if I shell into the NAS. For some reason the scope of "jq" running in Portainer is not valid.

Another example of something I'll be doing for the Channels specific version -- which is to add dependencies we'd need. Exec into the container and run microdnf update followed by microdnf install jq

That worked. Thank you kind sir.

I see from previous threads you've messed around with manual recordings. How does this look to you as a concept for OliveTin?:



I'll add the recording image URL as a field, and of course, the cURL command to post the json. But it was easy to put together, and looks like it'd do the job without platform-specific stuff other solutions are employing.

EDIT: Like all of the OliveTin scripts I've written so far -- amazingly simple:
manualrecordings.sh:

#!/bin/bash

# Define the values
name="$1"
channel="$2"
time=$(date -d "$(date +'%Y-%m-%d') $3" +%s)
duration=$(($4 * 60))
summary="$5"

source="manual"
image="https://tmsimg.fancybits.co/assets/p9467679_st_h6_aa.jpg 2"
raw=""

# Create the JSON content
json_content=$(cat <<EOF
{
    "Name": "$name",
    "Time": $time,
    "Duration": $duration,
    "Channels": ["$channel"],
    "Airing": {
        "Source": "$source",
        "Channel": "$channel",
        "Time": $time,
        "Duration": $duration,
        "Title": "$name",
        "Summary": "$summary",
        "Image": "$image",
        "Raw": "$raw"
    }
}
EOF
)

# Output the JSON content to a file
echo "$json_content" > /config/output.json

cat /config/output.json

And the config.yaml snippet:

  - title: Manually Add Recordings
    icon: '<img src = "https://community-assets.getchannels.com/original/2X/5/55232547f7e8f243069080b6aec0c71872f0f537.png" width = "48px"/>'
    shell: /config/manualrecordings.sh "{{ name }}" {{ channel }} {{ time }} {{ duration }} "{{ summary }}"
    arguments:
      - name: name
        type: ascii_sentence
        description: The name you'd like used for the recording
      - name: channel
        type: int
        description: The channel number to use for the recording
      - name: time
        type: very_dangerous_raw_string
        description: The time to start the recording in 24h format hh:mm
      - name: duration
        type: int
        description: The length of the recording in minutes
      - name: summary
        type: ascii_sentence
        description: A Description of the recording
1 Like

Great job, @bnhf ! :clap:

1 Like

Just wanted to point out a couple things.
Looks like you copy/pasted from a post which shows 2 clicks on a url, hence you have a space and number 2 where they don't belong.

It won't hurt anything leaving "Raw": "" in the json, but it's not needed

Thanks, I appreciate the feedback. I changed that temporary hard-coded URL to an image field and the config.yaml snippet looks like this:

  - title: Manually Add Recordings
    icon: '<img src = "https://community-assets.getchannels.com/original/2X/5/55232547f7e8f243069080b6aec0c71872f0f537.png" width = "48px"/>'
    shell: /config/manualrecordings.sh "{{ name }}" {{ channel }} {{ time }} {{ duration }} "{{ summary }}" {{ image }}
    arguments:
      - name: name
        type: ascii_sentence
        description: The name you'd like used for the recording
      - name: channel
        type: int
        description: The channel number to use for the recording
      - name: time
        type: very_dangerous_raw_string
        description: The time to start the recording in 24h format hh:mm
      - name: duration
        type: int
        description: The length of the recording in minutes
      - name: summary
        type: ascii_sentence
        description: A Description of the recording (optional)
      - name: image
        type: url
        description: An image URL to use for the recording (http://$CHANNELS_DVR/admin/recordings/files/uploads to add images and get URL)
        default: http://$CHANNELS_DVR/admin/recordings/files/uploads/your_image_number/content

With the idea that one would add their desired image to DVR-Manage-Images and then replace /your_image_number/ with the actual image number. We could also have a field for just the image number, but it seems reasonable to allow for a URL from outside of Channels DVR too.

I'll get rid of "raw" as I wasn't planning on adding a field for that anyway.

Great!

I imagine some users will want to schedule things a day or more in advance, so you might consider changing your time input to accept a date/time.

@racameron did a nice job with the script he created here

I created a quick and dirty macro in Keyboard Maestro and it filled out more of the possible fields for a manual recording. Here is the user input panel.

After converting the duration from minutes to seconds I wrote it out to a temp file and passed the json as a parameter in the curl command.
You have captured the important parameters for the manual recording. I don't know how to do it in Olivetin but setting the date/time could be made easier.

OK, I think we're in business! I've built a Channels specific version of OliveTin including a config.yaml and supporting scripts.

Currently it will do the following:

  • Turn off Comskip for selected channels
  • Turn Comskip back on for selected channels
  • List the Channels with Comskip turned off
  • Create manual recordings based on channel number, date and time
  • List Gracenote Station IDs for a given search string

Here's the docker-compose for Portainer-Stacks:

version: '3.9'
services:
  olivetin:
    image: bnhf/olivetin:latest
    container_name: olivetin
    ports:
      - 1337:1337
    environment:
      - CHANNELS_DVR=${CHANNELS_DVR} # Add your Channels DVR server in the form hostname:port or ip:port
      - UPDATE_YAMLS=${UPDATE_YAMLS} # Set this to true to update config.yaml
      - UPDATE_SCRIPTS=${UPDATE_SCRIPTS} # Set this to true to update all included scripts
      - TZ=${TZ} # Add your local timezone in standard linux format. E.G. US/Eastern, US/Central, US/Mountain, US/Pacific, etc
    volumes:
      - /data/olivetin:/config # replace host path or volume as needed
    restart: unless-stopped

And the some example environment variables:

Once it's up-and-running you should be able to find the WebUI at http://<your_docker_host>:1337. Let me know how it goes with what I've defined so far, and feel free to suggest additions of existing scripts or other command line stuff you use for Channels DVR.

Now that I've built OliveTin for Channels, when you have it running let's talk about testing the Bash version of the Ruby script from earlier in the thread. This is not something I'm doing, so it'd be great if you could take it for a test drive. We'll want to tweak one or two variables when you're ready.

I've improved the date and time setting from this morning, thanks to some input from @chDVRuser. It's a fuzzy logic type of thing, but I'm suggesting either 24hr hh:mm or mm/dd/yyyy hh:mm as sensible options. You can also do stuff like Next Friday at 16:00 apparently, but I didn't test for that.

I just deployed your docker-compose and setting a manual recording looks great. Thank for effort.

1 Like

OK so I stumbled around and managed to get this installed on my Mac mini. I manually created this config.yaml file using the example snippet above. I composed the docker and now it's running. I am able to access to dashboard and execute commands, but the log file shows errors for each. What am I missing, or what am I doing wrong?

SCR-20230924-tpzf

I re-deployed the stack trying localhost:8089 instead of 10.0.1.21:8089 for the CHANNELS_DVR env variable, since I have Portainer running on the same Mac mini as my Channels DVR server and that's the IP address. But unfortunately that didn't make any difference, all command attempts gave the same errors in the log.

Thanks for any help, this project feels very promising! I'm happy to experiment with adding any new scripts with your instruction once I've got it working properly.

You are using the wrong docker-compose. Use the one listed in post #29

As @cyoungers pointed out, you're not using the final docker-compose. In addition, there's an issue with the way you're doing your volume mapping:

screenshot-community.getchannels.com-2023.09.25-07_19_24

Directory bindings like this are done in the following fashion:
<path_on_docker_host>:<path_in_container>
You can change the part to the left on the colon, but not the part to the right. And, of course, you need the colon.

EDIT: Also, Linux timezones are a specific thing, and US/Western isn't one of them. :slight_smile:

screenshot-community.getchannels.com-2023.09.25-07_57_08

You'll want to use US/Pacific, or whatever, from this list:

1 Like

Thanks, that did the trick. I saw deployment created a bunch of new files alongside config.yaml, so that's when I knew I got the path_on_docker_host correct:

SCR-20230925-iclj

I then tested all of the Actions on my olivetin webUI and they worked! Note that I did have to rename manualrecord.sh to manualrecordings.sh for that Action to work, though.

Then I created the fixyoutubethumbnails.sh file and pasted in the ChatGPT translation in the code above, edited with one YouTube playlist video_group. But the next step, I think, is adding some parameters to identify it in config.yaml. What should I enter in there?

1 Like

OK, before I answer that, tell me more about the video_group change you made. Do you have multiple video_groups? Sounds like something we should prompt for in OliveTin, no? Are those groups from a list of fixed names (i.e. can we have a drop-down menu), or are these names made-up by the user where we would just have a text entry field?

If we setup OliveTin correctly, users shouldn't really need to edit scripts themselves, but rather enter needed variables in the webUI. Good catch on the mis-named script -- I've fixed that in the repo.

In order to have your manual recordings display correctly in DVR > Manage > Shows, you need to assign an Airing.SeriesID.
For manual recordings I recommended using the value manual/channel#.

{
    "Name": "Dabl manual recording",
    "Time": 1695664560,
    "Duration": 60,
    "Channels": ["6781"],
    "Airing": {
        "Source": "manual",
        "Channel": "6781",
        "Time": 1695664560,
        "Duration": 60,
        "Title": "Dabl manual recording with SeriesID",
        "Summary": "Dabl manual recording with SeriesID",
        "SeriesID": "manual/6781"
    }
}

{
    "Name": "Dabl manual recording",
    "Time": 1695664500,
    "Duration": 60,
    "Channels": ["6781"],
    "Airing": {
        "Source": "manual",
        "Channel": "6781",
        "Time": 1695664500,
        "Duration": 60,
        "Title": "Dabl manual recording without SeriesID",
        "Summary": "Dabl manual recording without SeriesID"
    }
}

Like this?

{
    "Name": "$name",
    "Time": $time,
    "Duration": $duration,
    "Channels": ["$channel"],
    "Airing": {
        "Source": "$source",
        "Channel": "$channel",
        "Time": $time,
        "Duration": $duration,
        "Title": "$name",
        "Summary": "$summary",
        "SeriesID": "$source/$channel",
        "Image": "$image"
    }
}