Kodi Media Sensors - need Python help with an upgrade idea!

Hi there.
I am trying to roll everything in my home into one app to rule them all. Naturally I’m using Home Assistant for this, but I hit a snag when working on my Kodi integration…

I recently installed the (excellent) Kodi Media Sensors add-on via HACS.

Rather than use the Upcoming Media Card I’ve opted to roll my own for a bit more control.

I’ve got it looking nice with my minimalist mushroom UI, and have been able to tweak a few lines of code so the sensor retrieves file names that I can pass to script to play the specific episodes/movies directly from HA.

There is just one thing that bugs me…

The Recently Added Episodes sensor returns a pretty basic list of, well… recently added episodes…

What I actually want is a list of the recently added episodes, compressed into the last unwatched episode of each series.

Now… after a bit of Google-fu I was able to locate this script which I was able to modify to output exactly what I want:

#!/usr/bin/env python
import requests
import json
import time, datetime
from datetime import date
import sys

kodi_ip = 'YOUR KODI IP HERE'
kodi_port = '8080'
kodi_url = 'http://' + kodi_ip + ':' + kodi_port + '/jsonrpc'

#setup the JSON request parameters to use for the next request to Kodi
#we poll the video library for the list of TV shows, sorted by last added
kodi_params = json.dumps({"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","params":{"properties":["playcount"],"limits":{"end":50,"start":0},"sort":{"ignorearticle":True,"method":"dateadded","order":"descending","useartistsortname":True}},"id":28})
kodi_response = requests.post(kodi_url, data=kodi_params)

#we format the JSON response so it can be read
json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)
json_object  = json.loads(json_data)

#Below are some examples of how to get data back out of the json_object, limits are the number of inprogress shows.
#print(json_object['result']['limits']['start'])
#print(json_object['result']['limits']['end'])
#print(json_object['result']['tvshows'][0])
#json_object = json.loads(json.dumps(json_object['result'], indent=4, sort_keys=True))
#print(kodi_data.get('tvshows'))
#print(json_object['tvshows'])

#next we begin a loop to build the query which will poll the episodes of the latest shows to get the first 6 that have unwatched episodes
#we need to empty the kodi_params as we will use this to store the multi show query, we put the JSON request for each tvshowid on a line and braket it [ ] once #complete. For each inprogress show Kodi returns - ["season", "episode", "playcount", "firstaired", "tvshowid", "lastplayed"]
kodi_params = ''
showcount = 0
for k in json_object['result']['tvshows']:
    #print (k['tvshowid'])
    #print (k['label'])
#Only handle unwatched or in progress shows
    if k['playcount'] < 1:
        showcount = showcount + 1
        if kodi_params == '':
            kodi_params = ('{"jsonrpc":"2.0","id":1,"method":"VideoLibrary.GetEpisodes","params":{"tvshowid": ' + str(k['tvshowid']) + ', "properties": ["playcount"],"sort":{"ignorearticle":true,"method":"episode","order":"ascending","useartistsortname":true}}}')
        else:
            kodi_params = kodi_params + ","+ "\n" + ('{"jsonrpc":"2.0","id":1,"method":"VideoLibrary.GetEpisodes","params":{"tvshowid": ' + str(k['tvshowid']) + ', "properties": ["playcount"],"sort":{"ignorearticle":true,"method":"episode","order":"ascending","useartistsortname":true}}}')
#once we have found 6 items we can stop
    if showcount >= 6: break

kodi_params = "[" + kodi_params + "]"
#print(kodi_params)
kodi_response = requests.post(kodi_url, data=kodi_params)
json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)
#print(json_data)
json_object  = json.loads(json_data)

#Kodi has now returned all the information for the episodes of the inprogress tvshows which we will need to process to determine the first unwatched episode.
kodi_params = ''

#we loop through the results for each show.
idcount = 0;
for k in json_object:
#we then loop through the episodes in each show, and take the first unwatched episode
    for x in k['result']['episodes']:
        if x['playcount'] < 1:
#then if the particular episode is the next unwatched for a show it will start writing the multiple "GetEpisodeDetails" 
            if kodi_params == '':
                kodi_params = ('{"jsonrpc":"2.0","id":' + str(idcount) + ',"method":"VideoLibrary.GetEpisodeDetails","params":{"episodeid":' + str(x['episodeid']) + ', "properties": ["title","firstaired","playcount","runtime","season","episode","showtitle","file","dateadded","art","fanart","rating"]}}')
            else:
                kodi_params = kodi_params + ","+ "\n" + ('{"jsonrpc":"2.0","id":' + str(idcount) + ',"method":"VideoLibrary.GetEpisodeDetails","params":{"episodeid":' + str(x['episodeid']) + ', "properties": ["title","firstaired","playcount", "runtime","season","episode","showtitle","file","dateadded","art","fanart","rating"]}}')
#                print(str(x['firstaired']) + ' ' + str(x['tvshowid'])+ ' ' + str(x['episodeid']))
#once we have found the first unwatched episode for a show we can break the loop and move onto the next show.
            idcount = idcount + 1
            break


#once the loop is complete we write the multiple requests and bound with [] and send to kodi
kodi_params = "[" + kodi_params + "]"
#print(kodi_params)

#kodi_response = requests.post(kodi_url, headers=kodi_header, data=kodi_params)
kodi_response = requests.post(kodi_url, data=kodi_params)
json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)
print(json_data)

The above script spits out a JSON list of the first unwatched episodes from any recently added TV shows - perfect!

This, unfortunately, is where I hit the limits of my technical expertise. I need help integrating the above script into the entitiies.py file of the Kodi Sensors add-on. Any Python pros able to help with this?

Thanks in advance for your assistance!
:smiley:

After a lot of fiddling around, I was able to get this up and running using PyScript, which is a great add-on for running Python. :smiley:

Much better…

@dt1000 Please could you share how you did it? I would like to use your script but I also don’t know how to do it with pyscript and how to also create the card in the photo? Thanks a lot

Sure. I have Pyscipt installed, and wrote the following script, kodi_tv.py, based on one I found online …

#!/usr/bin/env python
import requests
import json
import time, datetime
from datetime import date
from urllib import parse
import sys

kodi_ip = '192.168.0.200'
kodi_port = '8080'
kodi_url = 'http://' + kodi_ip + ':' + kodi_port + '/jsonrpc'

protocol = 'http'
auth = ''
base_web_url = (
            f'{protocol}://{auth}{kodi_ip}:{kodi_port}/image/image%3A%2F%2F'
        )

state.persist('pyscript.kodi_tv')
state.setattr('pyscript.kodi_tv.friendly_name', 'Kodi - Latest Episodes')
state.setattr('pyscript.kodi_tv.icon', 'mdi:language-python')

@pyscript_compile
def getKodi(get_params):
    try:
        get_response = requests.post(kodi_url, data=get_params)
        return get_response
    except Exception as exc:
        return None, exc
        
def get_web_url(path: str) -> str:
    """Get the web URL for the provided path.
    This is used for fanart/poster images that are not a http url.  For
    example the path is local to the kodi installation or a path to
    an NFS share.
    :param path: The local/nfs/samba/etc. path.
    :returns: The web url to access the image over http.
    """
    if path.lower().startswith("http"):
        return path
    # This looks strange, but the path needs to be quoted twice in order
    # to work.
    quoted_path = parse.quote(parse.quote(path, safe=""))
    return base_web_url + quoted_path


@service
def kodi_tv_update(action=None, id=None):
    state.set('pyscript.kodi_tv', 'Updating')
    #setup the JSON request parameters to use for the next request to Kodi
    #we poll the video library for the list of TV shows, sorted by last added
    kodi_params = json.dumps({"jsonrpc":"2.0","method":"VideoLibrary.GetTVShows","params":{"properties":["playcount"],"limits":{"end":50,"start":0},"sort":{"ignorearticle":True,"method":"dateadded","order":"descending","useartistsortname":True}},"id":28})
    #kodi_response = requests.post(kodi_url, data=kodi_params)
    kodi_response = task.executor(getKodi,kodi_params)

    #we format the JSON response so it can be read
    json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)
    json_object  = json.loads(json_data)

    #Below are some examples of how to get data back out of the json_object, limits are the number of inprogress shows.
    #print(json_object['result']['limits']['start'])
    #print(json_object['result']['limits']['end'])
    #print(json_object['result']['tvshows'][0])
    #json_object = json.loads(json.dumps(json_object['result'], indent=4, sort_keys=True))
    #print(kodi_data.get('tvshows'))
    #print(json_object['tvshows'])

    #next we begin a loop to build the query which will poll the episodes of the latest shows to get the first 6 that have unwatched episodes
    #we need to empty the kodi_params as we will use this to store the multi show query, we put the JSON request for each tvshowid on a line and braket it [ ] once #complete. For each inprogress show Kodi returns - ["season", "episode", "playcount", "firstaired", "tvshowid", "lastplayed"]
    kodi_params = ''
    showcount = 0
    for k in json_object['result']['tvshows']:
        #print (k['tvshowid'])
        #print (k['label'])
    #Only handle unwatched or in progress shows
        if k['playcount'] < 1:
            showcount = showcount + 1
            if kodi_params == '':
                kodi_params = ('{"jsonrpc":"2.0","id":1,"method":"VideoLibrary.GetEpisodes","params":{"tvshowid": ' + str(k['tvshowid']) + ', "properties": ["playcount"],"sort":{"ignorearticle":true,"method":"episode","order":"ascending","useartistsortname":true}}}')
            else:
                kodi_params = kodi_params + ","+ "\n" + ('{"jsonrpc":"2.0","id":1,"method":"VideoLibrary.GetEpisodes","params":{"tvshowid": ' + str(k['tvshowid']) + ', "properties": ["playcount"],"sort":{"ignorearticle":true,"method":"episode","order":"ascending","useartistsortname":true}}}')
    #once we have found 6 items we can stop
        if showcount >= 6: break

    kodi_params = "[" + kodi_params + "]"
    #print(kodi_params)
    #kodi_response = requests.post(kodi_url, data=kodi_params)
    kodi_response = task.executor(getKodi,kodi_params)
    json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)
    #print(json_data)
    json_object  = json.loads(json_data)

    #Kodi has now returned all the information for the episodes of the inprogress tvshows which we will need to process to determine the first unwatched episode.
    kodi_params = ''

    #we loop through the results for each show.
    idcount = 0;
    for k in json_object:
    #we then loop through the episodes in each show, and take the first unwatched episode
        for x in k['result']['episodes']:
            if x['playcount'] < 1:
    #then if the particular episode is the next unwatched for a show it will start writing the multiple "GetEpisodeDetails" 
                if kodi_params == '':
                    kodi_params = ('{"jsonrpc":"2.0","id":' + str(idcount) + ',"method":"VideoLibrary.GetEpisodeDetails","params":{"episodeid":' + str(x['episodeid']) + ', "properties": ["title","firstaired","playcount","runtime","season","episode","showtitle","file","dateadded","art","rating"]}}')
                else:
                    kodi_params = kodi_params + ","+ "\n" + ('{"jsonrpc":"2.0","id":' + str(idcount) + ',"method":"VideoLibrary.GetEpisodeDetails","params":{"episodeid":' + str(x['episodeid']) + ', "properties": ["title","firstaired","playcount", "runtime","season","episode","showtitle","file","dateadded","art","rating"]}}')
    #                print(str(x['firstaired']) + ' ' + str(x['tvshowid'])+ ' ' + str(x['episodeid']))
    #once we have found the first unwatched episode for a show we can break the loop and move onto the next show.
                idcount = idcount + 1
                break


    #once the loop is complete we write the multiple requests and bound with [] and send to kodi
    kodi_params = "[" + kodi_params + "]"
    #print(kodi_params)

    #kodi_response = requests.post(kodi_url, data=kodi_params)
    kodi_response = task.executor(getKodi,kodi_params)
    json_data = json.dumps(kodi_response.json(), indent=4, sort_keys=True)

    #state.set('pyscript.kodi_tv', json.loads(json_data))
    #log.info('Wibble')
    #state.set('pyscript.kodi_tv', json.loads(json_data)[0]['result']['episodedetails']['title'])

    jason = json.loads(json_data)

    i=0
    while i<6:
        if i < len(jason):
            episodeNum = jason[i]['result']['episodedetails']['episode']
            seriesNum = jason[i]['result']['episodedetails']['season']
            posterRaw = jason[i]['result']['episodedetails']['art']['tvshow.poster']
            posterRaw = parse.unquote(posterRaw)[8:].strip("/")
            seriesEpisode = 'S' + str(seriesNum).zfill(2) + 'E' + str(episodeNum).zfill(2)
            posterURL = get_web_url(
                posterRaw
            )
            state.setattr('pyscript.kodi_tv.showtitle_'+ str(i), jason[i]['result']['episodedetails']['showtitle'])
            state.setattr('pyscript.kodi_tv.episode_'+ str(i), seriesEpisode)
            state.setattr('pyscript.kodi_tv.poster_'+ str(i), posterURL)
            state.setattr('pyscript.kodi_tv.file_'+ str(i), jason[i]['result']['episodedetails']['file'])
        else:
            state.setattr('pyscript.kodi_tv.showtitle_'+ str(i), '')
            state.setattr('pyscript.kodi_tv.episode_'+ str(i), '')
            state.setattr('pyscript.kodi_tv.poster_'+ str(i), '')
            state.setattr('pyscript.kodi_tv.file_'+ str(i), '')
        i += 1

    state.set('pyscript.kodi_tv', len(jason))

    #print(json_data)
    #log.info(json_data)

I then have an HA script that runs the kodi_tv_update service, which is set to fire every afternoon to update the info…

alias: Kodi TV
sequence:
  - service: pyscript.kodi_tv_update
    data: {}
icon: mdi:language-python
mode: single

The data appears in a sensor like so…

To get the interface, I am using layout-card, card_mod and Mushroom UI.

type: custom:layout-card
layout_type: custom:horizontal-layout
cards:
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 0
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                margin-bottom: 0px !important;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_0')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 0
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                margin-top: 0px !important;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_0'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_0'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 0
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 1
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_1')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 1
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_1'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_1'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 1
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 2
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_2')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 2
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_2'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_2'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 2
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 3
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_3')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 3
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_3'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_3'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 3
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 4
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_4')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 4
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_4'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_4'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 4
  - type: conditional
    conditions:
      - condition: numeric_state
        entity: pyscript.kodi_tv
        above: 5
    card:
      type: custom:stack-in-card
      card_mod:
        style: |
          ha-card {
            margin-top: 4px !important;
            margin-bottom: 8px !important;
            margin-left: 4px !important;
            margin-right: 4px !important;
          }
      cards:
        - type: picture
          image: local/images/blank.png
          card_mod:
            style: |
              ha-card {          
                aspect-ratio: 2 / 2.85;
                --keep-background: true;
                background-image: url("{{ state_attr('pyscript.kodi_tv', 'poster_5')|string }}");
                background-position: center;
                background-repeat: no-repeat; /* Do not repeat the image */
                background-size: cover; /* Resize the background image to cover the entire container */
              }
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 5
        - type: custom:mushroom-template-card
          card_mod:
            style: |
              ha-card {          
                background: --card-background-color !important;
                --keep-background: true;
                top: -4px !important;
                left: 4px;
              }
          primary: '{{ state_attr(''pyscript.kodi_tv'', ''showtitle_5'')|string }}'
          secondary: '{{ state_attr(''pyscript.kodi_tv'', ''episode_5'')|string }}'
          icon: null
          icon_color: teal
          badge_color: ''
          multiline_secondary: false
          fill_container: true
          tap_action:
            action: fire-dom-event
            browser_mod:
              service: script.kodi_play_tv_file
              data:
                browser_id: THIS
                index: 5
layout:
  max_cols: 6
  width: 120
  max_width: 240
  card_margin: 0
  margin: 0

And finally, to make the buttons play the TV shows when you tap them, I wrote an HA script like this…

alias: Kodi Play TV File
sequence:
  - service: kodi.call_method
    metadata: {}
    data:
      method: Player.Open
      item:
        file: "{{ state_attr('pyscript.kodi_tv', ('file_' + index|string) )|string }}"
    target:
      entity_id: media_player.192_168_0_200
mode: single
icon: mdi:kodi

So, yeah… it’s a bit fiddly… but it works rather well. I also had to modify a few of the other elements to get the same functionality for movies, but that was a bit easier, as I didn’t have to roll my own sensor!

Oh… and don’t forget to set the IP in the scripts to that of your own KODI server!

Good luck! :smiling_face_with_three_hearts:

1 Like

@dt1000 thanks a lot!!! I was able to get your script working by making some changes as my kodi had authentication :slight_smile:
However the card is missing a blank.png file if you could share that too that would be awesome! Also could you also share the modified code for movies? Thanks so much again

Looking good!
That missing file is just a small, total transparent PNG. You can make one here!

yes done and everything is perfect thanks :slight_smile: but I can’t figure out what to change to do the same with films? Would you be so kind as to help me with that too? Thank you very much :slight_smile: