Skip to content

Instantly share code, notes, and snippets.

@Vigrond
Last active May 5, 2025 07:02

Revisions

  1. Vigrond revised this gist Jan 4, 2023. 1 changed file with 97 additions and 97 deletions.
    194 changes: 97 additions & 97 deletions jellyfinchromecast.md
    Original file line number Diff line number Diff line change
    @@ -81,107 +81,107 @@ This config will need to be updated:
    Copy the below config into `~/projects/jellyfin/nginx/nginx.conf`.

    ```
    # nginx config Borrowed from https://jellyfin.org/docs/general/networking/nginx/
    user nginx; ## Default: nobody
    worker_processes 1; ## Default: 1
    pid /tmp/nginx.pid;
    events {
    worker_connections 1024; ## Default: 1024
    }
    http {
    index index.html index.htm index.php;
    server {
    listen 80;
    listen [::]:80;
    server_name jellyfin.yourdomain.com;
    # Uncomment to redirect HTTP to HTTPS
    return 301 https://$host$request_uri;
    }
    server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name jellyfin.yourdomain.com;
    ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc.
    client_max_body_size 20M;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    add_header Strict-Transport-Security "max-age=31536000" always;
    ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    # Security / XSS Mitigation Headers
    # NOTE: X-Frame-Options may cause issues with the webOS app
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    # Content Security Policy
    # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    # Enforces https content and restricts JS/CSS to origin
    # External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
    # NOTE: The default CSP headers may cause issues with the webOS app
    # NOTE 2: cast_sender.js links may need to be updated. Check jellyfin console for load errors
    add_header Content-Security-Policy "default-src https: data: blob: http://image.tmdb.org; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/108/cast_sender.js https://www.gstatic.com/eureka/clank/107/cast_sender.js https://www.gstatic.com/eureka/clank/cast_sender.js https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";
    location = / {
    return 302 http://$host/web/;
    #return 302 https://$host/web/;
    }
    # nginx config Borrowed from https://jellyfin.org/docs/general/networking/nginx/
    location / {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    # Disable buffering when the nginx proxy gets very resource heavy upon streaming
    proxy_buffering off;
    }
    user nginx; ## Default: nobody
    worker_processes 1; ## Default: 1
    pid /tmp/nginx.pid;
    # location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/
    location = /web/ {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096/web/index.html;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    events {
    worker_connections 1024; ## Default: 1024
    }
    location /socket {
    # Proxy Jellyfin Websockets traffic
    proxy_pass http://jellyfin:8096;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    }
    }
    http {
    index index.html index.htm index.php;
    server {
    listen 80;
    listen [::]:80;
    server_name jellyfin.yourdomain.com;
    # Uncomment to redirect HTTP to HTTPS
    return 301 https://$host$request_uri;
    }
    server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name jellyfin.yourdomain.com;
    ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc.
    client_max_body_size 20M;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    add_header Strict-Transport-Security "max-age=31536000" always;
    ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    # Security / XSS Mitigation Headers
    # NOTE: X-Frame-Options may cause issues with the webOS app
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    # Content Security Policy
    # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    # Enforces https content and restricts JS/CSS to origin
    # External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
    # NOTE: The default CSP headers may cause issues with the webOS app
    # NOTE 2: cast_sender.js links may need to be updated. Check jellyfin console for load errors
    add_header Content-Security-Policy "default-src https: data: blob: http://image.tmdb.org; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/108/cast_sender.js https://www.gstatic.com/eureka/clank/107/cast_sender.js https://www.gstatic.com/eureka/clank/cast_sender.js https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";
    location = / {
    return 302 http://$host/web/;
    #return 302 https://$host/web/;
    }
    location / {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    # Disable buffering when the nginx proxy gets very resource heavy upon streaming
    proxy_buffering off;
    }
    # location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/
    location = /web/ {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096/web/index.html;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    }
    location /socket {
    # Proxy Jellyfin Websockets traffic
    proxy_pass http://jellyfin:8096;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    }
    }
    }
    }
    ```

    ### Create the docker-compose file
    @@ -506,6 +506,6 @@ ___

    ### `jellyfin` acts like it is playing, but the screen is black

    Check your supported bitrate. For example, my first generation Chromecast only supports bitrates up to 8k.
    Check your supported bitrate. For example, my first generation Chromecast only supports bitrates up to 8 Mbps.

    You can set the bitrate in user -> settings -> playback -> `Google Cast Streaming Quality`. Note: This seems to only change bitrate, not resolution as implied by the dropdown menu.
  2. Vigrond revised this gist Jan 4, 2023. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion jellyfinchromecast.md
    Original file line number Diff line number Diff line change
    @@ -234,7 +234,6 @@ services:
    # Optional - alternative address used for autodiscovery
    environment:
    - JELLYFIN_PublishedServerUrl=https://jellyfin.yourdomain.com
    ```

    ### Create the SSL Certificate using certbot
  3. Vigrond revised this gist Jan 4, 2023. 1 changed file with 512 additions and 1 deletion.
    513 changes: 512 additions & 1 deletion jellyfinchromecast.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,512 @@
    *Setting up Jellyfin with Chromecast
    # Setting up Jellyfin and Chromecast using Docker, Nginx, and dnsmasq

    1/03/2023

    + [Requirements](#requirements)
    + [Guide Specific Requirements](#guide-specific-requirements)
    + [Installing Docker](#installing-docker)
    + [Installing Jellyfin and Nginx](#installing-jellyfin-and-nginx)
    + [Set up our DNS server](#set-up-our-dns-server)
    + [Block Chromecast's hardcoded DNS server](#block-chromecasts-hardcoded-dns-server)
    + [Set your router's Primary DNS to your local DNS server](#set-your-routers-primary-dns-to-your-local-dns-server)
    + [Ensure Content-Security-Policy is correct](#ensure-content-security-policy-is-correct)
    + [Configure jellyfin networking settings](#configure-jellyfin-networking-settings)
    + [Troubleshooting](#troubleshooting)

    ## Introduction

    This will serve as a guide for setting up Jellyfin Web UI with Chromecast. This has proven to be a challenge because:

    * Chromecast requires that HTTP traffic be encrypted into HTTPS with a valid certificate.
    * Chromecast is hard coded with google DNS servers (8.8.8.8,8.8.4.4)
    * Basically, Chromecast assumes that you are a 3rd party website hosted on the public internet somewhere like Youtube. Which you are not, you are a local server hosting Jellyfin. This guide will make your Jellyfin server appear as such.

    The above points introduce some challenges such as:

    * Creating a valid signed HTTPS certificate for a public domain name
    * Making the above certificate work properly on a local network
    * Rerouting Google DNS addresses to our own internal DNS server
    * Creating a Content-Security-Policy so browsers do not block cross origin Chromecast js scripts

    ## Requirements:

    * A valid public domain name and access to its DNS records. This guide uses a domain registered on domains.google.com. **Creating a subdomain / CNAME in your DNS settings is NOT necessary.**
    * A router that allows static routing settings (most modern routers).
    * An internal DNS server on your local network. This guide uses an Ubuntu laptop as the DNS server, NGINX proxy server, and Jellyfin server.

    ## Guide Specific Requirements:

    This guide uses a specific setup that may or may not apply to your environment.

    * Ubuntu 22.04 for hosting Jellyfin, a DNS server, and an NGINX proxy
    * Jellyfin 10.8.8
    * Docker 20.10.17 and docker-compose 1.29.2 for containerization of jellyfin and NGINX
    * dnsmasq DNS server

    ## Installing Docker
    Follow instructions here https://docs.docker.com/desktop/install/ubuntu/ and ensure the test script works. Consider linking `docker-compose` (vs using `docker compose` without the dash) for accuracy of the guide.

    ## Installing Jellyfin and Nginx

    We will setup a docker compose file to handle Jellyfin and NGINX

    ### Create the necessary directories:

    ```
    mkdir -p ~/projects/jellyfin && cd ~/projects/jellyfin && mkdir -p cache config media nginx
    ```

    This will create the `jellyfin` folder in a projects folder in your home directory. It will also create the necessary docker `volume` folders for jellyfin and nginx.

    ### Create the Nginx Dockerfile

    ```
    cat <<"EOT" >> ~/projects/jellyfin/nginx/Dockerfile
    FROM nginx:latest
    RUN apt-get update
    RUN apt-get install -y certbot
    EOT
    ```

    This creates an Nginx container with certbot installed.

    ### Create the Nginx configuration file

    This config will need to be updated:

    * Replace mentions of `yourdomain.com` with your actual domain name.
    * Once jellyfin is running, update the `Content-Security-Policy` header with the appropriate `cast_sender.js` links. If `cast_sender.js` is not loading, it will be obvious in the console errors in the `jellyfin` web ui. (will remind again at the end of the guide). These `cast_sender.js` links may need to be updated as `jellyfin` is updated. (This will work without `add_header Content-Security-Policy`, but do you trust code running on your local network that much?)

    Copy the below config into `~/projects/jellyfin/nginx/nginx.conf`.

    ```
    # nginx config Borrowed from https://jellyfin.org/docs/general/networking/nginx/
    user nginx; ## Default: nobody
    worker_processes 1; ## Default: 1
    pid /tmp/nginx.pid;
    events {
    worker_connections 1024; ## Default: 1024
    }
    http {
    index index.html index.htm index.php;
    server {
    listen 80;
    listen [::]:80;
    server_name jellyfin.yourdomain.com;
    # Uncomment to redirect HTTP to HTTPS
    return 301 https://$host$request_uri;
    }
    server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name jellyfin.yourdomain.com;
    ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc.
    client_max_body_size 20M;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    add_header Strict-Transport-Security "max-age=31536000" always;
    ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    # Security / XSS Mitigation Headers
    # NOTE: X-Frame-Options may cause issues with the webOS app
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    # Content Security Policy
    # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    # Enforces https content and restricts JS/CSS to origin
    # External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
    # NOTE: The default CSP headers may cause issues with the webOS app
    # NOTE 2: cast_sender.js links may need to be updated. Check jellyfin console for load errors
    add_header Content-Security-Policy "default-src https: data: blob: http://image.tmdb.org; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/108/cast_sender.js https://www.gstatic.com/eureka/clank/107/cast_sender.js https://www.gstatic.com/eureka/clank/cast_sender.js https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";
    location = / {
    return 302 http://$host/web/;
    #return 302 https://$host/web/;
    }
    location / {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    # Disable buffering when the nginx proxy gets very resource heavy upon streaming
    proxy_buffering off;
    }
    # location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/
    location = /web/ {
    # Proxy main Jellyfin traffic
    proxy_pass http://jellyfin:8096/web/index.html;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    }
    location /socket {
    # Proxy Jellyfin Websockets traffic
    proxy_pass http://jellyfin:8096;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Protocol $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    }
    }
    }
    ```

    ### Create the docker-compose file

    This config utilizes hardware acceleration as described here https://jellyfin.org/docs/general/administration/hardware-acceleration#hardware-acceleration-on-docker-linux

    This will enable `jellyfin` to use a GPU to do encoding and decoding.

    In this example, I have an NVIDIA Quadro card I want to take advantage of, so I have installed the `nvidia container toolkit` documented here https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#getting-started

    **If you do not want to utilize hardware acceleration, remove the `deploy` tree in the config**

    Update the environment variable `JELLYFIN_PublishedServerUrl` in this configuration to match your domain name.

    Copy the below config into `~/projects/jellyfin/docker-compose.yml`.

    ```
    version: '3.5'
    services:
    web:
    build: nginx
    container_name: jellyfin_web
    restart: always
    ports:
    - "80:80"
    - "443:443"
    volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    - ./nginx/letsencrypt:/etc/letsencrypt
    - ./nginx/ssl:/etc/nginx/ssl
    logging:
    driver: "json-file"
    options:
    max-size: "500k"
    max-file: "10"
    jellyfin:
    image: jellyfin/jellyfin
    container_name: jellyfin
    volumes:
    - ./config:/config
    - ./cache:/cache
    - ./media:/media
    # Optional - Hardware acceleration
    deploy:
    resources:
    reservations:
    devices:
    - capabilities: [gpu]
    restart: 'unless-stopped'
    # Optional - alternative address used for autodiscovery
    environment:
    - JELLYFIN_PublishedServerUrl=https://jellyfin.yourdomain.com
    ```

    ### Create the SSL Certificate using certbot

    First we need to run our `docker-compose` file. Nginx will automatically fail because it will not be able to find the SSL certificate specified in our `nginx.conf`. But we don't need to worry about that right now.

    Go ahead and run `docker-compose up` while in the `~/projects/jellyfin` directory

    Once it loads, you'll probably see something like:

    ```
    user@group:~/projects/jellyfin$ docker-compose up
    Starting jellyfin_web ... done
    Starting jellyfin ... done
    Attaching to jellyfin_web, jellyfin
    web_1 | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
    web_1 | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
    web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
    web_1 | 10-listen-on-ipv6-by-default.sh: info: IPv6 listen already enabled
    web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
    web_1 | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
    web_1 | /docker-entrypoint.sh: Configuration complete; ready for start up
    web_1 | 2023/01/04 09:46:55 [emerg] 1#1: cannot load certificate...
    ...
    ```

    #### Let us spin up another `nginx container` and setup the SSL certificate.

    Open up a new terminal and navigate to our project directory

    `cd ~/projects/jellyfin`

    Let us start a new nginx container with a bash prompt presented to us:

    `docker-compose run web /bin/bash`

    Now with certbot already installed in our `Dockerfile`, we can simply run:

    `certbot certonly --manual -d *.yourdomain.com --agree-tos -m youremail@address.com --preferred-challenges=dns`

    * Replace `yourdomain.com` while preserving the `*.` wildcard in front of it.
    * Replace `youremail@address.com`with your own email

    This command will perform a DNS-01 challenge to validate you control your domain name:

    ```
    Performing the following challenges:
    dns-01 challenge for yourdomain.com
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please deploy a DNS TXT record under the name
    _acme-challenge.yourdomain.com with the following value:
    917nbBrGIYv0WbvM0URhPvcWFKh5wpryZQtXtJfti_8
    Before continuing, verify the record is deployed.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Press Enter to Continue
    ```

    #### Create a custom DNS record

    Now we need to login to our domain registrar and navigate to the DNS settings.

    We will set a custom record using the info `certbot` is giving us:

    ```
    Host name:
    _acme-challenge.yourdomain.com
    Type:
    TXT
    TTL:
    1 Hour
    Data:
    917nbBrGIYv0WbvM0URhPvcWFKh5wpryZQtXtJfti_8
    ```

    Every registrar is different and may require some fiddling. For example, with `domains.google.com`, we only want to put in `_acme-challenge` into the `Host name` field, as Google autofills the `.yourdomain.com` afterwards.

    Once this is done, press `Enter` to continue.

    If successful, `certbot` will place the created SSL certificates into `/etc/letsencrypt`.

    Type `exit` to shut down the container and return to our project directory.

    #### Verify server is running

    Type `docker ps` to verify that `jellyfin` and `jellyfin_web` containers are running and the status is healthy. You may also look at the output of `docker-compose up`.

    ## Set up our DNS server

    We will use `dnsmasq` for our DNS service.

    Keep in mind that if `libvirtd` is installed, it uses its own installation of `dnsmasq`. We will not be using this version, and install one that automatically sets up a service for us.

    `sudo apt-get install dnsmasq`

    You may receive and error message that the service failed to start on a specified address. This will be fixed in the next step.

    ### Configuring dnsmasq

    Open the `dnsmasq` configuration file:

    `sudo gedit /etc/dnsmasq.conf`

    We are presented with a well-commented configuration file. Luckily, we only need to worry about a few settings:

    Ensure no-resolv is uncommented:

    ```
    # If you don't want dnsmasq to read /etc/resolv.conf or any other
    # file, getting its servers from this file instead (see below), then
    # uncomment this.
    no-resolv
    ```

    Ensure DNS forwarding addresses are set. This example uses Quad9's DNS service:

    ```
    # Add other name servers here, with domain specs if they are for
    # non-public domains.
    server=9.9.9.9
    server=149.112.112.112
    ```

    Set our `jellyfin` redirect address where `192.168.0.2` is replaced your jellyfin machine's local IP address. Static/Manual IP settings on your network configuration is highly recommended:

    ```
    # Add domains which you want to force to an IP address here.
    # The example below send any host in double-click.net to a local
    # web-server.
    address=/jellyfin.yourdomain.com/192.168.0.2
    ```

    Set the ethernet interface. You can find yours by using `ip -br -f inet address` and looking for the name of the device that lists the correct IP.

    (alternatively, you may set the `listen-address`)

    Whatever IP is assigned to the device (or the `listen-address`) will be the local DNS server address.

    ```
    # If you want dnsmasq to listen for DHCP and DNS requests only on
    # specified interfaces (and the loopback) give the name of the
    # interface (eg eth0) here.
    # Repeat the line for more than one interface.
    interface=eth0
    ```

    Ensure `bind-interfaces` is uncommented:

    ```
    # On systems which support it, dnsmasq binds the wildcard address,
    # even when it is listening on only some interfaces. It then discards
    # requests that it shouldn't reply to. This has the advantage of
    # working even when interfaces come and go and change address. If you
    # want dnsmasq to really bind only the interfaces it is listening on,
    # uncomment this option. About the only time you may need this is when
    # running another nameserver on the same machine.
    bind-interfaces
    ```

    Ensure you save the file after done editing.

    ### Restart the dnsmasq service

    `service dnsmasq restart`

    Ensure it is active and running

    `service dnsmasq status`

    ## Block Chromecast's hardcoded DNS server

    To block Chromecast's access to `8.8.8.8` and instead use your own DNS server you need to login to your router admin panel and find the `Routing` settings.

    For a `TP-Link AX1800` router, the route entry would look as follows:

    ```
    Network Destination:
    8.8.8.8
    Subnet Mask:
    255.255.255.255
    Default Gateway (your router's address)
    192.168.0.1
    Interface
    LAN
    Description
    Google DNS 1
    ```

    Any device trying to access `8.8.8.8` is now redirected to your router.


    ## Set your router's Primary DNS to your local DNS server

    Now that `8.8.8.8` is blocked, set the router's `Primary DNS server` to your `local DNS server address`.

    Now would be a good time to restart the Chromecast if it has been on.

    ## Ensure Content-Security-Policy is correct

    Start up your Chrome browser and ensure the dev tools are open. ( right click -> inspect )

    In the `Network` tab ensure `Disable Cache` is enabled.

    Switch to the `Console` tab and then enter in `jellyfin.yourserver.com`

    This should automatically load `https` and display a secure connection (lock icon next to the address bar)

    Once logged into your jellyfin dashboard, ensure the console log does not display any errors that look like:

    ```
    Refused to load the script 'https://www.gstatic.com/eureka/clank/108/cast_sender.js' because it violates the following Content Security Policy directive...
    ```

    If you do, you need to add the URL in the error to the `add_header Content-Security-Policy` line in your `nginx.conf`.

    As mentioned before, this will work without `add_header Content-Security-Policy`, but do you trust the code running on your local server that much?

    ## Configure jellyfin networking settings

    Access the Networking settings by going to Dashboard - > Networking

    Set `LAN Networks` to your local area network range with slash notation. Example: `192.168.0.0/24`

    Set `Known Proxies` to your NGINX Proxy server name, `jellyfin.yourserver.com`

    **Do not change any HTTPS settings, it is not necessary with the nginx proxy**

    ## Troubleshooting

    ### `jellyfin.yourserver.com` isn't loading

    Ensure `docker-compose up` has been ran in your project folder

    Check docker logs `docker logs jellyfin`, `docker logs jellyfin_web` for any errors

    Ensure your device's network settings is set to use your local DNS address. You can check using nmap: `nmap jellyfin.yourserver.com` and it should point to the correct local IP address.

    Ensure the DNS server is running. `service dnsmasq status`

    Ensure `jellyfin`'s networking HTTPS settings are disabled

    ___

    ### `Google Cast` isn't showing up in the cast list

    Ensure you're running the site in Google Chrome or Chromium browsers

    Ensure there are no errors loading `cast_sender.js` in the browser console logs

    Ensure your Chromecast is plugged in and powered on.

    ___

    ### I get an error saying Chromecast is unable to communicate with my `jellyfin` server

    Your Chromecast likely still has access to the public `8.8.8.8` DNS server. Double check your router settings.

    `nmap 8.8.8.8` should fail

    ___

    ### `jellyfin` acts like it is playing, but the screen is black

    Check your supported bitrate. For example, my first generation Chromecast only supports bitrates up to 8k.

    You can set the bitrate in user -> settings -> playback -> `Google Cast Streaming Quality`. Note: This seems to only change bitrate, not resolution as implied by the dropdown menu.
  4. Vigrond revised this gist Jan 4, 2023. 2 changed files with 1 addition and 3 deletions.
    3 changes: 0 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +0,0 @@
    test

    `test`
    1 change: 1 addition & 0 deletions jellyfinchromecast.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    *Setting up Jellyfin with Chromecast
  5. Vigrond renamed this gist Jan 4, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  6. Vigrond created this gist Jan 4, 2023.
    3 changes: 3 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    test

    `test`