diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..990c8acd4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,63 @@ +#### +# Go +#### + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Packr2 artifacts +**/*-packr.go + +# GraphQL generated output +pkg/models/generated_*.go +ui/v2.5/src/core/generated-*.tsx + +# packr generated files +*-packr.go + +#### +# Jetbrains +#### + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +#### +# Random +#### + +ui/v2.5/node_modules +ui/v2.5/build + +*.db + +stash +dist + +docker \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4f92a344f..822b8ce50 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,6 @@ # GraphQL generated output pkg/models/generated_*.go -ui/v2/src/core/generated-*.tsx ui/v2.5/src/core/generated-*.tsx # packr generated files @@ -49,6 +48,9 @@ ui/v2.5/src/core/generated-*.tsx .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml +# Goland Junk +pkg/pkg + #### # Random #### @@ -59,3 +61,4 @@ node_modules stash dist +.DS_Store diff --git a/.travis.yml b/.travis.yml index ca5eecc95..c94da2c44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ env: before_install: - echo -e "machine github.com\n login $CI_USER_TOKEN" > ~/.netrc - nvm install 12 -- travis_retry yarn --cwd ui/v2.5 install --frozen-lockfile +- travis_retry make pre-ui - make generate -- CI=false yarn --cwd ui/v2.5 build-ci +- CI=false make ui-validate ui-only #- go get -v github.com/mgechev/revive script: # left lint off to avoid getting extra dependency @@ -41,10 +41,12 @@ deploy: api_key: secure: tGJ2q62CfPdayid2qEtW2aGRhMgCl3lBXYYQqp3eH0vFgIIf6cs7IDX7YC/x3XKMEQ/iMLZmtCXZvSTqNrD6Sk7MSnt30GIs+4uxIZDnnd8mV5X3K4n4gjD+NAORc4DrQBvUGrYMKJsR5gtkH0nu6diWb1o1If7OiJEuCPRhrmQYcza7NUdABnA9Z2wn2RNUV9Ga33WUCqLMEU5GtNBlfQPiP/khCQrqn/ocR6wUjYut3J6YagzqH4wsfJi3glHyWtowcNIw1LZi5zFxHD/bRBT4Tln7yypkjWNq9eQILA6i6kRUGf7ggyTx26/k8n4tnu+QD0vVh4EcjlThpU/LGyUXzKrrxjRwaDZnM0oYxg5AfHcBuAiAdo0eWnV3lEWRfTJMIVb9MPf4qDmzR4RREfB5OXOxwq3ODeCcJE8sTIMD/wBPZrlqS/QrRpND2gn2X4snkVukN9t9F4CMTFMtVSzFV7TDJW5E5Lq6VEExulteQhs6kcK9NRPNAaLgRQAw7X9kVWfDtiGUP+fE2i8F9Bo8bm7sOT5O5VPMPykx3EgeNg1IqIgMTCsMlhMJT4xBJoQUgmd2wWyf3Ryw+P+sFgdb5Sd7+lFgJBjMUUoOxMxAOiEgdFvCXcr+/Udyz2RdtetU1/6VzXzLPcKOw0wubZeBkISqu7o9gpfdMP9Eq00= file: - - dist/stash-osx - - dist/stash-win.exe - - dist/stash-linux - - dist/stash-pi + - dist/stash-osx + - dist/stash-win.exe + - dist/stash-linux + - dist/stash-linux-arm64v8 + - dist/stash-linux-arm32v7 + - dist/stash-pi skip_cleanup: true overwrite: true name: "${STASH_VERSION}: Latest development build" @@ -53,20 +55,29 @@ deploy: on: repo: stashapp/stash branch: develop + # docker image build for develop release + - provider: script + skip_cleanup: true + script: bash ./docker/ci/x86_64/docker_push.sh development-x86_64 + on: + repo: stashapp/stash + branch: develop # official master release - only build when tagged - provider: releases api_key: secure: tGJ2q62CfPdayid2qEtW2aGRhMgCl3lBXYYQqp3eH0vFgIIf6cs7IDX7YC/x3XKMEQ/iMLZmtCXZvSTqNrD6Sk7MSnt30GIs+4uxIZDnnd8mV5X3K4n4gjD+NAORc4DrQBvUGrYMKJsR5gtkH0nu6diWb1o1If7OiJEuCPRhrmQYcza7NUdABnA9Z2wn2RNUV9Ga33WUCqLMEU5GtNBlfQPiP/khCQrqn/ocR6wUjYut3J6YagzqH4wsfJi3glHyWtowcNIw1LZi5zFxHD/bRBT4Tln7yypkjWNq9eQILA6i6kRUGf7ggyTx26/k8n4tnu+QD0vVh4EcjlThpU/LGyUXzKrrxjRwaDZnM0oYxg5AfHcBuAiAdo0eWnV3lEWRfTJMIVb9MPf4qDmzR4RREfB5OXOxwq3ODeCcJE8sTIMD/wBPZrlqS/QrRpND2gn2X4snkVukN9t9F4CMTFMtVSzFV7TDJW5E5Lq6VEExulteQhs6kcK9NRPNAaLgRQAw7X9kVWfDtiGUP+fE2i8F9Bo8bm7sOT5O5VPMPykx3EgeNg1IqIgMTCsMlhMJT4xBJoQUgmd2wWyf3Ryw+P+sFgdb5Sd7+lFgJBjMUUoOxMxAOiEgdFvCXcr+/Udyz2RdtetU1/6VzXzLPcKOw0wubZeBkISqu7o9gpfdMP9Eq00= file: - - dist/stash-osx - - dist/stash-win.exe - - dist/stash-linux - - dist/stash-pi + - dist/stash-osx + - dist/stash-win.exe + - dist/stash-linux + - dist/stash-linux-arm64v8 + - dist/stash-linux-arm32v7 + - dist/stash-pi # make the release a draft so the maintainers can confirm before releasing draft: true skip_cleanup: true overwrite: true - # don't write the body. To be done manually for now. In future we might + # don't write the body. To be done manually for now. In future we might # want to generate the changelog or get it from a file name: ${STASH_VERSION} on: @@ -74,3 +85,12 @@ deploy: tags: true # make sure we don't release using the latest_develop tag condition: $TRAVIS_TAG != latest_develop + # docker image build for master release + - provider: script + skip_cleanup: true + script: bash ./docker/ci/x86_64/docker_push.sh latest + on: + repo: stashapp/stash + tags: true + # make sure we don't release using the latest_develop tag + condition: $TRAVIS_TAG != latest_develop diff --git a/Makefile b/Makefile index 659a80120..c3e46b17f 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,52 @@ -ifeq ($(OS),Windows_NT) - SEPARATOR := && - SET := set +IS_WIN = +ifeq (${SHELL}, sh.exe) + IS_WIN = true +endif +ifeq (${SHELL}, cmd) + IS_WIN = true endif -release: generate ui build +ifdef IS_WIN + SEPARATOR := && + SET := set +else + SEPARATOR := ; + SET := export +endif -build: - $(eval DATE := $(shell go run scripts/getDate.go)) +# set LDFLAGS environment variable to any extra ldflags required +# set OUTPUT to generate a specific binary name + +LDFLAGS := $(LDFLAGS) +ifdef OUTPUT + OUTPUT := -o $(OUTPUT) +endif + +.PHONY: release pre-build install clean + +release: generate ui build-release + +pre-build: +ifndef BUILD_DATE + $(eval BUILD_DATE := $(shell go run -mod=vendor scripts/getDate.go)) +endif + +ifndef GITHASH $(eval GITHASH := $(shell git rev-parse --short HEAD)) +endif + +ifndef STASH_VERSION $(eval STASH_VERSION := $(shell git describe --tags --exclude latest_develop)) - $(SET) CGO_ENABLED=1 $(SEPARATOR) go build -mod=vendor -v -ldflags "-X 'github.com/stashapp/stash/pkg/api.version=$(STASH_VERSION)' -X 'github.com/stashapp/stash/pkg/api.buildstamp=$(DATE)' -X 'github.com/stashapp/stash/pkg/api.githash=$(GITHASH)'" +endif + +build: pre-build + $(eval LDFLAGS := $(LDFLAGS) -X 'github.com/stashapp/stash/pkg/api.version=$(STASH_VERSION)' -X 'github.com/stashapp/stash/pkg/api.buildstamp=$(BUILD_DATE)' -X 'github.com/stashapp/stash/pkg/api.githash=$(GITHASH)') + $(SET) CGO_ENABLED=1 $(SEPARATOR) go build $(OUTPUT) -mod=vendor -v -ldflags "$(LDFLAGS) $(EXTRA_LDFLAGS)" + +# strips debug symbols from the release build +# consider -trimpath in go build if we move to go 1.13+ +build-release: EXTRA_LDFLAGS := -s -w +build-release: build install: packr2 install @@ -58,11 +95,25 @@ it: pre-ui: cd ui/v2.5 && yarn install --frozen-lockfile -.PHONY: ui -ui: +.PHONY: ui-only +ui-only: pre-build + $(SET) REACT_APP_DATE="$(BUILD_DATE)" $(SEPARATOR) \ + $(SET) REACT_APP_GITHASH=$(GITHASH) $(SEPARATOR) \ + $(SET) REACT_APP_STASH_VERSION=$(STASH_VERSION) $(SEPARATOR) \ cd ui/v2.5 && yarn build + +.PHONY: ui +ui: ui-only packr2 +.PHONY: ui-start +ui-start: pre-build + $(SET) REACT_APP_DATE="$(BUILD_DATE)" $(SEPARATOR) \ + $(SET) REACT_APP_GITHASH=$(GITHASH) $(SEPARATOR) \ + $(SET) REACT_APP_STASH_VERSION=$(STASH_VERSION) $(SEPARATOR) \ + cd ui/v2.5 && yarn start + +.PHONY: fmt-ui fmt-ui: cd ui/v2.5 && yarn format diff --git a/README.md b/README.md index b00c8c456..b0f3a995e 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,14 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/stashapp/stash)](https://goreportcard.com/report/github.com/stashapp/stash) [![Discord](https://img.shields.io/discord/559159668438728723.svg?logo=discord)](https://discord.gg/2TsNFKt) +https://stashapp.cc + **Stash is a Go app which organizes and serves your porn.** See a demo [here](https://vimeo.com/275537038) (password is stashapp). +An in-app manual is available, and the manual pages can be viewed [here](https://github.com/stashapp/stash/tree/develop/ui/v2.5/src/docs/en). + # Docker install Follow [this README.md in the docker directory.](docker/production/README.md) diff --git a/docker/build/x86_64/Dockerfile b/docker/build/x86_64/Dockerfile index 8ac117647..54fed78f9 100644 --- a/docker/build/x86_64/Dockerfile +++ b/docker/build/x86_64/Dockerfile @@ -6,8 +6,12 @@ FROM golang:1.11.13 as compiler RUN apt-get update && apt-get install -y apt-transport-https RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + +# prevent caching of the key +ADD https://dl.yarnpkg.com/debian/pubkey.gpg yarn.gpg +RUN cat yarn.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ + rm yarn.gpg RUN apt-get update && \ apt-get install -y nodejs yarn xz-utils --no-install-recommends || exit 1; \ diff --git a/docker/ci/x86_64/Dockerfile b/docker/ci/x86_64/Dockerfile new file mode 100644 index 000000000..54d1bec4f --- /dev/null +++ b/docker/ci/x86_64/Dockerfile @@ -0,0 +1,25 @@ + +# must be built from /dist directory + +FROM ubuntu:18.04 as prep +LABEL MAINTAINER="https://discord.gg/Uz29ny" + +RUN apt-get update && \ + apt-get -y install curl xz-utils && \ + apt-get autoclean -y && \ + rm -rf /var/lib/apt/lists/* +WORKDIR / +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +RUN curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ + tar xf /ffmpeg.tar.xz && \ + rm ffmpeg.tar.xz && \ + mv /ffmpeg*/ /ffmpeg/ + +FROM ubuntu:18.04 as app +RUN apt-get update && apt-get -y install ca-certificates +COPY --from=prep /ffmpeg/ffmpeg /ffmpeg/ffprobe /usr/bin/ +COPY /stash-linux /usr/bin/stash + +EXPOSE 9999 +CMD ["stash"] diff --git a/docker/ci/x86_64/README.md b/docker/ci/x86_64/README.md new file mode 100644 index 000000000..e6dc0abcf --- /dev/null +++ b/docker/ci/x86_64/README.md @@ -0,0 +1 @@ +This dockerfile is used by travis to build the stash image. It must be run after cross-compiling - that is, `stash-linux` must exist in the `dist` directory. This image must be built from the `dist` directory. \ No newline at end of file diff --git a/docker/ci/x86_64/docker_push.sh b/docker/ci/x86_64/docker_push.sh new file mode 100644 index 000000000..771ea8786 --- /dev/null +++ b/docker/ci/x86_64/docker_push.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +DOCKER_TAG=$1 + +# must build the image from dist directory +docker build -t stashapp/stash:$DOCKER_TAG -f ./docker/ci/x86_64/Dockerfile ./dist + +echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin +docker push stashapp/stash:$DOCKER_TAG diff --git a/docker/compiler/Dockerfile b/docker/compiler/Dockerfile index 1a0126979..09c1ffd08 100644 --- a/docker/compiler/Dockerfile +++ b/docker/compiler/Dockerfile @@ -10,9 +10,12 @@ ENV PACKR2_DOWNLOAD_URL=https://github.com/gobuffalo/packr/releases/download/v${ # Install tools RUN apt-get update && apt-get install -y apt-transport-https RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list +# prevent caching of the key +ADD https://dl.yarnpkg.com/debian/pubkey.gpg yarn.gpg +RUN cat yarn.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ + rm yarn.gpg RUN apt-get update && \ apt-get install -y automake autogen \ @@ -20,6 +23,8 @@ RUN apt-get update && \ patch make tar xz-utils bzip2 gzip sed cpio \ gcc-6-multilib g++-6-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \ gcc-arm-linux-gnueabi libc-dev-armel-cross linux-libc-dev-armel-cross \ + gcc-arm-linux-gnueabihf libc-dev-armhf-cross \ + gcc-aarch64-linux-gnu libc-dev-arm64-cross \ nodejs yarn --no-install-recommends || exit 1; \ rm -rf /var/lib/apt/lists/*; diff --git a/docker/compiler/Makefile b/docker/compiler/Makefile index 278130e0f..c705b1b6f 100644 --- a/docker/compiler/Makefile +++ b/docker/compiler/Makefile @@ -1,6 +1,6 @@ -user=stashappdev +user=stashapp repo=compiler -version=2 +version=3 latest: docker build -t ${user}/${repo}:latest . diff --git a/docker/develop/x86_64/Dockerfile b/docker/develop/x86_64/Dockerfile index f80bec722..94624c7b0 100644 --- a/docker/develop/x86_64/Dockerfile +++ b/docker/develop/x86_64/Dockerfile @@ -7,9 +7,12 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* WORKDIR / SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN curl -L -o /stash $(curl -s https://api.github.com/repos/stashapp/stash/releases/tags/latest_develop | awk '/browser_download_url/ && /stash-linux/' | sed -e 's/.*: "\(.*\)"/\1/') && \ - chmod +x /stash && \ - curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ + +# added " to end of stash-linux clause so that it doesn't pick up the arm builds +RUN curl -L -o /stash $(curl -s https://api.github.com/repos/stashapp/stash/releases/tags/latest_develop | awk '/browser_download_url/ && /stash-linux"/' | sed -e 's/.*: "\(.*\)"/\1/') && \ + chmod +x /stash + +RUN curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ tar xf /ffmpeg.tar.xz && \ rm ffmpeg.tar.xz && \ mv /ffmpeg*/ /ffmpeg/ diff --git a/docker/production/x86_64/Dockerfile b/docker/production/x86_64/Dockerfile index c7cd55cc5..41285d760 100644 --- a/docker/production/x86_64/Dockerfile +++ b/docker/production/x86_64/Dockerfile @@ -7,9 +7,12 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* WORKDIR / SHELL ["/bin/bash", "-o", "pipefail", "-c"] -RUN curl -L -o /stash $(curl -s https://api.github.com/repos/stashapp/stash/releases/latest | awk '/browser_download_url/ && /stash-linux/' | sed -e 's/.*: "\(.*\)"/\1/') && \ - chmod +x /stash && \ - curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ + +# added " to end of stash-linux clause so that it doesn't pick up the arm builds +RUN curl -L -o /stash $(curl -s https://api.github.com/repos/stashapp/stash/releases/latest | awk '/browser_download_url/ && /stash-linux/"' | sed -e 's/.*: "\(.*\)"/\1/') && \ + chmod +x /stash + +RUN curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ tar xf /ffmpeg.tar.xz && \ rm ffmpeg.tar.xz && \ mv /ffmpeg*/ /ffmpeg/ diff --git a/go.mod b/go.mod index e4e407dd8..a822751e1 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,14 @@ module github.com/stashapp/stash require ( github.com/99designs/gqlgen v0.9.0 github.com/antchfx/htmlquery v1.2.3 - github.com/bmatcuk/doublestar v1.3.1 + github.com/bmatcuk/doublestar/v2 v2.0.1 + github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c + github.com/chromedp/chromedp v0.5.3 github.com/disintegration/imaging v1.6.0 github.com/go-chi/chi v4.0.2+incompatible github.com/gobuffalo/packr/v2 v2.0.2 github.com/golang-migrate/migrate/v4 v4.3.1 + github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.2.0 github.com/gorilla/websocket v1.4.0 github.com/h2non/filetype v1.0.8 @@ -16,16 +19,18 @@ require ( github.com/jmoiron/sqlx v1.2.0 github.com/json-iterator/go v1.1.9 github.com/mattn/go-sqlite3 v1.13.0 + github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 github.com/rs/cors v1.6.0 github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f github.com/sirupsen/logrus v1.4.2 github.com/spf13/pflag v1.0.3 github.com/spf13/viper v1.4.0 github.com/stretchr/testify v1.5.1 + github.com/tidwall/gjson v1.6.0 github.com/vektah/gqlparser v1.1.2 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/image v0.0.0-20190118043309-183bebdce1b2 - golang.org/x/net v0.0.0-20200421231249-e086a090c8fd + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 gopkg.in/yaml.v2 v2.2.2 ) diff --git a/go.sum b/go.sum index d9ef0d5a2..d1da3efc4 100644 --- a/go.sum +++ b/go.sum @@ -37,11 +37,16 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bmatcuk/doublestar v1.3.1 h1:rT8rxDPsavp9G+4ZULzqhhUSaI/OPsTZNG88Z3i0xvY= -github.com/bmatcuk/doublestar v1.3.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/bmatcuk/doublestar/v2 v2.0.1 h1:EFT91DmIMRcrUEcYUW7AqSAwKvNzP5+CoDmNVBbcQOU= +github.com/bmatcuk/doublestar/v2 v2.0.1/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= +github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c h1:qM1xzKK8kc93zKPkxK4iqtjksqDDrU6g9wGnr30jyLo= +github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE= +github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg= +github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= @@ -310,6 +315,12 @@ github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOreDJznWevcn8NTmQEW5STSBgIkpkjzqXc= github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -408,6 +419,8 @@ github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46s github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs= +github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -425,6 +438,9 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c= @@ -470,6 +486,8 @@ github.com/mongodb/mongo-go-driver v0.3.0/go.mod h1:NK/HWDIIZkaYsnYa0hmtP443T5EL github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 h1:Ohgj9L0EYOgXxkDp+bczlMBiulwmqYzQpvQNUdtt3oc= +github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007/go.mod h1:wKCOWMb6iNlvKiOToY2cNuaovSXvIiv1zDi9QDR7aGQ= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= @@ -582,6 +600,7 @@ github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -590,7 +609,13 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -676,6 +701,8 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgP golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -719,6 +746,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/gqlgen.yml b/gqlgen.yml index 29e794f31..0d1a780e5 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -44,3 +44,7 @@ models: model: github.com/stashapp/stash/pkg/models.ScrapedSceneTag SceneFileType: model: github.com/stashapp/stash/pkg/models.SceneFileType + ScrapedMovie: + model: github.com/stashapp/stash/pkg/models.ScrapedMovie + ScrapedMovieStudio: + model: github.com/stashapp/stash/pkg/models.ScrapedMovieStudio diff --git a/graphql/documents/data/config.graphql b/graphql/documents/data/config.graphql index e050bfdd1..ada4e99fb 100644 --- a/graphql/documents/data/config.graphql +++ b/graphql/documents/data/config.graphql @@ -3,10 +3,15 @@ fragment ConfigGeneralData on ConfigGeneralResult { databasePath generatedPath cachePath + calculateMD5 + videoFileNamingAlgorithm + previewSegments + previewSegmentDuration + previewExcludeStart + previewExcludeEnd + previewPreset maxTranscodeSize maxStreamingTranscodeSize - forceMkv - forceHevc username password maxSessionAge @@ -16,6 +21,7 @@ fragment ConfigGeneralData on ConfigGeneralResult { logAccess excludes scraperUserAgent + scraperCDPPath } fragment ConfigInterfaceData on ConfigInterfaceResult { diff --git a/graphql/documents/data/gallery.graphql b/graphql/documents/data/gallery.graphql index 66dd8ead2..2f77518b4 100644 --- a/graphql/documents/data/gallery.graphql +++ b/graphql/documents/data/gallery.graphql @@ -8,4 +8,9 @@ fragment GalleryData on Gallery { name path } + scene { + id + title + path + } } diff --git a/graphql/documents/data/scene-slim.graphql b/graphql/documents/data/scene-slim.graphql index 6d6fb3b05..535d79c87 100644 --- a/graphql/documents/data/scene-slim.graphql +++ b/graphql/documents/data/scene-slim.graphql @@ -1,6 +1,7 @@ fragment SlimSceneData on Scene { id checksum + oshash title details url diff --git a/graphql/documents/data/scene.graphql b/graphql/documents/data/scene.graphql index 06a7cab5a..aa02db41a 100644 --- a/graphql/documents/data/scene.graphql +++ b/graphql/documents/data/scene.graphql @@ -1,6 +1,7 @@ fragment SceneData on Scene { id checksum + oshash title details url @@ -33,8 +34,6 @@ fragment SceneData on Scene { ...SceneMarkerData } - is_streamable - gallery { ...GalleryData } diff --git a/graphql/documents/data/scrapers.graphql b/graphql/documents/data/scrapers.graphql index e9c8f324b..eb10e6a5c 100644 --- a/graphql/documents/data/scrapers.graphql +++ b/graphql/documents/data/scrapers.graphql @@ -19,7 +19,7 @@ fragment ScrapedPerformerData on ScrapedPerformer { } fragment ScrapedScenePerformerData on ScrapedScenePerformer { - id + stored_id name gender url @@ -38,6 +38,12 @@ fragment ScrapedScenePerformerData on ScrapedScenePerformer { aliases } +fragment ScrapedMovieStudioData on ScrapedMovieStudio { + id + name + url +} + fragment ScrapedMovieData on ScrapedMovie { name aliases @@ -47,10 +53,16 @@ fragment ScrapedMovieData on ScrapedMovie { director url synopsis + front_image + back_image + + studio { + ...ScrapedMovieStudioData + } } fragment ScrapedSceneMovieData on ScrapedSceneMovie { - id + stored_id name aliases duration @@ -62,13 +74,13 @@ fragment ScrapedSceneMovieData on ScrapedSceneMovie { } fragment ScrapedSceneStudioData on ScrapedSceneStudio { - id + stored_id name url } fragment ScrapedSceneTagData on ScrapedSceneTag { - id + stored_id name } @@ -105,4 +117,4 @@ fragment ScrapedSceneData on ScrapedScene { movies { ...ScrapedSceneMovieData } -} \ No newline at end of file +} diff --git a/graphql/documents/data/studio.graphql b/graphql/documents/data/studio.graphql index 9d6af7945..890f9c43f 100644 --- a/graphql/documents/data/studio.graphql +++ b/graphql/documents/data/studio.graphql @@ -3,6 +3,22 @@ fragment StudioData on Studio { checksum name url + parent_studio { + id + checksum + name + url + image_path + scene_count + } + child_studios { + id + checksum + name + url + image_path + scene_count + } image_path scene_count } diff --git a/graphql/documents/data/tag.graphql b/graphql/documents/data/tag.graphql index f779ad064..650f52a56 100644 --- a/graphql/documents/data/tag.graphql +++ b/graphql/documents/data/tag.graphql @@ -1,6 +1,7 @@ fragment TagData on Tag { id name + image_path scene_count scene_marker_count } diff --git a/graphql/documents/mutations/metadata.graphql b/graphql/documents/mutations/metadata.graphql index d02f10b09..925fd5158 100644 --- a/graphql/documents/mutations/metadata.graphql +++ b/graphql/documents/mutations/metadata.graphql @@ -22,6 +22,10 @@ mutation MetadataClean { metadataClean } +mutation MigrateHashNaming { + migrateHashNaming +} + mutation StopJob { stopJob } \ No newline at end of file diff --git a/graphql/documents/mutations/plugins.graphql b/graphql/documents/mutations/plugins.graphql new file mode 100644 index 000000000..b989c7ca6 --- /dev/null +++ b/graphql/documents/mutations/plugins.graphql @@ -0,0 +1,7 @@ +mutation ReloadPlugins { + reloadPlugins +} + +mutation RunPluginTask($plugin_id: ID!, $task_name: String!, $args: [PluginArgInput!]) { + runPluginTask(plugin_id: $plugin_id, task_name: $task_name, args: $args) +} diff --git a/graphql/documents/mutations/scene.graphql b/graphql/documents/mutations/scene.graphql index 80c38d109..0915b48cd 100644 --- a/graphql/documents/mutations/scene.graphql +++ b/graphql/documents/mutations/scene.graphql @@ -80,6 +80,10 @@ mutation SceneDestroy($id: ID!, $delete_file: Boolean, $delete_generated : Boole sceneDestroy(input: {id: $id, delete_file: $delete_file, delete_generated: $delete_generated}) } +mutation ScenesDestroy($ids: [ID!]!, $delete_file: Boolean, $delete_generated : Boolean) { + scenesDestroy(input: {ids: $ids, delete_file: $delete_file, delete_generated: $delete_generated}) +} + mutation SceneGenerateScreenshot($id: ID!, $at: Float) { sceneGenerateScreenshot(id: $id, at: $at) } diff --git a/graphql/documents/mutations/scrapers.graphql b/graphql/documents/mutations/scrapers.graphql new file mode 100644 index 000000000..3f186faec --- /dev/null +++ b/graphql/documents/mutations/scrapers.graphql @@ -0,0 +1,3 @@ +mutation ReloadScrapers { + reloadScrapers +} diff --git a/graphql/documents/mutations/studio.graphql b/graphql/documents/mutations/studio.graphql index 19ece5bb7..539b96358 100644 --- a/graphql/documents/mutations/studio.graphql +++ b/graphql/documents/mutations/studio.graphql @@ -1,9 +1,10 @@ mutation StudioCreate( $name: String!, $url: String, - $image: String) { + $image: String + $parent_id: ID) { - studioCreate(input: { name: $name, url: $url, image: $image }) { + studioCreate(input: { name: $name, url: $url, image: $image, parent_id: $parent_id }) { ...StudioData } } @@ -12,9 +13,10 @@ mutation StudioUpdate( $id: ID! $name: String, $url: String, - $image: String) { + $image: String + $parent_id: ID) { - studioUpdate(input: { id: $id, name: $name, url: $url, image: $image }) { + studioUpdate(input: { id: $id, name: $name, url: $url, image: $image, parent_id: $parent_id }) { ...StudioData } } diff --git a/graphql/documents/mutations/tag.graphql b/graphql/documents/mutations/tag.graphql index 85e1efcd2..3dcd8f2b3 100644 --- a/graphql/documents/mutations/tag.graphql +++ b/graphql/documents/mutations/tag.graphql @@ -1,5 +1,5 @@ -mutation TagCreate($name: String!) { - tagCreate(input: { name: $name }) { +mutation TagCreate($name: String!, $image: String) { + tagCreate(input: { name: $name, image: $image }) { ...TagData } } @@ -8,8 +8,8 @@ mutation TagDestroy($id: ID!) { tagDestroy(input: { id: $id }) } -mutation TagUpdate($id: ID!, $name: String!) { - tagUpdate(input: { id: $id, name: $name }) { +mutation TagUpdate($id: ID!, $name: String!, $image: String) { + tagUpdate(input: { id: $id, name: $name, image: $image }) { ...TagData } } \ No newline at end of file diff --git a/graphql/documents/queries/gallery.graphql b/graphql/documents/queries/gallery.graphql index 20535611d..205f661bc 100644 --- a/graphql/documents/queries/gallery.graphql +++ b/graphql/documents/queries/gallery.graphql @@ -1,5 +1,5 @@ -query FindGalleries($filter: FindFilterType) { - findGalleries(filter: $filter) { +query FindGalleries($filter: FindFilterType, $gallery_filter: GalleryFilterType) { + findGalleries(gallery_filter: $gallery_filter, filter: $filter) { count galleries { ...GalleryData diff --git a/graphql/documents/queries/misc.graphql b/graphql/documents/queries/misc.graphql index 2786ca997..fe9e85da7 100644 --- a/graphql/documents/queries/misc.graphql +++ b/graphql/documents/queries/misc.graphql @@ -1,9 +1,3 @@ -query FindTag($id: ID!) { - findTag(id: $id) { - ...TagData - } -} - query MarkerStrings($q: String, $sort: String) { markerStrings(q: $q, sort: $sort) { id diff --git a/graphql/documents/queries/plugins.graphql b/graphql/documents/queries/plugins.graphql new file mode 100644 index 000000000..72b8407da --- /dev/null +++ b/graphql/documents/queries/plugins.graphql @@ -0,0 +1,25 @@ +query Plugins { + plugins { + id + name + description + url + version + + tasks { + name + description + } + } +} + +query PluginTasks { + pluginTasks { + name + description + plugin { + id + name + } + } +} diff --git a/graphql/documents/queries/scene.graphql b/graphql/documents/queries/scene.graphql index 343335503..ff02ee4fb 100644 --- a/graphql/documents/queries/scene.graphql +++ b/graphql/documents/queries/scene.graphql @@ -53,4 +53,12 @@ query ParseSceneFilenames($filter: FindFilterType!, $config: SceneParserInput!) tag_ids } } -} \ No newline at end of file +} + +query SceneStreams($id: ID!) { + sceneStreams(id: $id) { + url + mime_type + label + } +} diff --git a/graphql/documents/queries/scrapers/scrapers.graphql b/graphql/documents/queries/scrapers/scrapers.graphql index cc4fa15e9..9904f31f5 100644 --- a/graphql/documents/queries/scrapers/scrapers.graphql +++ b/graphql/documents/queries/scrapers/scrapers.graphql @@ -20,6 +20,17 @@ query ListSceneScrapers { } } +query ListMovieScrapers { + listMovieScrapers { + id + name + movie { + urls + supported_scrapes + } + } +} + query ScrapePerformerList($scraper_id: ID!, $query: String!) { scrapePerformerList(scraper_id: $scraper_id, query: $query) { ...ScrapedPerformerData @@ -48,4 +59,10 @@ query ScrapeSceneURL($url: String!) { scrapeSceneURL(url: $url) { ...ScrapedSceneData } -} \ No newline at end of file +} + +query ScrapeMovieURL($url: String!) { + scrapeMovieURL(url: $url) { + ...ScrapedMovieData + } +} diff --git a/graphql/documents/queries/studio.graphql b/graphql/documents/queries/studio.graphql index a4898c210..f18f67e93 100644 --- a/graphql/documents/queries/studio.graphql +++ b/graphql/documents/queries/studio.graphql @@ -1,5 +1,5 @@ -query FindStudios($filter: FindFilterType) { - findStudios(filter: $filter) { +query FindStudios($filter: FindFilterType, $studio_filter: StudioFilterType ) { + findStudios(filter: $filter, studio_filter: $studio_filter) { count studios { ...StudioData diff --git a/graphql/documents/queries/tag.graphql b/graphql/documents/queries/tag.graphql new file mode 100644 index 000000000..468b70d7d --- /dev/null +++ b/graphql/documents/queries/tag.graphql @@ -0,0 +1,14 @@ +query FindTags($filter: FindFilterType, $tag_filter: TagFilterType ) { + findTags(filter: $filter, tag_filter: $tag_filter) { + count + tags { + ...TagData + } + } +} + +query FindTag($id: ID!) { + findTag(id: $id) { + ...TagData + } +} \ No newline at end of file diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 3a57a551e..e2ebf0280 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -2,11 +2,16 @@ type Query { """Find a scene by ID or Checksum""" findScene(id: ID, checksum: String): Scene + findSceneByHash(input: SceneHashInput!): Scene + """A function which queries Scene objects""" findScenes(scene_filter: SceneFilterType, scene_ids: [Int!], filter: FindFilterType): FindScenesResultType! findScenesByPathRegex(filter: FindFilterType): FindScenesResultType! + """Return valid stream paths""" + sceneStreams(id: ID): [SceneStreamEndpoint!]! + parseSceneFilenames(filter: FindFilterType, config: SceneParserInput!): SceneParserResultType! """A function which queries SceneMarker objects""" @@ -20,7 +25,7 @@ type Query { """Find a studio by ID""" findStudio(id: ID!): Studio """A function which queries Studio objects""" - findStudios(filter: FindFilterType): FindStudiosResultType! + findStudios(studio_filter: StudioFilterType, filter: FindFilterType): FindStudiosResultType! """Find a movie by ID""" findMovie(id: ID!): Movie @@ -28,9 +33,10 @@ type Query { findMovies(movie_filter: MovieFilterType, filter: FindFilterType): FindMoviesResultType! findGallery(id: ID!): Gallery - findGalleries(filter: FindFilterType): FindGalleriesResultType! + findGalleries(gallery_filter: GalleryFilterType, filter: FindFilterType): FindGalleriesResultType! findTag(id: ID!): Tag + findTags(tag_filter: TagFilterType, filter: FindFilterType): FindTagsResultType! """Retrieve random scene markers for the wall""" markerWall(q: String): [SceneMarker!]! @@ -53,6 +59,8 @@ type Query { """List available scrapers""" listPerformerScrapers: [Scraper!]! listSceneScrapers: [Scraper!]! + listMovieScrapers: [Scraper!]! + """Scrape a list of performers based on name""" scrapePerformerList(scraper_id: ID!, query: String!): [ScrapedPerformer!]! """Scrapes a complete performer record based on a scrapePerformerList result""" @@ -63,12 +71,21 @@ type Query { scrapeScene(scraper_id: ID!, scene: SceneUpdateInput!): ScrapedScene """Scrapes a complete performer record based on a URL""" scrapeSceneURL(url: String!): ScrapedScene + """Scrapes a complete movie record based on a URL""" + scrapeMovieURL(url: String!): ScrapedMovie """Scrape a performer using Freeones""" scrapeFreeones(performer_name: String!): ScrapedPerformer """Scrape a list of performers from a query""" scrapeFreeonesPerformerList(query: String!): [String!]! + # Plugins + """List loaded plugins""" + plugins: [Plugin!] + """List available plugin operations""" + pluginTasks: [PluginTask!] + + # Config """Returns the current, complete configuration""" configuration: ConfigResult! @@ -104,6 +121,7 @@ type Mutation { sceneUpdate(input: SceneUpdateInput!): Scene bulkSceneUpdate(input: BulkSceneUpdateInput!): [Scene!] sceneDestroy(input: SceneDestroyInput!): Boolean! + scenesDestroy(input: ScenesDestroyInput!): Boolean! scenesUpdate(input: [SceneUpdateInput!]!): [Scene] """Increments the o-counter for a scene. Returns the new value""" @@ -152,6 +170,15 @@ type Mutation { metadataAutoTag(input: AutoTagMetadataInput!): String! """Clean metadata. Returns the job ID""" metadataClean: String! + """Migrate generated files for the current hash naming""" + migrateHashNaming: String! + + """Reload scrapers""" + reloadScrapers: Boolean! + + """Run plugin task. Returns the job ID""" + runPluginTask(plugin_id: ID!, task_name: String!, args: [PluginArgInput!]): String! + reloadPlugins: Boolean! stopJob: Boolean! } diff --git a/graphql/schema/types/config.graphql b/graphql/schema/types/config.graphql index 0f65c97fb..0803ca9d1 100644 --- a/graphql/schema/types/config.graphql +++ b/graphql/schema/types/config.graphql @@ -7,6 +7,21 @@ enum StreamingResolutionEnum { "Original", ORIGINAL } +enum PreviewPreset { + "X264_ULTRAFAST", ultrafast + "X264_VERYFAST", veryfast + "X264_FAST", fast + "X264_MEDIUM", medium + "X264_SLOW", slow + "X264_SLOWER", slower + "X264_VERYSLOW", veryslow +} + +enum HashAlgorithm { + MD5 + "oshash", OSHASH +} + input ConfigGeneralInput { """Array of file paths to content""" stashes: [String!] @@ -16,14 +31,24 @@ input ConfigGeneralInput { generatedPath: String """Path to cache""" cachePath: String + """Whether to calculate MD5 checksums for scene video files""" + calculateMD5: Boolean! + """Hash algorithm to use for generated file naming""" + videoFileNamingAlgorithm: HashAlgorithm! + """Number of segments in a preview file""" + previewSegments: Int + """Preview segment duration, in seconds""" + previewSegmentDuration: Float + """Duration of start of video to exclude when generating previews""" + previewExcludeStart: String + """Duration of end of video to exclude when generating previews""" + previewExcludeEnd: String + """Preset when generating preview""" + previewPreset: PreviewPreset """Max generated transcode size""" maxTranscodeSize: StreamingResolutionEnum """Max streaming transcode size""" maxStreamingTranscodeSize: StreamingResolutionEnum - """Force MKV as supported format""" - forceMkv: Boolean! - """Force HEVC as a supported codec""" - forceHevc: Boolean! """Username""" username: String """Password""" @@ -42,6 +67,8 @@ input ConfigGeneralInput { excludes: [String!] """Scraper user agent string""" scraperUserAgent: String + """Scraper CDP path. Path to chrome executable or remote address""" + scraperCDPPath: String } type ConfigGeneralResult { @@ -53,14 +80,24 @@ type ConfigGeneralResult { generatedPath: String! """Path to cache""" cachePath: String! + """Whether to calculate MD5 checksums for scene video files""" + calculateMD5: Boolean! + """Hash algorithm to use for generated file naming""" + videoFileNamingAlgorithm: HashAlgorithm! + """Number of segments in a preview file""" + previewSegments: Int! + """Preview segment duration, in seconds""" + previewSegmentDuration: Float! + """Duration of start of video to exclude when generating previews""" + previewExcludeStart: String! + """Duration of end of video to exclude when generating previews""" + previewExcludeEnd: String! + """Preset when generating preview""" + previewPreset: PreviewPreset! """Max generated transcode size""" maxTranscodeSize: StreamingResolutionEnum """Max streaming transcode size""" maxStreamingTranscodeSize: StreamingResolutionEnum - """Force MKV as supported format""" - forceMkv: Boolean! - """Force HEVC as a supported codec""" - forceHevc: Boolean! """Username""" username: String! """Password""" @@ -79,6 +116,8 @@ type ConfigGeneralResult { excludes: [String!]! """Scraper user agent string""" scraperUserAgent: String + """Scraper CDP path. Path to chrome executable or remote address""" + scraperCDPPath: String } input ConfigInterfaceInput { diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 92ac58e2c..cda7adb6d 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -89,6 +89,31 @@ input SceneFilterType { input MovieFilterType { """Filter to only include movies with this studio""" studios: MultiCriterionInput + """Filter to only include movies missing this property""" + is_missing: String +} + +input StudioFilterType { + """Filter to only include studios with this parent studio""" + parents: MultiCriterionInput + """Filter to only include studios missing this property""" + is_missing: String +} + +input GalleryFilterType { + """Filter to only include galleries missing this property""" + is_missing: String +} + +input TagFilterType { + """Filter to only include tags missing this property""" + is_missing: String + + """Filter by number of scenes with this tag""" + scene_count: IntCriterionInput + + """Filter by number of markers with this tag""" + marker_count: IntCriterionInput } enum CriterionModifier { diff --git a/graphql/schema/types/gallery.graphql b/graphql/schema/types/gallery.graphql index fdd031f0e..e94be7be1 100644 --- a/graphql/schema/types/gallery.graphql +++ b/graphql/schema/types/gallery.graphql @@ -4,6 +4,7 @@ type Gallery { checksum: String! path: String! title: String + scene: Scene """The files in the gallery""" files: [GalleryFilesType!]! # Resolver diff --git a/graphql/schema/types/metadata.graphql b/graphql/schema/types/metadata.graphql index dede131fc..25a5b66f7 100644 --- a/graphql/schema/types/metadata.graphql +++ b/graphql/schema/types/metadata.graphql @@ -1,12 +1,35 @@ input GenerateMetadataInput { sprites: Boolean! previews: Boolean! - previewPreset: PreviewPreset imagePreviews: Boolean! + previewOptions: GeneratePreviewOptionsInput markers: Boolean! transcodes: Boolean! """gallery thumbnails for cache usage""" thumbnails: Boolean! + + """scene ids to generate for""" + sceneIDs: [ID!] + """marker ids to generate for""" + markerIDs: [ID!] + """gallery ids to generate for""" + galleryIDs: [ID!] + + """overwrite existing media""" + overwrite: Boolean +} + +input GeneratePreviewOptionsInput { + """Number of segments in a preview file""" + previewSegments: Int + """Preview segment duration, in seconds""" + previewSegmentDuration: Float + """Duration of start of video to exclude when generating previews""" + previewExcludeStart: String + """Duration of end of video to exclude when generating previews""" + previewExcludeEnd: String + """Preset when generating preview""" + previewPreset: PreviewPreset } input ScanMetadataInput { @@ -27,13 +50,3 @@ type MetadataUpdateStatus { status: String! message: String! } - -enum PreviewPreset { - "X264_ULTRAFAST", ultrafast - "X264_VERYFAST", veryfast - "X264_FAST", fast - "X264_MEDIUM", medium - "X264_SLOW", slow - "X264_SLOWER", slower - "X264_VERYSLOW", veryslow -} diff --git a/graphql/schema/types/performer.graphql b/graphql/schema/types/performer.graphql index 621f23dd9..d10b95b69 100644 --- a/graphql/schema/types/performer.graphql +++ b/graphql/schema/types/performer.graphql @@ -4,6 +4,7 @@ enum GenderEnum { TRANSGENDER_MALE TRANSGENDER_FEMALE INTERSEX + NON_BINARY } type Performer { diff --git a/graphql/schema/types/plugin.graphql b/graphql/schema/types/plugin.graphql new file mode 100644 index 000000000..30d18c7ce --- /dev/null +++ b/graphql/schema/types/plugin.graphql @@ -0,0 +1,35 @@ + +type Plugin { + id: ID! + name: String! + description: String + url: String + version: String + + tasks: [PluginTask!] +} + +type PluginTask { + name: String! + description: String + plugin: Plugin! +} + +type PluginResult { + error: String + result: String +} + +input PluginArgInput { + key: String! + value: PluginValueInput +} + +input PluginValueInput { + str: String + i: Int + b: Boolean + f: Float + o: [PluginArgInput!] + a: [PluginValueInput!] +} diff --git a/graphql/schema/types/scene.graphql b/graphql/schema/types/scene.graphql index 8afdd4b43..3844c6a8e 100644 --- a/graphql/schema/types/scene.graphql +++ b/graphql/schema/types/scene.graphql @@ -25,7 +25,8 @@ type SceneMovie { type Scene { id: ID! - checksum: String! + checksum: String + oshash: String title: String details: String url: String @@ -36,7 +37,6 @@ type Scene { file: SceneFileType! # Resolver paths: ScenePathsType! # Resolver - is_streamable: Boolean! # Resolver scene_markers: [SceneMarker!]! gallery: Gallery @@ -99,6 +99,12 @@ input SceneDestroyInput { delete_generated: Boolean } +input ScenesDestroyInput { + ids: [ID!]! + delete_file: Boolean + delete_generated: Boolean +} + type FindScenesResultType { count: Int! scenes: [Scene!]! @@ -132,4 +138,15 @@ type SceneParserResult { type SceneParserResultType { count: Int! results: [SceneParserResult!]! -} \ No newline at end of file +} + +input SceneHashInput { + checksum: String + oshash: String +} + +type SceneStreamEndpoint { + url: String! + mime_type: String + label: String +} diff --git a/graphql/schema/types/scraped-movie.graphql b/graphql/schema/types/scraped-movie.graphql index 7589de364..ac221fb88 100644 --- a/graphql/schema/types/scraped-movie.graphql +++ b/graphql/schema/types/scraped-movie.graphql @@ -1,3 +1,10 @@ +type ScrapedMovieStudio { + """Set if studio matched""" + id: ID + name: String! + url: String +} + """A movie from a scraping operation...""" type ScrapedMovie { name: String @@ -8,6 +15,11 @@ type ScrapedMovie { director: String url: String synopsis: String + studio: ScrapedMovieStudio + + """This should be base64 encoded""" + front_image: String + back_image: String } input ScrapedMovieInput { @@ -19,4 +31,4 @@ input ScrapedMovieInput { director: String url: String synopsis: String -} \ No newline at end of file +} diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index 69c050a63..6a7dcaa0b 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -20,12 +20,14 @@ type Scraper { performer: ScraperSpec """Details for scene scraper""" scene: ScraperSpec + """Details for movie scraper""" + movie: ScraperSpec } type ScrapedScenePerformer { """Set if performer matched""" - id: ID + stored_id: ID name: String! gender: String url: String @@ -46,7 +48,7 @@ type ScrapedScenePerformer { type ScrapedSceneMovie { """Set if movie matched""" - id: ID + stored_id: ID name: String! aliases: String duration: String @@ -59,14 +61,14 @@ type ScrapedSceneMovie { type ScrapedSceneStudio { """Set if studio matched""" - id: ID + stored_id: ID name: String! url: String } type ScrapedSceneTag { """Set if tag matched""" - id: ID + stored_id: ID name: String! } diff --git a/graphql/schema/types/studio.graphql b/graphql/schema/types/studio.graphql index fc6579794..90309b8c5 100644 --- a/graphql/schema/types/studio.graphql +++ b/graphql/schema/types/studio.graphql @@ -3,7 +3,8 @@ type Studio { checksum: String! name: String! url: String - + parent_studio: Studio + child_studios: [Studio!]! image_path: String # Resolver scene_count: Int # Resolver } @@ -11,6 +12,7 @@ type Studio { input StudioCreateInput { name: String! url: String + parent_id: ID """This should be base64 encoded""" image: String } @@ -19,6 +21,7 @@ input StudioUpdateInput { id: ID! name: String url: String + parent_id: ID, """This should be base64 encoded""" image: String } diff --git a/graphql/schema/types/tag.graphql b/graphql/schema/types/tag.graphql index d1cf5bb5a..68fb53e81 100644 --- a/graphql/schema/types/tag.graphql +++ b/graphql/schema/types/tag.graphql @@ -2,19 +2,31 @@ type Tag { id: ID! name: String! + image_path: String # Resolver scene_count: Int # Resolver scene_marker_count: Int # Resolver } input TagCreateInput { name: String! + + """This should be base64 encoded""" + image: String } input TagUpdateInput { id: ID! name: String! + + """This should be base64 encoded""" + image: String } input TagDestroyInput { id: ID! +} + +type FindTagsResultType { + count: Int! + tags: [Tag!]! } \ No newline at end of file diff --git a/main.go b/main.go index ad54dc4d6..e54d5cad7 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,12 @@ import ( func main() { manager.Initialize() - database.Initialize(config.GetDatabasePath()) + + // perform the post-migration for new databases + if database.Initialize(config.GetDatabasePath()) { + manager.GetInstance().PostMigrate() + } + api.Start() blockForever() } diff --git a/pkg/api/context_keys.go b/pkg/api/context_keys.go index 917e1c587..5b0581bef 100644 --- a/pkg/api/context_keys.go +++ b/pkg/api/context_keys.go @@ -11,4 +11,5 @@ const ( studioKey key = 3 movieKey key = 4 ContextUser key = 5 + tagKey key = 6 ) diff --git a/pkg/api/images.go b/pkg/api/images.go index bec90eb82..013df4be3 100644 --- a/pkg/api/images.go +++ b/pkg/api/images.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/gobuffalo/packr/v2" + "github.com/stashapp/stash/pkg/utils" ) var performerBox *packr.Box @@ -30,3 +31,19 @@ func getRandomPerformerImage(gender string) ([]byte, error) { index := rand.Intn(len(imageFiles)) return box.Find(imageFiles[index]) } + +func getRandomPerformerImageUsingName(name, gender string) ([]byte, error) { + var box *packr.Box + switch strings.ToUpper(gender) { + case "FEMALE": + box = performerBox + case "MALE": + box = performerBoxMale + default: + box = performerBox + + } + imageFiles := box.List() + index := utils.IntFromString(name) % uint64(len(imageFiles)) + return box.Find(imageFiles[index]) +} diff --git a/pkg/api/migrate.go b/pkg/api/migrate.go index 6305f47f8..929f750fb 100644 --- a/pkg/api/migrate.go +++ b/pkg/api/migrate.go @@ -8,6 +8,7 @@ import ( "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/manager" ) type migrateData struct { @@ -80,6 +81,9 @@ func doMigrateHandler(w http.ResponseWriter, r *http.Request) { return } + // perform post-migration operations + manager.GetInstance().PostMigrate() + // if no backup path was provided, then delete the created backup if formBackupPath == "" { err = os.Remove(backupPath) diff --git a/pkg/api/resolver.go b/pkg/api/resolver.go index 5713e0b22..63ef59e87 100644 --- a/pkg/api/resolver.go +++ b/pkg/api/resolver.go @@ -43,6 +43,22 @@ func (r *Resolver) Tag() models.TagResolver { return &tagResolver{r} } +func (r *Resolver) ScrapedSceneTag() models.ScrapedSceneTagResolver { + return &scrapedSceneTagResolver{r} +} + +func (r *Resolver) ScrapedSceneMovie() models.ScrapedSceneMovieResolver { + return &scrapedSceneMovieResolver{r} +} + +func (r *Resolver) ScrapedScenePerformer() models.ScrapedScenePerformerResolver { + return &scrapedScenePerformerResolver{r} +} + +func (r *Resolver) ScrapedSceneStudio() models.ScrapedSceneStudioResolver { + return &scrapedSceneStudioResolver{r} +} + type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type subscriptionResolver struct{ *Resolver } @@ -54,6 +70,10 @@ type sceneMarkerResolver struct{ *Resolver } type studioResolver struct{ *Resolver } type movieResolver struct{ *Resolver } type tagResolver struct{ *Resolver } +type scrapedSceneTagResolver struct{ *Resolver } +type scrapedSceneMovieResolver struct{ *Resolver } +type scrapedScenePerformerResolver struct{ *Resolver } +type scrapedSceneStudioResolver struct{ *Resolver } func (r *queryResolver) MarkerWall(ctx context.Context, q *string) ([]*models.SceneMarker, error) { qb := models.NewSceneMarkerQueryBuilder() diff --git a/pkg/api/resolver_model_gallery.go b/pkg/api/resolver_model_gallery.go index 1fcf260ee..ebee45420 100644 --- a/pkg/api/resolver_model_gallery.go +++ b/pkg/api/resolver_model_gallery.go @@ -2,6 +2,7 @@ package api import ( "context" + "github.com/stashapp/stash/pkg/models" ) @@ -13,3 +14,12 @@ func (r *galleryResolver) Files(ctx context.Context, obj *models.Gallery) ([]*mo baseURL, _ := ctx.Value(BaseURLCtxKey).(string) return obj.GetFiles(baseURL), nil } + +func (r *galleryResolver) Scene(ctx context.Context, obj *models.Gallery) (*models.Scene, error) { + if !obj.SceneID.Valid { + return nil, nil + } + + qb := models.NewSceneQueryBuilder() + return qb.Find(int(obj.SceneID.Int64)) +} diff --git a/pkg/api/resolver_model_scene.go b/pkg/api/resolver_model_scene.go index 36e921052..9d2c26e3b 100644 --- a/pkg/api/resolver_model_scene.go +++ b/pkg/api/resolver_model_scene.go @@ -4,11 +4,24 @@ import ( "context" "github.com/stashapp/stash/pkg/api/urlbuilders" - "github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) +func (r *sceneResolver) Checksum(ctx context.Context, obj *models.Scene) (*string, error) { + if obj.Checksum.Valid { + return &obj.Checksum.String, nil + } + return nil, nil +} + +func (r *sceneResolver) Oshash(ctx context.Context, obj *models.Scene) (*string, error) { + if obj.OSHash.Valid { + return &obj.OSHash.String, nil + } + return nil, nil +} + func (r *sceneResolver) Title(ctx context.Context, obj *models.Scene) (*string, error) { if obj.Title.Valid { return &obj.Title.String, nil @@ -81,12 +94,6 @@ func (r *sceneResolver) Paths(ctx context.Context, obj *models.Scene) (*models.S }, nil } -func (r *sceneResolver) IsStreamable(ctx context.Context, obj *models.Scene) (bool, error) { - // ignore error - ret, _ := manager.IsStreamable(obj) - return ret, nil -} - func (r *sceneResolver) SceneMarkers(ctx context.Context, obj *models.Scene) ([]*models.SceneMarker, error) { qb := models.NewSceneMarkerQueryBuilder() return qb.FindBySceneID(obj.ID, nil) diff --git a/pkg/api/resolver_model_scraper.go b/pkg/api/resolver_model_scraper.go new file mode 100644 index 000000000..583194496 --- /dev/null +++ b/pkg/api/resolver_model_scraper.go @@ -0,0 +1,23 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash/pkg/models" +) + +func (r *scrapedSceneTagResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneTag) (*string, error) { + return obj.ID, nil +} + +func (r *scrapedSceneMovieResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneMovie) (*string, error) { + return obj.ID, nil +} + +func (r *scrapedScenePerformerResolver) StoredID(ctx context.Context, obj *models.ScrapedScenePerformer) (*string, error) { + return obj.ID, nil +} + +func (r *scrapedSceneStudioResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneStudio) (*string, error) { + return obj.ID, nil +} diff --git a/pkg/api/resolver_model_studio.go b/pkg/api/resolver_model_studio.go index fee539c6c..f068fa12c 100644 --- a/pkg/api/resolver_model_studio.go +++ b/pkg/api/resolver_model_studio.go @@ -2,6 +2,7 @@ package api import ( "context" + "github.com/stashapp/stash/pkg/api/urlbuilders" "github.com/stashapp/stash/pkg/models" ) @@ -31,3 +32,17 @@ func (r *studioResolver) SceneCount(ctx context.Context, obj *models.Studio) (*i res, err := qb.CountByStudioID(obj.ID) return &res, err } + +func (r *studioResolver) ParentStudio(ctx context.Context, obj *models.Studio) (*models.Studio, error) { + if !obj.ParentID.Valid { + return nil, nil + } + + qb := models.NewStudioQueryBuilder() + return qb.Find(int(obj.ParentID.Int64), nil) +} + +func (r *studioResolver) ChildStudios(ctx context.Context, obj *models.Studio) ([]*models.Studio, error) { + qb := models.NewStudioQueryBuilder() + return qb.FindChildren(obj.ID, nil) +} diff --git a/pkg/api/resolver_model_tag.go b/pkg/api/resolver_model_tag.go index 26a13dfc7..24e44fdb2 100644 --- a/pkg/api/resolver_model_tag.go +++ b/pkg/api/resolver_model_tag.go @@ -2,6 +2,8 @@ package api import ( "context" + + "github.com/stashapp/stash/pkg/api/urlbuilders" "github.com/stashapp/stash/pkg/models" ) @@ -22,3 +24,9 @@ func (r *tagResolver) SceneMarkerCount(ctx context.Context, obj *models.Tag) (*i count, err := qb.CountByTagID(obj.ID) return &count, err } + +func (r *tagResolver) ImagePath(ctx context.Context, obj *models.Tag) (*string, error) { + baseURL, _ := ctx.Value(BaseURLCtxKey).(string) + imagePath := urlbuilders.NewTagURLBuilder(baseURL, obj.ID).GetTagImageURL() + return &imagePath, nil +} diff --git a/pkg/api/resolver_mutation_configure.go b/pkg/api/resolver_mutation_configure.go index ab7deb743..edeb7f011 100644 --- a/pkg/api/resolver_mutation_configure.go +++ b/pkg/api/resolver_mutation_configure.go @@ -2,6 +2,7 @@ package api import ( "context" + "errors" "fmt" "path/filepath" @@ -45,6 +46,37 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co config.Set(config.Cache, input.CachePath) } + if !input.CalculateMd5 && input.VideoFileNamingAlgorithm == models.HashAlgorithmMd5 { + return makeConfigGeneralResult(), errors.New("calculateMD5 must be true if using MD5") + } + + if input.VideoFileNamingAlgorithm != config.GetVideoFileNamingAlgorithm() { + // validate changing VideoFileNamingAlgorithm + if err := manager.ValidateVideoFileNamingAlgorithm(input.VideoFileNamingAlgorithm); err != nil { + return makeConfigGeneralResult(), err + } + + config.Set(config.VideoFileNamingAlgorithm, input.VideoFileNamingAlgorithm) + } + + config.Set(config.CalculateMD5, input.CalculateMd5) + + if input.PreviewSegments != nil { + config.Set(config.PreviewSegments, *input.PreviewSegments) + } + if input.PreviewSegmentDuration != nil { + config.Set(config.PreviewSegmentDuration, *input.PreviewSegmentDuration) + } + if input.PreviewExcludeStart != nil { + config.Set(config.PreviewExcludeStart, *input.PreviewExcludeStart) + } + if input.PreviewExcludeEnd != nil { + config.Set(config.PreviewExcludeEnd, *input.PreviewExcludeEnd) + } + if input.PreviewPreset != nil { + config.Set(config.PreviewPreset, input.PreviewPreset.String()) + } + if input.MaxTranscodeSize != nil { config.Set(config.MaxTranscodeSize, input.MaxTranscodeSize.String()) } @@ -52,8 +84,6 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co if input.MaxStreamingTranscodeSize != nil { config.Set(config.MaxStreamingTranscodeSize, input.MaxStreamingTranscodeSize.String()) } - config.Set(config.ForceMKV, input.ForceMkv) - config.Set(config.ForceHEVC, input.ForceHevc) if input.Username != nil { config.Set(config.Username, input.Username) @@ -89,8 +119,15 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co config.Set(config.Exclude, input.Excludes) } + refreshScraperCache := false if input.ScraperUserAgent != nil { config.Set(config.ScraperUserAgent, input.ScraperUserAgent) + refreshScraperCache = true + } + + if input.ScraperCDPPath != nil { + config.Set(config.ScraperCDPPath, input.ScraperCDPPath) + refreshScraperCache = true } if err := config.Write(); err != nil { @@ -98,6 +135,9 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input models.Co } manager.GetInstance().RefreshConfig() + if refreshScraperCache { + manager.GetInstance().RefreshScraperCache() + } return makeConfigGeneralResult(), nil } diff --git a/pkg/api/resolver_mutation_metadata.go b/pkg/api/resolver_mutation_metadata.go index 65a743af8..13f4da1ef 100644 --- a/pkg/api/resolver_mutation_metadata.go +++ b/pkg/api/resolver_mutation_metadata.go @@ -23,7 +23,7 @@ func (r *mutationResolver) MetadataExport(ctx context.Context) (string, error) { } func (r *mutationResolver) MetadataGenerate(ctx context.Context, input models.GenerateMetadataInput) (string, error) { - manager.GetInstance().Generate(input.Sprites, input.Previews, input.PreviewPreset, input.ImagePreviews, input.Markers, input.Transcodes, input.Thumbnails) + manager.GetInstance().Generate(input) return "todo", nil } @@ -37,6 +37,11 @@ func (r *mutationResolver) MetadataClean(ctx context.Context) (string, error) { return "todo", nil } +func (r *mutationResolver) MigrateHashNaming(ctx context.Context) (string, error) { + manager.GetInstance().MigrateHash() + return "todo", nil +} + func (r *mutationResolver) JobStatus(ctx context.Context) (*models.MetadataUpdateStatus, error) { status := manager.GetInstance().Status ret := models.MetadataUpdateStatus{ diff --git a/pkg/api/resolver_mutation_movie.go b/pkg/api/resolver_mutation_movie.go index d52d0dabe..85c8f63f0 100644 --- a/pkg/api/resolver_mutation_movie.go +++ b/pkg/api/resolver_mutation_movie.go @@ -19,32 +19,35 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr var backimageData []byte var err error - if input.FrontImage == nil { + // HACK: if back image is being set, set the front image to the default. + // This is because we can't have a null front image with a non-null back image. + if input.FrontImage == nil && input.BackImage != nil { input.FrontImage = &models.DefaultMovieImage } - if input.BackImage == nil { - input.BackImage = &models.DefaultMovieImage - } + // Process the base 64 encoded image string - _, frontimageData, err = utils.ProcessBase64Image(*input.FrontImage) - if err != nil { - return nil, err + if input.FrontImage != nil { + _, frontimageData, err = utils.ProcessBase64Image(*input.FrontImage) + if err != nil { + return nil, err + } } + // Process the base 64 encoded image string - _, backimageData, err = utils.ProcessBase64Image(*input.BackImage) - if err != nil { - return nil, err + if input.BackImage != nil { + _, backimageData, err = utils.ProcessBase64Image(*input.BackImage) + if err != nil { + return nil, err + } } // Populate a new movie from the input currentTime := time.Now() newMovie := models.Movie{ - BackImage: backimageData, - FrontImage: frontimageData, - Checksum: checksum, - Name: sql.NullString{String: input.Name, Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + Checksum: checksum, + Name: sql.NullString{String: input.Name, Valid: true}, + CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, } if input.Aliases != nil { @@ -90,6 +93,14 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr return nil, err } + // update image table + if len(frontimageData) > 0 { + if err := qb.UpdateMovieImages(movie.ID, frontimageData, backimageData, tx); err != nil { + _ = tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err @@ -106,19 +117,22 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUp ID: movieID, UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()}, } + var frontimageData []byte + var err error + frontImageIncluded := wasFieldIncluded(ctx, "front_image") if input.FrontImage != nil { - _, frontimageData, err := utils.ProcessBase64Image(*input.FrontImage) + _, frontimageData, err = utils.ProcessBase64Image(*input.FrontImage) if err != nil { return nil, err } - updatedMovie.FrontImage = &frontimageData } + backImageIncluded := wasFieldIncluded(ctx, "back_image") + var backimageData []byte if input.BackImage != nil { - _, backimageData, err := utils.ProcessBase64Image(*input.BackImage) + _, backimageData, err = utils.ProcessBase64Image(*input.BackImage) if err != nil { return nil, err } - updatedMovie.BackImage = &backimageData } if input.Name != nil { @@ -177,6 +191,43 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUp return nil, err } + // update image table + if frontImageIncluded || backImageIncluded { + if !frontImageIncluded { + frontimageData, err = qb.GetFrontImage(updatedMovie.ID, tx) + if err != nil { + tx.Rollback() + return nil, err + } + } + if !backImageIncluded { + backimageData, err = qb.GetBackImage(updatedMovie.ID, tx) + if err != nil { + tx.Rollback() + return nil, err + } + } + + if len(frontimageData) == 0 && len(backimageData) == 0 { + // both images are being nulled. Destroy them. + if err := qb.DestroyMovieImages(movie.ID, tx); err != nil { + tx.Rollback() + return nil, err + } + } else { + // HACK - if front image is null and back image is not null, then set the front image + // to the default image since we can't have a null front image and a non-null back image + if frontimageData == nil && backimageData != nil { + _, frontimageData, _ = utils.ProcessBase64Image(models.DefaultMovieImage) + } + + if err := qb.UpdateMovieImages(movie.ID, frontimageData, backimageData, tx); err != nil { + _ = tx.Rollback() + return nil, err + } + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err diff --git a/pkg/api/resolver_mutation_performer.go b/pkg/api/resolver_mutation_performer.go index 697192392..a00d568a6 100644 --- a/pkg/api/resolver_mutation_performer.go +++ b/pkg/api/resolver_mutation_performer.go @@ -18,13 +18,7 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per var imageData []byte var err error - if input.Image == nil { - gender := "" - if input.Gender != nil { - gender = input.Gender.String() - } - imageData, err = getRandomPerformerImage(gender) - } else { + if input.Image != nil { _, imageData, err = utils.ProcessBase64Image(*input.Image) } @@ -35,7 +29,6 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per // Populate a new performer from the input currentTime := time.Now() newPerformer := models.Performer{ - Image: imageData, Checksum: checksum, CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, @@ -103,6 +96,14 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdatePerformerImage(performer.ID, imageData, tx); err != nil { + _ = tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err @@ -118,12 +119,14 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per ID: performerID, UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()}, } + var imageData []byte + var err error + imageIncluded := wasFieldIncluded(ctx, "image") if input.Image != nil { - _, imageData, err := utils.ProcessBase64Image(*input.Image) + _, imageData, err = utils.ProcessBase64Image(*input.Image) if err != nil { return nil, err } - updatedPerformer.Image = imageData } if input.Name != nil { // generate checksum from performer name rather than image @@ -188,10 +191,24 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per qb := models.NewPerformerQueryBuilder() performer, err := qb.Update(updatedPerformer, tx) if err != nil { - _ = tx.Rollback() + tx.Rollback() return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdatePerformerImage(performer.ID, imageData, tx); err != nil { + tx.Rollback() + return nil, err + } + } else if imageIncluded { + // must be unsetting + if err := qb.DestroyPerformerImage(performer.ID, tx); err != nil { + tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err diff --git a/pkg/api/resolver_mutation_plugin.go b/pkg/api/resolver_mutation_plugin.go new file mode 100644 index 000000000..c5a83e8fc --- /dev/null +++ b/pkg/api/resolver_mutation_plugin.go @@ -0,0 +1,48 @@ +package api + +import ( + "context" + "net/http" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/manager" + "github.com/stashapp/stash/pkg/manager/config" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/plugin/common" +) + +func (r *mutationResolver) RunPluginTask(ctx context.Context, pluginID string, taskName string, args []*models.PluginArgInput) (string, error) { + currentUser := getCurrentUserID(ctx) + + var cookie *http.Cookie + var err error + if currentUser != nil { + cookie, err = createSessionCookie(*currentUser) + if err != nil { + return "", err + } + } + + serverConnection := common.StashServerConnection{ + Scheme: "http", + Port: config.GetPort(), + SessionCookie: cookie, + Dir: config.GetConfigPath(), + } + + if HasTLSConfig() { + serverConnection.Scheme = "https" + } + + manager.GetInstance().RunPluginTask(pluginID, taskName, args, serverConnection) + return "todo", nil +} + +func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) { + err := manager.GetInstance().PluginCache.ReloadPlugins() + if err != nil { + logger.Errorf("Error reading plugin configs: %s", err.Error()) + } + + return true, nil +} diff --git a/pkg/api/resolver_mutation_scene.go b/pkg/api/resolver_mutation_scene.go index 3cee12c11..1e4d78b1e 100644 --- a/pkg/api/resolver_mutation_scene.go +++ b/pkg/api/resolver_mutation_scene.go @@ -10,6 +10,7 @@ import ( "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/manager" + "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) @@ -80,13 +81,15 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T if input.Date != nil { updatedScene.Date = &models.SQLiteDate{String: *input.Date, Valid: true} } + if input.CoverImage != nil && *input.CoverImage != "" { var err error _, coverImageData, err = utils.ProcessBase64Image(*input.CoverImage) if err != nil { return nil, err } - updatedScene.Cover = &coverImageData + + // update the cover after updating the scene } if input.Rating != nil { @@ -111,6 +114,13 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T return nil, err } + // update cover table + if len(coverImageData) > 0 { + if err := qb.UpdateSceneCover(sceneID, coverImageData, tx); err != nil { + return nil, err + } + } + // Clear the existing gallery value gqb := models.NewGalleryQueryBuilder() err = gqb.ClearGalleryId(sceneID, tx) @@ -188,8 +198,7 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T // only update the cover image if provided and everything else was successful if coverImageData != nil { - - err = manager.SetSceneScreenshot(scene.Checksum, coverImageData) + err = manager.SetSceneScreenshot(scene.GetHash(config.GetVideoFileNamingAlgorithm()), coverImageData) if err != nil { return nil, err } @@ -409,7 +418,7 @@ func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneD // if delete generated is true, then delete the generated files // for the scene if input.DeleteGenerated != nil && *input.DeleteGenerated { - manager.DeleteGeneratedSceneFiles(scene) + manager.DeleteGeneratedSceneFiles(scene, config.GetVideoFileNamingAlgorithm()) } // if delete file is true, then delete the file as well @@ -421,6 +430,48 @@ func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneD return true, nil } +func (r *mutationResolver) ScenesDestroy(ctx context.Context, input models.ScenesDestroyInput) (bool, error) { + qb := models.NewSceneQueryBuilder() + tx := database.DB.MustBeginTx(ctx, nil) + + var scenes []*models.Scene + for _, id := range input.Ids { + sceneID, _ := strconv.Atoi(id) + + scene, err := qb.Find(sceneID) + if scene != nil { + scenes = append(scenes, scene) + } + err = manager.DestroyScene(sceneID, tx) + + if err != nil { + tx.Rollback() + return false, err + } + } + + if err := tx.Commit(); err != nil { + return false, err + } + + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + for _, scene := range scenes { + // if delete generated is true, then delete the generated files + // for the scene + if input.DeleteGenerated != nil && *input.DeleteGenerated { + manager.DeleteGeneratedSceneFiles(scene, fileNamingAlgo) + } + + // if delete file is true, then delete the file as well + // if it fails, just log a message + if input.DeleteFile != nil && *input.DeleteFile { + manager.DeleteSceneFile(scene) + } + } + + return true, nil +} + func (r *mutationResolver) SceneMarkerCreate(ctx context.Context, input models.SceneMarkerCreateInput) (*models.SceneMarker, error) { primaryTagID, _ := strconv.Atoi(input.PrimaryTagID) sceneID, _ := strconv.Atoi(input.SceneID) @@ -479,7 +530,7 @@ func (r *mutationResolver) SceneMarkerDestroy(ctx context.Context, id string) (b if scene != nil { seconds := int(marker.Seconds) - manager.DeleteSceneMarkerFiles(scene, seconds) + manager.DeleteSceneMarkerFiles(scene, seconds, config.GetVideoFileNamingAlgorithm()) } return true, nil @@ -548,7 +599,7 @@ func changeMarker(ctx context.Context, changeType int, changedMarker models.Scen if scene != nil { seconds := int(existingMarker.Seconds) - manager.DeleteSceneMarkerFiles(scene, seconds) + manager.DeleteSceneMarkerFiles(scene, seconds, config.GetVideoFileNamingAlgorithm()) } } diff --git a/pkg/api/resolver_mutation_scraper.go b/pkg/api/resolver_mutation_scraper.go new file mode 100644 index 000000000..0f82fdcb2 --- /dev/null +++ b/pkg/api/resolver_mutation_scraper.go @@ -0,0 +1,17 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash/pkg/manager" +) + +func (r *mutationResolver) ReloadScrapers(ctx context.Context) (bool, error) { + err := manager.GetInstance().ScraperCache.ReloadScrapers() + + if err != nil { + return false, err + } + + return true, nil +} diff --git a/pkg/api/resolver_mutation_studio.go b/pkg/api/resolver_mutation_studio.go index 1d07a809c..1069137f6 100644 --- a/pkg/api/resolver_mutation_studio.go +++ b/pkg/api/resolver_mutation_studio.go @@ -7,6 +7,7 @@ import ( "time" "github.com/stashapp/stash/pkg/database" + "github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) @@ -31,7 +32,6 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio // Populate a new studio from the input currentTime := time.Now() newStudio := models.Studio{ - Image: imageData, Checksum: checksum, Name: sql.NullString{String: input.Name, Valid: true}, CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, @@ -40,6 +40,10 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio if input.URL != nil { newStudio.URL = sql.NullString{String: *input.URL, Valid: true} } + if input.ParentID != nil { + parentID, _ := strconv.ParseInt(*input.ParentID, 10, 64) + newStudio.ParentID = sql.NullInt64{Int64: parentID, Valid: true} + } // Start the transaction and save the studio tx := database.DB.MustBeginTx(ctx, nil) @@ -50,6 +54,14 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdateStudioImage(studio.ID, imageData, tx); err != nil { + _ = tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err @@ -61,36 +73,68 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.StudioUpdateInput) (*models.Studio, error) { // Populate studio from the input studioID, _ := strconv.Atoi(input.ID) - updatedStudio := models.Studio{ + + updatedStudio := models.StudioPartial{ ID: studioID, - UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()}, + UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()}, } + + var imageData []byte + imageIncluded := wasFieldIncluded(ctx, "image") if input.Image != nil { - _, imageData, err := utils.ProcessBase64Image(*input.Image) + var err error + _, imageData, err = utils.ProcessBase64Image(*input.Image) if err != nil { return nil, err } - updatedStudio.Image = imageData } if input.Name != nil { // generate checksum from studio name rather than image checksum := utils.MD5FromString(*input.Name) - updatedStudio.Name = sql.NullString{String: *input.Name, Valid: true} - updatedStudio.Checksum = checksum + updatedStudio.Name = &sql.NullString{String: *input.Name, Valid: true} + updatedStudio.Checksum = &checksum } if input.URL != nil { - updatedStudio.URL = sql.NullString{String: *input.URL, Valid: true} + updatedStudio.URL = &sql.NullString{String: *input.URL, Valid: true} + } + + if input.ParentID != nil { + parentID, _ := strconv.ParseInt(*input.ParentID, 10, 64) + updatedStudio.ParentID = &sql.NullInt64{Int64: parentID, Valid: true} + } else { + // parent studio must be nullable + updatedStudio.ParentID = &sql.NullInt64{Valid: false} } // Start the transaction and save the studio tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewStudioQueryBuilder() + + if err := manager.ValidateModifyStudio(updatedStudio, tx); err != nil { + tx.Rollback() + return nil, err + } + studio, err := qb.Update(updatedStudio, tx) if err != nil { - _ = tx.Rollback() + tx.Rollback() return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdateStudioImage(studio.ID, imageData, tx); err != nil { + tx.Rollback() + return nil, err + } + } else if imageIncluded { + // must be unsetting + if err := qb.DestroyStudioImage(studio.ID, tx); err != nil { + tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err diff --git a/pkg/api/resolver_mutation_tag.go b/pkg/api/resolver_mutation_tag.go index 192b06fde..b23d99d00 100644 --- a/pkg/api/resolver_mutation_tag.go +++ b/pkg/api/resolver_mutation_tag.go @@ -2,10 +2,14 @@ package api import ( "context" - "github.com/stashapp/stash/pkg/database" - "github.com/stashapp/stash/pkg/models" + "fmt" "strconv" "time" + + "github.com/stashapp/stash/pkg/database" + "github.com/stashapp/stash/pkg/manager" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) func (r *mutationResolver) TagCreate(ctx context.Context, input models.TagCreateInput) (*models.Tag, error) { @@ -17,15 +21,41 @@ func (r *mutationResolver) TagCreate(ctx context.Context, input models.TagCreate UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, } - // Start the transaction and save the studio + var imageData []byte + var err error + + if input.Image != nil { + _, imageData, err = utils.ProcessBase64Image(*input.Image) + + if err != nil { + return nil, err + } + } + + // Start the transaction and save the tag tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewTagQueryBuilder() + + // ensure name is unique + if err := manager.EnsureTagNameUnique(newTag, tx); err != nil { + tx.Rollback() + return nil, err + } + tag, err := qb.Create(newTag, tx) if err != nil { - _ = tx.Rollback() + tx.Rollback() return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdateTagImage(tag.ID, imageData, tx); err != nil { + _ = tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err @@ -43,15 +73,61 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input models.TagUpdate UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()}, } + var imageData []byte + var err error + + imageIncluded := wasFieldIncluded(ctx, "image") + if input.Image != nil { + _, imageData, err = utils.ProcessBase64Image(*input.Image) + + if err != nil { + return nil, err + } + } + // Start the transaction and save the tag tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewTagQueryBuilder() + + // ensure name is unique + existing, err := qb.Find(tagID, tx) + if err != nil { + tx.Rollback() + return nil, err + } + + if existing == nil { + tx.Rollback() + return nil, fmt.Errorf("Tag with ID %d not found", tagID) + } + + if existing.Name != updatedTag.Name { + if err := manager.EnsureTagNameUnique(updatedTag, tx); err != nil { + tx.Rollback() + return nil, err + } + } + tag, err := qb.Update(updatedTag, tx) if err != nil { _ = tx.Rollback() return nil, err } + // update image table + if len(imageData) > 0 { + if err := qb.UpdateTagImage(tag.ID, imageData, tx); err != nil { + tx.Rollback() + return nil, err + } + } else if imageIncluded { + // must be unsetting + if err := qb.DestroyTagImage(tag.ID, tx); err != nil { + tx.Rollback() + return nil, err + } + } + // Commit if err := tx.Commit(); err != nil { return nil, err diff --git a/pkg/api/resolver_query_configuration.go b/pkg/api/resolver_query_configuration.go index da059de60..d8e0bbeaf 100644 --- a/pkg/api/resolver_query_configuration.go +++ b/pkg/api/resolver_query_configuration.go @@ -40,16 +40,22 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult { maxStreamingTranscodeSize := config.GetMaxStreamingTranscodeSize() scraperUserAgent := config.GetScraperUserAgent() + scraperCDPPath := config.GetScraperCDPPath() return &models.ConfigGeneralResult{ Stashes: config.GetStashPaths(), DatabasePath: config.GetDatabasePath(), GeneratedPath: config.GetGeneratedPath(), CachePath: config.GetCachePath(), + CalculateMd5: config.IsCalculateMD5(), + VideoFileNamingAlgorithm: config.GetVideoFileNamingAlgorithm(), + PreviewSegments: config.GetPreviewSegments(), + PreviewSegmentDuration: config.GetPreviewSegmentDuration(), + PreviewExcludeStart: config.GetPreviewExcludeStart(), + PreviewExcludeEnd: config.GetPreviewExcludeEnd(), + PreviewPreset: config.GetPreviewPreset(), MaxTranscodeSize: &maxTranscodeSize, MaxStreamingTranscodeSize: &maxStreamingTranscodeSize, - ForceMkv: config.GetForceMKV(), - ForceHevc: config.GetForceHEVC(), Username: config.GetUsername(), Password: config.GetPasswordHash(), MaxSessionAge: config.GetMaxSessionAge(), @@ -59,6 +65,7 @@ func makeConfigGeneralResult() *models.ConfigGeneralResult { LogAccess: config.GetLogAccess(), Excludes: config.GetExcludes(), ScraperUserAgent: &scraperUserAgent, + ScraperCDPPath: &scraperCDPPath, } } diff --git a/pkg/api/resolver_query_find_gallery.go b/pkg/api/resolver_query_find_gallery.go index d014e05ef..9468b73bc 100644 --- a/pkg/api/resolver_query_find_gallery.go +++ b/pkg/api/resolver_query_find_gallery.go @@ -2,8 +2,9 @@ package api import ( "context" - "github.com/stashapp/stash/pkg/models" "strconv" + + "github.com/stashapp/stash/pkg/models" ) func (r *queryResolver) FindGallery(ctx context.Context, id string) (*models.Gallery, error) { @@ -12,9 +13,9 @@ func (r *queryResolver) FindGallery(ctx context.Context, id string) (*models.Gal return qb.Find(idInt) } -func (r *queryResolver) FindGalleries(ctx context.Context, filter *models.FindFilterType) (*models.FindGalleriesResultType, error) { +func (r *queryResolver) FindGalleries(ctx context.Context, galleryFilter *models.GalleryFilterType, filter *models.FindFilterType) (*models.FindGalleriesResultType, error) { qb := models.NewGalleryQueryBuilder() - galleries, total := qb.Query(filter) + galleries, total := qb.Query(galleryFilter, filter) return &models.FindGalleriesResultType{ Count: total, Galleries: galleries, diff --git a/pkg/api/resolver_query_find_scene.go b/pkg/api/resolver_query_find_scene.go index fc4e806ea..781e6c5b3 100644 --- a/pkg/api/resolver_query_find_scene.go +++ b/pkg/api/resolver_query_find_scene.go @@ -21,6 +21,28 @@ func (r *queryResolver) FindScene(ctx context.Context, id *string, checksum *str return scene, err } +func (r *queryResolver) FindSceneByHash(ctx context.Context, input models.SceneHashInput) (*models.Scene, error) { + qb := models.NewSceneQueryBuilder() + var scene *models.Scene + var err error + + if input.Checksum != nil { + scene, err = qb.FindByChecksum(*input.Checksum) + if err != nil { + return nil, err + } + } + + if scene == nil && input.Oshash != nil { + scene, err = qb.FindByOSHash(*input.Oshash) + if err != nil { + return nil, err + } + } + + return scene, err +} + func (r *queryResolver) FindScenes(ctx context.Context, sceneFilter *models.SceneFilterType, sceneIds []int, filter *models.FindFilterType) (*models.FindScenesResultType, error) { qb := models.NewSceneQueryBuilder() scenes, total := qb.Query(sceneFilter, filter) diff --git a/pkg/api/resolver_query_find_studio.go b/pkg/api/resolver_query_find_studio.go index 4b39130f4..25528c517 100644 --- a/pkg/api/resolver_query_find_studio.go +++ b/pkg/api/resolver_query_find_studio.go @@ -12,9 +12,9 @@ func (r *queryResolver) FindStudio(ctx context.Context, id string) (*models.Stud return qb.Find(idInt, nil) } -func (r *queryResolver) FindStudios(ctx context.Context, filter *models.FindFilterType) (*models.FindStudiosResultType, error) { +func (r *queryResolver) FindStudios(ctx context.Context, studioFilter *models.StudioFilterType, filter *models.FindFilterType) (*models.FindStudiosResultType, error) { qb := models.NewStudioQueryBuilder() - studios, total := qb.Query(filter) + studios, total := qb.Query(studioFilter, filter) return &models.FindStudiosResultType{ Count: total, Studios: studios, diff --git a/pkg/api/resolver_query_find_tag.go b/pkg/api/resolver_query_find_tag.go index 64fc866c9..7e8727b9b 100644 --- a/pkg/api/resolver_query_find_tag.go +++ b/pkg/api/resolver_query_find_tag.go @@ -2,8 +2,9 @@ package api import ( "context" - "github.com/stashapp/stash/pkg/models" "strconv" + + "github.com/stashapp/stash/pkg/models" ) func (r *queryResolver) FindTag(ctx context.Context, id string) (*models.Tag, error) { @@ -12,6 +13,15 @@ func (r *queryResolver) FindTag(ctx context.Context, id string) (*models.Tag, er return qb.Find(idInt, nil) } +func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilterType, filter *models.FindFilterType) (*models.FindTagsResultType, error) { + qb := models.NewTagQueryBuilder() + tags, total := qb.Query(tagFilter, filter) + return &models.FindTagsResultType{ + Count: total, + Tags: tags, + }, nil +} + func (r *queryResolver) AllTags(ctx context.Context) ([]*models.Tag, error) { qb := models.NewTagQueryBuilder() return qb.All() diff --git a/pkg/api/resolver_query_plugin.go b/pkg/api/resolver_query_plugin.go new file mode 100644 index 000000000..059328e12 --- /dev/null +++ b/pkg/api/resolver_query_plugin.go @@ -0,0 +1,16 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash/pkg/manager" + "github.com/stashapp/stash/pkg/models" +) + +func (r *queryResolver) Plugins(ctx context.Context) ([]*models.Plugin, error) { + return manager.GetInstance().PluginCache.ListPlugins(), nil +} + +func (r *queryResolver) PluginTasks(ctx context.Context) ([]*models.PluginTask, error) { + return manager.GetInstance().PluginCache.ListPluginTasks(), nil +} diff --git a/pkg/api/resolver_query_scene.go b/pkg/api/resolver_query_scene.go new file mode 100644 index 000000000..faa745f5e --- /dev/null +++ b/pkg/api/resolver_query_scene.go @@ -0,0 +1,31 @@ +package api + +import ( + "context" + "errors" + "strconv" + + "github.com/stashapp/stash/pkg/api/urlbuilders" + "github.com/stashapp/stash/pkg/manager" + "github.com/stashapp/stash/pkg/models" +) + +func (r *queryResolver) SceneStreams(ctx context.Context, id *string) ([]*models.SceneStreamEndpoint, error) { + // find the scene + qb := models.NewSceneQueryBuilder() + idInt, _ := strconv.Atoi(*id) + scene, err := qb.Find(idInt) + + if err != nil { + return nil, err + } + + if scene == nil { + return nil, errors.New("nil scene") + } + + baseURL, _ := ctx.Value(BaseURLCtxKey).(string) + builder := urlbuilders.NewSceneURLBuilder(baseURL, scene.ID) + + return manager.GetSceneStreamPaths(scene, builder.GetStreamURL()) +} diff --git a/pkg/api/resolver_query_scraper.go b/pkg/api/resolver_query_scraper.go index ea5d36ad3..c66f2ebc6 100644 --- a/pkg/api/resolver_query_scraper.go +++ b/pkg/api/resolver_query_scraper.go @@ -3,6 +3,7 @@ package api import ( "context" + "github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/scraper" ) @@ -12,12 +13,12 @@ func (r *queryResolver) ScrapeFreeones(ctx context.Context, performer_name strin scrapedPerformer := models.ScrapedPerformerInput{ Name: &performer_name, } - return scraper.GetFreeonesScraper().ScrapePerformer(scrapedPerformer) + return manager.GetInstance().ScraperCache.ScrapePerformer(scraper.FreeonesScraperID, scrapedPerformer) } // deprecated func (r *queryResolver) ScrapeFreeonesPerformerList(ctx context.Context, query string) ([]string, error) { - scrapedPerformers, err := scraper.GetFreeonesScraper().ScrapePerformerNames(query) + scrapedPerformers, err := manager.GetInstance().ScraperCache.ScrapePerformerList(scraper.FreeonesScraperID, query) if err != nil { return nil, err @@ -33,11 +34,15 @@ func (r *queryResolver) ScrapeFreeonesPerformerList(ctx context.Context, query s } func (r *queryResolver) ListPerformerScrapers(ctx context.Context) ([]*models.Scraper, error) { - return scraper.ListPerformerScrapers() + return manager.GetInstance().ScraperCache.ListPerformerScrapers(), nil } func (r *queryResolver) ListSceneScrapers(ctx context.Context) ([]*models.Scraper, error) { - return scraper.ListSceneScrapers() + return manager.GetInstance().ScraperCache.ListSceneScrapers(), nil +} + +func (r *queryResolver) ListMovieScrapers(ctx context.Context) ([]*models.Scraper, error) { + return manager.GetInstance().ScraperCache.ListMovieScrapers(), nil } func (r *queryResolver) ScrapePerformerList(ctx context.Context, scraperID string, query string) ([]*models.ScrapedPerformer, error) { @@ -45,21 +50,25 @@ func (r *queryResolver) ScrapePerformerList(ctx context.Context, scraperID strin return nil, nil } - return scraper.ScrapePerformerList(scraperID, query) + return manager.GetInstance().ScraperCache.ScrapePerformerList(scraperID, query) } func (r *queryResolver) ScrapePerformer(ctx context.Context, scraperID string, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { - return scraper.ScrapePerformer(scraperID, scrapedPerformer) + return manager.GetInstance().ScraperCache.ScrapePerformer(scraperID, scrapedPerformer) } func (r *queryResolver) ScrapePerformerURL(ctx context.Context, url string) (*models.ScrapedPerformer, error) { - return scraper.ScrapePerformerURL(url) + return manager.GetInstance().ScraperCache.ScrapePerformerURL(url) } func (r *queryResolver) ScrapeScene(ctx context.Context, scraperID string, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { - return scraper.ScrapeScene(scraperID, scene) + return manager.GetInstance().ScraperCache.ScrapeScene(scraperID, scene) } func (r *queryResolver) ScrapeSceneURL(ctx context.Context, url string) (*models.ScrapedScene, error) { - return scraper.ScrapeSceneURL(url) + return manager.GetInstance().ScraperCache.ScrapeSceneURL(url) +} + +func (r *queryResolver) ScrapeMovieURL(ctx context.Context, url string) (*models.ScrapedMovie, error) { + return manager.GetInstance().ScraperCache.ScrapeMovieURL(url) } diff --git a/pkg/api/routes_movie.go b/pkg/api/routes_movie.go index 3c6659c59..00c882d6e 100644 --- a/pkg/api/routes_movie.go +++ b/pkg/api/routes_movie.go @@ -7,6 +7,7 @@ import ( "github.com/go-chi/chi" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) type movieRoutes struct{} @@ -25,12 +26,28 @@ func (rs movieRoutes) Routes() chi.Router { func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) { movie := r.Context().Value(movieKey).(*models.Movie) - _, _ = w.Write(movie.FrontImage) + qb := models.NewMovieQueryBuilder() + image, _ := qb.GetFrontImage(movie.ID, nil) + + defaultParam := r.URL.Query().Get("default") + if len(image) == 0 || defaultParam == "true" { + _, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage) + } + + utils.ServeImage(image, w, r) } func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) { movie := r.Context().Value(movieKey).(*models.Movie) - _, _ = w.Write(movie.BackImage) + qb := models.NewMovieQueryBuilder() + image, _ := qb.GetBackImage(movie.ID, nil) + + defaultParam := r.URL.Query().Get("default") + if len(image) == 0 || defaultParam == "true" { + _, image, _ = utils.ProcessBase64Image(models.DefaultMovieImage) + } + + utils.ServeImage(image, w, r) } func MovieCtx(next http.Handler) http.Handler { diff --git a/pkg/api/routes_performer.go b/pkg/api/routes_performer.go index 57958998b..d552b2e89 100644 --- a/pkg/api/routes_performer.go +++ b/pkg/api/routes_performer.go @@ -2,13 +2,12 @@ package api import ( "context" - "crypto/md5" - "fmt" - "github.com/go-chi/chi" - "github.com/stashapp/stash/pkg/models" "net/http" "strconv" - "strings" + + "github.com/go-chi/chi" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) type performerRoutes struct{} @@ -26,17 +25,15 @@ func (rs performerRoutes) Routes() chi.Router { func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) { performer := r.Context().Value(performerKey).(*models.Performer) - etag := fmt.Sprintf("%x", md5.Sum(performer.Image)) + qb := models.NewPerformerQueryBuilder() + image, _ := qb.GetPerformerImage(performer.ID, nil) - if match := r.Header.Get("If-None-Match"); match != "" { - if strings.Contains(match, etag) { - w.WriteHeader(http.StatusNotModified) - return - } + defaultParam := r.URL.Query().Get("default") + if len(image) == 0 || defaultParam == "true" { + image, _ = getRandomPerformerImageUsingName(performer.Name.String, performer.Gender.String) } - w.Header().Add("Etag", etag) - _, _ = w.Write(performer.Image) + utils.ServeImage(image, w, r) } func PerformerCtx(next http.Handler) http.Handler { diff --git a/pkg/api/routes_scene.go b/pkg/api/routes_scene.go index 171d868b4..8f2cdf9c6 100644 --- a/pkg/api/routes_scene.go +++ b/pkg/api/routes_scene.go @@ -2,9 +2,7 @@ package api import ( "context" - "io" "net/http" - "os" "strconv" "strings" @@ -24,8 +22,15 @@ func (rs sceneRoutes) Routes() chi.Router { r.Route("/{sceneId}", func(r chi.Router) { r.Use(SceneCtx) - r.Get("/stream", rs.Stream) - r.Get("/stream.mp4", rs.Stream) + + // streaming endpoints + r.Get("/stream", rs.StreamDirect) + r.Get("/stream.mkv", rs.StreamMKV) + r.Get("/stream.webm", rs.StreamWebM) + r.Get("/stream.m3u8", rs.StreamHLS) + r.Get("/stream.ts", rs.StreamTS) + r.Get("/stream.mp4", rs.StreamMp4) + r.Get("/screenshot", rs.Screenshot) r.Get("/preview", rs.Preview) r.Get("/webp", rs.Webp) @@ -42,41 +47,95 @@ func (rs sceneRoutes) Routes() chi.Router { // region Handlers -func (rs sceneRoutes) Stream(w http.ResponseWriter, r *http.Request) { - - scene := r.Context().Value(sceneKey).(*models.Scene) - - container := "" +func getSceneFileContainer(scene *models.Scene) ffmpeg.Container { + var container ffmpeg.Container if scene.Format.Valid { - container = scene.Format.String + container = ffmpeg.Container(scene.Format.String) } else { // container isn't in the DB // shouldn't happen, fallback to ffprobe tmpVideoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path) if err != nil { logger.Errorf("[transcode] error reading video file: %s", err.Error()) - return + return ffmpeg.Container("") } - container = string(ffmpeg.MatchContainer(tmpVideoFile.Container, scene.Path)) + container = ffmpeg.MatchContainer(tmpVideoFile.Container, scene.Path) } - // detect if not a streamable file and try to transcode it instead - filepath := manager.GetInstance().Paths.Scene.GetStreamPath(scene.Path, scene.Checksum) - videoCodec := scene.VideoCodec.String - audioCodec := ffmpeg.MissingUnsupported - if scene.AudioCodec.Valid { - audioCodec = ffmpeg.AudioCodec(scene.AudioCodec.String) - } - hasTranscode, _ := manager.HasTranscode(scene) - if ffmpeg.IsValidCodec(videoCodec) && ffmpeg.IsValidCombo(videoCodec, ffmpeg.Container(container)) && ffmpeg.IsValidAudioForContainer(audioCodec, ffmpeg.Container(container)) || hasTranscode { - manager.RegisterStream(filepath, &w) - http.ServeFile(w, r, filepath) - manager.WaitAndDeregisterStream(filepath, &w, r) + return container +} +func (rs sceneRoutes) StreamDirect(w http.ResponseWriter, r *http.Request) { + scene := r.Context().Value(sceneKey).(*models.Scene) + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + + filepath := manager.GetInstance().Paths.Scene.GetStreamPath(scene.Path, scene.GetHash(fileNamingAlgo)) + manager.RegisterStream(filepath, &w) + http.ServeFile(w, r, filepath) + manager.WaitAndDeregisterStream(filepath, &w, r) +} + +func (rs sceneRoutes) StreamMKV(w http.ResponseWriter, r *http.Request) { + // only allow mkv streaming if the scene container is an mkv already + scene := r.Context().Value(sceneKey).(*models.Scene) + + container := getSceneFileContainer(scene) + if container != ffmpeg.Matroska { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("not an mkv file")) return } + rs.streamTranscode(w, r, ffmpeg.CodecMKVAudio) +} + +func (rs sceneRoutes) StreamWebM(w http.ResponseWriter, r *http.Request) { + rs.streamTranscode(w, r, ffmpeg.CodecVP9) +} + +func (rs sceneRoutes) StreamMp4(w http.ResponseWriter, r *http.Request) { + rs.streamTranscode(w, r, ffmpeg.CodecH264) +} + +func (rs sceneRoutes) StreamHLS(w http.ResponseWriter, r *http.Request) { + scene := r.Context().Value(sceneKey).(*models.Scene) + + videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path) + if err != nil { + logger.Errorf("[stream] error reading video file: %s", err.Error()) + return + } + + logger.Debug("Returning HLS playlist") + + // getting the playlist manifest only + w.Header().Set("Content-Type", ffmpeg.MimeHLS) + var str strings.Builder + + ffmpeg.WriteHLSPlaylist(*videoFile, r.URL.String(), &str) + + requestByteRange := utils.CreateByteRange(r.Header.Get("Range")) + if requestByteRange.RawString != "" { + logger.Debugf("Requested range: %s", requestByteRange.RawString) + } + + ret := requestByteRange.Apply([]byte(str.String())) + rangeStr := requestByteRange.ToHeaderValue(int64(str.Len())) + w.Header().Set("Content-Range", rangeStr) + + w.Write(ret) +} + +func (rs sceneRoutes) StreamTS(w http.ResponseWriter, r *http.Request) { + rs.streamTranscode(w, r, ffmpeg.CodecHLS) +} + +func (rs sceneRoutes) streamTranscode(w http.ResponseWriter, r *http.Request, videoCodec ffmpeg.Codec) { + logger.Debugf("Streaming as %s", videoCodec.MimeType) + scene := r.Context().Value(sceneKey).(*models.Scene) + // needs to be transcoded + videoFile, err := ffmpeg.NewVideoFile(manager.GetInstance().FFProbePath, scene.Path) if err != nil { logger.Errorf("[stream] error reading video file: %s", err.Error()) @@ -87,82 +146,54 @@ func (rs sceneRoutes) Stream(w http.ResponseWriter, r *http.Request) { r.ParseForm() startTime := r.Form.Get("start") - encoder := ffmpeg.NewEncoder(manager.GetInstance().FFMPEGPath) + var stream *ffmpeg.Stream - var stream io.ReadCloser - var process *os.Process - mimeType := ffmpeg.MimeWebm - - if audioCodec == ffmpeg.MissingUnsupported { - //ffmpeg fails if it trys to transcode a non supported audio codec - stream, process, err = encoder.StreamTranscodeVideo(*videoFile, startTime, config.GetMaxStreamingTranscodeSize()) - } else { - copyVideo := false // try to be smart if the video to be transcoded is in a Matroska container - // mp4 has always supported audio so it doesn't need to be checked - // while mpeg_ts has seeking issues if we don't reencode the video - - if config.GetForceMKV() { // If MKV is forced as supported and video codec is also supported then only transcode audio - if ffmpeg.Container(container) == ffmpeg.Matroska { - switch videoCodec { - case ffmpeg.H264, ffmpeg.Vp9, ffmpeg.Vp8: - copyVideo = true - case ffmpeg.Hevc: - if config.GetForceHEVC() { - copyVideo = true - } - - } - } - } - - if copyVideo { // copy video stream instead of transcoding it - stream, process, err = encoder.StreamMkvTranscodeAudio(*videoFile, startTime, config.GetMaxStreamingTranscodeSize()) - mimeType = ffmpeg.MimeMkv - - } else { - stream, process, err = encoder.StreamTranscode(*videoFile, startTime, config.GetMaxStreamingTranscodeSize()) - } + audioCodec := ffmpeg.MissingUnsupported + if scene.AudioCodec.Valid { + audioCodec = ffmpeg.AudioCodec(scene.AudioCodec.String) } + options := ffmpeg.GetTranscodeStreamOptions(*videoFile, videoCodec, audioCodec) + options.StartTime = startTime + options.MaxTranscodeSize = config.GetMaxStreamingTranscodeSize() + + encoder := ffmpeg.NewEncoder(manager.GetInstance().FFMPEGPath) + stream, err = encoder.GetTranscodeStream(options) + if err != nil { logger.Errorf("[stream] error transcoding video file: %s", err.Error()) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", mimeType) - - logger.Infof("[stream] transcoding video file to %s", mimeType) - - // handle if client closes the connection - notify := r.Context().Done() - go func() { - <-notify - logger.Info("[stream] client closed the connection. Killing stream process.") - process.Kill() - }() - - _, err = io.Copy(w, stream) - if err != nil { - logger.Errorf("[stream] error serving transcoded video file: %s", err.Error()) - } + stream.Serve(w, r) } func (rs sceneRoutes) Screenshot(w http.ResponseWriter, r *http.Request) { scene := r.Context().Value(sceneKey).(*models.Scene) - filepath := manager.GetInstance().Paths.Scene.GetScreenshotPath(scene.Checksum) - http.ServeFile(w, r, filepath) + filepath := manager.GetInstance().Paths.Scene.GetScreenshotPath(scene.GetHash(config.GetVideoFileNamingAlgorithm())) + + // fall back to the scene image blob if the file isn't present + screenshotExists, _ := utils.FileExists(filepath) + if screenshotExists { + http.ServeFile(w, r, filepath) + } else { + qb := models.NewSceneQueryBuilder() + cover, _ := qb.GetSceneCover(scene.ID, nil) + utils.ServeImage(cover, w, r) + } } func (rs sceneRoutes) Preview(w http.ResponseWriter, r *http.Request) { scene := r.Context().Value(sceneKey).(*models.Scene) - filepath := manager.GetInstance().Paths.Scene.GetStreamPreviewPath(scene.Checksum) - http.ServeFile(w, r, filepath) + filepath := manager.GetInstance().Paths.Scene.GetStreamPreviewPath(scene.GetHash(config.GetVideoFileNamingAlgorithm())) + utils.ServeFileNoCache(w, r, filepath) } func (rs sceneRoutes) Webp(w http.ResponseWriter, r *http.Request) { scene := r.Context().Value(sceneKey).(*models.Scene) - filepath := manager.GetInstance().Paths.Scene.GetStreamPreviewImagePath(scene.Checksum) + filepath := manager.GetInstance().Paths.Scene.GetStreamPreviewImagePath(scene.GetHash(config.GetVideoFileNamingAlgorithm())) http.ServeFile(w, r, filepath) } @@ -218,14 +249,14 @@ func (rs sceneRoutes) ChapterVtt(w http.ResponseWriter, r *http.Request) { func (rs sceneRoutes) VttThumbs(w http.ResponseWriter, r *http.Request) { scene := r.Context().Value(sceneKey).(*models.Scene) w.Header().Set("Content-Type", "text/vtt") - filepath := manager.GetInstance().Paths.Scene.GetSpriteVttFilePath(scene.Checksum) + filepath := manager.GetInstance().Paths.Scene.GetSpriteVttFilePath(scene.GetHash(config.GetVideoFileNamingAlgorithm())) http.ServeFile(w, r, filepath) } func (rs sceneRoutes) VttSprite(w http.ResponseWriter, r *http.Request) { scene := r.Context().Value(sceneKey).(*models.Scene) w.Header().Set("Content-Type", "image/jpeg") - filepath := manager.GetInstance().Paths.Scene.GetSpriteImageFilePath(scene.Checksum) + filepath := manager.GetInstance().Paths.Scene.GetSpriteImageFilePath(scene.GetHash(config.GetVideoFileNamingAlgorithm())) http.ServeFile(w, r, filepath) } @@ -239,7 +270,7 @@ func (rs sceneRoutes) SceneMarkerStream(w http.ResponseWriter, r *http.Request) http.Error(w, http.StatusText(404), 404) return } - filepath := manager.GetInstance().Paths.SceneMarkers.GetStreamPath(scene.Checksum, int(sceneMarker.Seconds)) + filepath := manager.GetInstance().Paths.SceneMarkers.GetStreamPath(scene.GetHash(config.GetVideoFileNamingAlgorithm()), int(sceneMarker.Seconds)) http.ServeFile(w, r, filepath) } @@ -253,7 +284,7 @@ func (rs sceneRoutes) SceneMarkerPreview(w http.ResponseWriter, r *http.Request) http.Error(w, http.StatusText(404), 404) return } - filepath := manager.GetInstance().Paths.SceneMarkers.GetStreamPreviewImagePath(scene.Checksum, int(sceneMarker.Seconds)) + filepath := manager.GetInstance().Paths.SceneMarkers.GetStreamPreviewImagePath(scene.GetHash(config.GetVideoFileNamingAlgorithm()), int(sceneMarker.Seconds)) // If the image doesn't exist, send the placeholder exists, _ := utils.FileExists(filepath) @@ -275,15 +306,19 @@ func SceneCtx(next http.Handler) http.Handler { sceneID, _ := strconv.Atoi(sceneIdentifierQueryParam) var scene *models.Scene - var err error qb := models.NewSceneQueryBuilder() if sceneID == 0 { - scene, err = qb.FindByChecksum(sceneIdentifierQueryParam) + // determine checksum/os by the length of the query param + if len(sceneIdentifierQueryParam) == 32 { + scene, _ = qb.FindByChecksum(sceneIdentifierQueryParam) + } else { + scene, _ = qb.FindByOSHash(sceneIdentifierQueryParam) + } } else { - scene, err = qb.Find(sceneID) + scene, _ = qb.Find(sceneID) } - if err != nil { + if scene == nil { http.Error(w, http.StatusText(404), 404) return } diff --git a/pkg/api/routes_studio.go b/pkg/api/routes_studio.go index 22d0702c5..d1c2b51c9 100644 --- a/pkg/api/routes_studio.go +++ b/pkg/api/routes_studio.go @@ -2,13 +2,12 @@ package api import ( "context" - "crypto/md5" - "fmt" - "github.com/go-chi/chi" - "github.com/stashapp/stash/pkg/models" "net/http" "strconv" - "strings" + + "github.com/go-chi/chi" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) type studioRoutes struct{} @@ -26,22 +25,15 @@ func (rs studioRoutes) Routes() chi.Router { func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) { studio := r.Context().Value(studioKey).(*models.Studio) - etag := fmt.Sprintf("%x", md5.Sum(studio.Image)) - if match := r.Header.Get("If-None-Match"); match != "" { - if strings.Contains(match, etag) { - w.WriteHeader(http.StatusNotModified) - return - } + qb := models.NewStudioQueryBuilder() + image, _ := qb.GetStudioImage(studio.ID, nil) + + defaultParam := r.URL.Query().Get("default") + if len(image) == 0 || defaultParam == "true" { + _, image, _ = utils.ProcessBase64Image(models.DefaultStudioImage) } - contentType := http.DetectContentType(studio.Image) - if contentType == "text/xml; charset=utf-8" || contentType == "text/plain; charset=utf-8" { - contentType = "image/svg+xml" - } - - w.Header().Set("Content-Type", contentType) - w.Header().Add("Etag", etag) - _, _ = w.Write(studio.Image) + utils.ServeImage(image, w, r) } func StudioCtx(next http.Handler) http.Handler { diff --git a/pkg/api/routes_tag.go b/pkg/api/routes_tag.go new file mode 100644 index 000000000..0364eb95d --- /dev/null +++ b/pkg/api/routes_tag.go @@ -0,0 +1,58 @@ +package api + +import ( + "context" + "net/http" + "strconv" + + "github.com/go-chi/chi" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" +) + +type tagRoutes struct{} + +func (rs tagRoutes) Routes() chi.Router { + r := chi.NewRouter() + + r.Route("/{tagId}", func(r chi.Router) { + r.Use(TagCtx) + r.Get("/image", rs.Image) + }) + + return r +} + +func (rs tagRoutes) Image(w http.ResponseWriter, r *http.Request) { + tag := r.Context().Value(tagKey).(*models.Tag) + qb := models.NewTagQueryBuilder() + image, _ := qb.GetTagImage(tag.ID, nil) + + // use default image if not present + defaultParam := r.URL.Query().Get("default") + if len(image) == 0 || defaultParam == "true" { + image = models.DefaultTagImage + } + + utils.ServeImage(image, w, r) +} + +func TagCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tagID, err := strconv.Atoi(chi.URLParam(r, "tagId")) + if err != nil { + http.Error(w, http.StatusText(404), 404) + return + } + + qb := models.NewTagQueryBuilder() + tag, err := qb.Find(tagID, nil) + if err != nil { + http.Error(w, http.StatusText(404), 404) + return + } + + ctx := context.WithValue(r.Context(), tagKey, tag) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} diff --git a/pkg/api/server.go b/pkg/api/server.go index 9d3a038c3..aef48be8d 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -154,6 +154,7 @@ func Start() { r.Mount("/scene", sceneRoutes{}.Routes()) r.Mount("/studio", studioRoutes{}.Routes()) r.Mount("/movie", movieRoutes{}.Routes()) + r.Mount("/tag", tagRoutes{}.Routes()) r.HandleFunc("/css", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/css") @@ -251,6 +252,24 @@ func Start() { }) startThumbCache() + + // Serve static folders + customServedFolders := config.GetCustomServedFolders() + if customServedFolders != nil { + r.HandleFunc("/custom/*", func(w http.ResponseWriter, r *http.Request) { + r.URL.Path = strings.Replace(r.URL.Path, "/custom", "", 1) + + // map the path to the applicable filesystem location + var dir string + r.URL.Path, dir = customServedFolders.GetFilesystemLocation(r.URL.Path) + if dir != "" { + http.FileServer(http.Dir(dir)).ServeHTTP(w, r) + } else { + http.NotFound(w, r) + } + }) + } + // Serve the web app r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { ext := path.Ext(r.URL.Path) @@ -338,6 +357,15 @@ func makeTLSConfig() *tls.Config { return tlsConfig } +func HasTLSConfig() bool { + ret, _ := utils.FileExists(paths.GetSSLCert()) + if ret { + ret, _ = utils.FileExists(paths.GetSSLKey()) + } + + return ret +} + type contextKey struct { name string } diff --git a/pkg/api/session.go b/pkg/api/session.go index e619fd782..8be4876bd 100644 --- a/pkg/api/session.go +++ b/pkg/api/session.go @@ -1,12 +1,14 @@ package api import ( + "context" "fmt" "html/template" "net/http" "github.com/stashapp/stash/pkg/manager/config" + "github.com/gorilla/securecookie" "github.com/gorilla/sessions" ) @@ -125,3 +127,26 @@ func getSessionUserID(w http.ResponseWriter, r *http.Request) (string, error) { return "", nil } + +func getCurrentUserID(ctx context.Context) *string { + userCtxVal := ctx.Value(ContextUser) + if userCtxVal != nil { + currentUser := userCtxVal.(string) + return ¤tUser + } + + return nil +} + +func createSessionCookie(username string) (*http.Cookie, error) { + session := sessions.NewSession(sessionStore, cookieName) + session.Values[userIDKey] = username + + encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, + sessionStore.Codecs...) + if err != nil { + return nil, err + } + + return sessions.NewCookie(session.Name(), encoded, session.Options), nil +} diff --git a/pkg/api/urlbuilders/movie.go b/pkg/api/urlbuilders/movie.go index 7e454c070..6f7694b83 100644 --- a/pkg/api/urlbuilders/movie.go +++ b/pkg/api/urlbuilders/movie.go @@ -3,13 +3,13 @@ package urlbuilders import "strconv" type MovieURLBuilder struct { - BaseURL string + BaseURL string MovieID string } func NewMovieURLBuilder(baseURL string, movieID int) MovieURLBuilder { return MovieURLBuilder{ - BaseURL: baseURL, + BaseURL: baseURL, MovieID: strconv.Itoa(movieID), } } @@ -21,4 +21,3 @@ func (b MovieURLBuilder) GetMovieFrontImageURL() string { func (b MovieURLBuilder) GetMovieBackImageURL() string { return b.BaseURL + "/movie/" + b.MovieID + "/backimage" } - diff --git a/pkg/api/urlbuilders/scene.go b/pkg/api/urlbuilders/scene.go index b211793ca..57c50f3cf 100644 --- a/pkg/api/urlbuilders/scene.go +++ b/pkg/api/urlbuilders/scene.go @@ -18,7 +18,7 @@ func NewSceneURLBuilder(baseURL string, sceneID int) SceneURLBuilder { } func (b SceneURLBuilder) GetStreamURL() string { - return b.BaseURL + "/scene/" + b.SceneID + "/stream.mp4" + return b.BaseURL + "/scene/" + b.SceneID + "/stream" } func (b SceneURLBuilder) GetStreamPreviewURL() string { diff --git a/pkg/api/urlbuilders/tag.go b/pkg/api/urlbuilders/tag.go new file mode 100644 index 000000000..e3f9415c9 --- /dev/null +++ b/pkg/api/urlbuilders/tag.go @@ -0,0 +1,19 @@ +package urlbuilders + +import "strconv" + +type TagURLBuilder struct { + BaseURL string + TagID string +} + +func NewTagURLBuilder(baseURL string, tagID int) TagURLBuilder { + return TagURLBuilder{ + BaseURL: baseURL, + TagID: strconv.Itoa(tagID), + } +} + +func (b TagURLBuilder) GetTagImageURL() string { + return b.BaseURL + "/tag/" + b.TagID + "/image" +} diff --git a/pkg/database/database.go b/pkg/database/database.go index 63f6d9cb1..e02aaca91 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -19,7 +19,7 @@ import ( var DB *sqlx.DB var dbPath string -var appSchemaVersion uint = 8 +var appSchemaVersion uint = 12 var databaseSchemaVersion uint const sqlite3Driver = "sqlite3ex" @@ -29,7 +29,11 @@ func init() { registerCustomDriver() } -func Initialize(databasePath string) { +// Initialize initializes the database. If the database is new, then it +// performs a full migration to the latest schema version. Otherwise, any +// necessary migrations must be run separately using RunMigrations. +// Returns true if the database is new. +func Initialize(databasePath string) bool { dbPath = databasePath if err := getDatabaseSchemaVersion(); err != nil { @@ -42,7 +46,7 @@ func Initialize(databasePath string) { panic(err) } // RunMigrations calls Initialise. Just return - return + return true } else { if databaseSchemaVersion > appSchemaVersion { panic(fmt.Sprintf("Database schema version %d is incompatible with required schema version %d", databaseSchemaVersion, appSchemaVersion)) @@ -51,12 +55,14 @@ func Initialize(databasePath string) { // if migration is needed, then don't open the connection if NeedsMigration() { logger.Warnf("Database schema version %d does not match required schema version %d.", databaseSchemaVersion, appSchemaVersion) - return + return false } } const disableForeignKeys = false DB = open(databasePath, disableForeignKeys) + + return false } func open(databasePath string, disableForeignKeys bool) *sqlx.DB { @@ -100,6 +106,7 @@ func Backup(backupPath string) error { } defer db.Close() + logger.Infof("Backing up database into: %s", backupPath) _, err = db.Exec(`VACUUM INTO "` + backupPath + `"`) if err != nil { return fmt.Errorf("Vacuum failed: %s", err) @@ -109,6 +116,7 @@ func Backup(backupPath string) error { } func RestoreFromBackup(backupPath string) error { + logger.Infof("Restoring backup database %s into %s", backupPath, dbPath) return os.Rename(backupPath, dbPath) } @@ -177,18 +185,28 @@ func RunMigrations() error { databaseSchemaVersion, _, _ = m.Version() stepNumber := appSchemaVersion - databaseSchemaVersion if stepNumber != 0 { + logger.Infof("Migrating database from version %d to %d", databaseSchemaVersion, appSchemaVersion) err = m.Steps(int(stepNumber)) if err != nil { // migration failed + logger.Errorf("Error migrating database: %s", err.Error()) m.Close() return err } } + m.Close() // re-initialise the database Initialize(dbPath) + // run a vacuum on the database + logger.Info("Performing vacuum on database") + _, err = DB.Exec("VACUUM") + if err != nil { + logger.Warnf("error while performing post-migration vacuum: %s", err.Error()) + } + return nil } diff --git a/pkg/database/migrations/10_image_tables.up.sql b/pkg/database/migrations/10_image_tables.up.sql new file mode 100644 index 000000000..8480e573f --- /dev/null +++ b/pkg/database/migrations/10_image_tables.up.sql @@ -0,0 +1,514 @@ +-- recreate scenes, studios and performers tables +ALTER TABLE `studios` rename to `_studios_old`; +ALTER TABLE `scenes` rename to `_scenes_old`; +ALTER TABLE `performers` RENAME TO `_performers_old`; +ALTER TABLE `movies` rename to `_movies_old`; + +-- remove studio image +CREATE TABLE `studios` ( + `id` integer not null primary key autoincrement, + `checksum` varchar(255) not null, + `name` varchar(255), + `url` varchar(255), + `parent_id` integer DEFAULT NULL CHECK ( id IS NOT parent_id ) REFERENCES studios(id) on delete set null, + `created_at` datetime not null, + `updated_at` datetime not null +); + +DROP INDEX `studios_checksum_unique`; +DROP INDEX `index_studios_on_name`; +DROP INDEX `index_studios_on_checksum`; + +CREATE UNIQUE INDEX `studios_checksum_unique` on `studios` (`checksum`); +CREATE INDEX `index_studios_on_name` on `studios` (`name`); +CREATE INDEX `index_studios_on_checksum` on `studios` (`checksum`); + +-- remove scene cover +CREATE TABLE `scenes` ( + `id` integer not null primary key autoincrement, + `path` varchar(510) not null, + `checksum` varchar(255) not null, + `title` varchar(255), + `details` text, + `url` varchar(255), + `date` date, + `rating` tinyint, + `size` varchar(255), + `duration` float, + `video_codec` varchar(255), + `audio_codec` varchar(255), + `width` tinyint, + `height` tinyint, + `framerate` float, + `bitrate` integer, + `studio_id` integer, + `o_counter` tinyint not null default 0, + `format` varchar(255), + `created_at` datetime not null, + `updated_at` datetime not null, + -- changed from cascade delete + foreign key(`studio_id`) references `studios`(`id`) on delete SET NULL +); + +DROP INDEX IF EXISTS `scenes_path_unique`; +DROP INDEX IF EXISTS `scenes_checksum_unique`; +DROP INDEX IF EXISTS `index_scenes_on_studio_id`; + +CREATE UNIQUE INDEX `scenes_path_unique` on `scenes` (`path`); +CREATE UNIQUE INDEX `scenes_checksum_unique` on `scenes` (`checksum`); +CREATE INDEX `index_scenes_on_studio_id` on `scenes` (`studio_id`); + +-- remove performer image +CREATE TABLE `performers` ( + `id` integer not null primary key autoincrement, + `checksum` varchar(255) not null, + `name` varchar(255), + `gender` varchar(20), + `url` varchar(255), + `twitter` varchar(255), + `instagram` varchar(255), + `birthdate` date, + `ethnicity` varchar(255), + `country` varchar(255), + `eye_color` varchar(255), + `height` varchar(255), + `measurements` varchar(255), + `fake_tits` varchar(255), + `career_length` varchar(255), + `tattoos` varchar(255), + `piercings` varchar(255), + `aliases` varchar(255), + `favorite` boolean not null default '0', + `created_at` datetime not null, + `updated_at` datetime not null +); + +DROP INDEX `performers_checksum_unique`; +DROP INDEX `index_performers_on_name`; + +CREATE UNIQUE INDEX `performers_checksum_unique` on `performers` (`checksum`); +CREATE INDEX `index_performers_on_name` on `performers` (`name`); + +-- remove front_image and back_image +CREATE TABLE `movies` ( + `id` integer not null primary key autoincrement, + `name` varchar(255) not null, + `aliases` varchar(255), + `duration` integer, + `date` date, + `rating` tinyint, + `studio_id` integer, + `director` varchar(255), + `synopsis` text, + `checksum` varchar(255) not null, + `url` varchar(255), + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`studio_id`) references `studios`(`id`) on delete set null +); + +DROP INDEX `movies_name_unique`; +DROP INDEX `movies_checksum_unique`; +DROP INDEX `index_movies_on_studio_id`; + +CREATE UNIQUE INDEX `movies_name_unique` on `movies` (`name`); +CREATE UNIQUE INDEX `movies_checksum_unique` on `movies` (`checksum`); +CREATE INDEX `index_movies_on_studio_id` on `movies` (`studio_id`); + +-- recreate the tables referencing the above tables to correct their references +ALTER TABLE `galleries` rename to `_galleries_old`; +ALTER TABLE `performers_scenes` rename to `_performers_scenes_old`; +ALTER TABLE `scene_markers` rename to `_scene_markers_old`; +ALTER TABLE `scene_markers_tags` rename to `_scene_markers_tags_old`; +ALTER TABLE `scenes_tags` rename to `_scenes_tags_old`; +ALTER TABLE `movies_scenes` rename to `_movies_scenes_old`; +ALTER TABLE `scraped_items` rename to `_scraped_items_old`; + +CREATE TABLE `galleries` ( + `id` integer not null primary key autoincrement, + `path` varchar(510) not null, + `checksum` varchar(255) not null, + `scene_id` integer, + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX IF EXISTS `index_galleries_on_scene_id`; +DROP INDEX IF EXISTS `galleries_path_unique`; +DROP INDEX IF EXISTS `galleries_checksum_unique`; + +CREATE INDEX `index_galleries_on_scene_id` on `galleries` (`scene_id`); +CREATE UNIQUE INDEX `galleries_path_unique` on `galleries` (`path`); +CREATE UNIQUE INDEX `galleries_checksum_unique` on `galleries` (`checksum`); + +CREATE TABLE `performers_scenes` ( + `performer_id` integer, + `scene_id` integer, + foreign key(`performer_id`) references `performers`(`id`), + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX `index_performers_scenes_on_scene_id`; +DROP INDEX `index_performers_scenes_on_performer_id`; + +CREATE INDEX `index_performers_scenes_on_scene_id` on `performers_scenes` (`scene_id`); +CREATE INDEX `index_performers_scenes_on_performer_id` on `performers_scenes` (`performer_id`); + +CREATE TABLE `scene_markers` ( + `id` integer not null primary key autoincrement, + `title` varchar(255) not null, + `seconds` float not null, + `primary_tag_id` integer not null, + `scene_id` integer, + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`primary_tag_id`) references `tags`(`id`), + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX `index_scene_markers_on_scene_id`; +DROP INDEX `index_scene_markers_on_primary_tag_id`; + +CREATE INDEX `index_scene_markers_on_scene_id` on `scene_markers` (`scene_id`); +CREATE INDEX `index_scene_markers_on_primary_tag_id` on `scene_markers` (`primary_tag_id`); + +CREATE TABLE `scene_markers_tags` ( + `scene_marker_id` integer, + `tag_id` integer, + foreign key(`scene_marker_id`) references `scene_markers`(`id`) on delete CASCADE, + foreign key(`tag_id`) references `tags`(`id`) +); + +DROP INDEX `index_scene_markers_tags_on_tag_id`; +DROP INDEX `index_scene_markers_tags_on_scene_marker_id`; + +CREATE INDEX `index_scene_markers_tags_on_tag_id` on `scene_markers_tags` (`tag_id`); +CREATE INDEX `index_scene_markers_tags_on_scene_marker_id` on `scene_markers_tags` (`scene_marker_id`); + +CREATE TABLE `scenes_tags` ( + `scene_id` integer, + `tag_id` integer, + foreign key(`scene_id`) references `scenes`(`id`) on delete CASCADE, + foreign key(`tag_id`) references `tags`(`id`) +); + +DROP INDEX `index_scenes_tags_on_tag_id`; +DROP INDEX `index_scenes_tags_on_scene_id`; + +CREATE INDEX `index_scenes_tags_on_tag_id` on `scenes_tags` (`tag_id`); +CREATE INDEX `index_scenes_tags_on_scene_id` on `scenes_tags` (`scene_id`); + +CREATE TABLE `movies_scenes` ( + `movie_id` integer, + `scene_id` integer, + `scene_index` tinyint, + foreign key(`movie_id`) references `movies`(`id`) on delete cascade, + foreign key(`scene_id`) references `scenes`(`id`) on delete cascade +); + +DROP INDEX `index_movies_scenes_on_movie_id`; +DROP INDEX `index_movies_scenes_on_scene_id`; + +CREATE INDEX `index_movies_scenes_on_movie_id` on `movies_scenes` (`movie_id`); +CREATE INDEX `index_movies_scenes_on_scene_id` on `movies_scenes` (`scene_id`); + +-- remove movie_id since doesn't appear to be used +CREATE TABLE `scraped_items` ( + `id` integer not null primary key autoincrement, + `title` varchar(255), + `description` text, + `url` varchar(255), + `date` date, + `rating` varchar(255), + `tags` varchar(510), + `models` varchar(510), + `episode` integer, + `gallery_filename` varchar(255), + `gallery_url` varchar(510), + `video_filename` varchar(255), + `video_url` varchar(255), + `studio_id` integer, + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`studio_id`) references `studios`(`id`) +); + +DROP INDEX `index_scraped_items_on_studio_id`; + +CREATE INDEX `index_scraped_items_on_studio_id` on `scraped_items` (`studio_id`); + +-- now populate from the old tables +-- these tables are changed so require the full column def +INSERT INTO `studios` + ( + `id`, + `checksum`, + `name`, + `url`, + `parent_id`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `checksum`, + `name`, + `url`, + `parent_id`, + `created_at`, + `updated_at` + FROM `_studios_old`; + +INSERT INTO `scenes` + ( + `id`, + `path`, + `checksum`, + `title`, + `details`, + `url`, + `date`, + `rating`, + `size`, + `duration`, + `video_codec`, + `audio_codec`, + `width`, + `height`, + `framerate`, + `bitrate`, + `studio_id`, + `o_counter`, + `format`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `path`, + `checksum`, + `title`, + `details`, + `url`, + `date`, + `rating`, + `size`, + `duration`, + `video_codec`, + `audio_codec`, + `width`, + `height`, + `framerate`, + `bitrate`, + `studio_id`, + `o_counter`, + `format`, + `created_at`, + `updated_at` + FROM `_scenes_old`; + +INSERT INTO `performers` + ( + `id`, + `checksum`, + `name`, + `gender`, + `url`, + `twitter`, + `instagram`, + `birthdate`, + `ethnicity`, + `country`, + `eye_color`, + `height`, + `measurements`, + `fake_tits`, + `career_length`, + `tattoos`, + `piercings`, + `aliases`, + `favorite`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `checksum`, + `name`, + `gender`, + `url`, + `twitter`, + `instagram`, + `birthdate`, + `ethnicity`, + `country`, + `eye_color`, + `height`, + `measurements`, + `fake_tits`, + `career_length`, + `tattoos`, + `piercings`, + `aliases`, + `favorite`, + `created_at`, + `updated_at` + FROM `_performers_old`; + +INSERT INTO `movies` + ( + `id`, + `name`, + `aliases`, + `duration`, + `date`, + `rating`, + `studio_id`, + `director`, + `synopsis`, + `checksum`, + `url`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `name`, + `aliases`, + `duration`, + `date`, + `rating`, + `studio_id`, + `director`, + `synopsis`, + `checksum`, + `url`, + `created_at`, + `updated_at` + FROM `_movies_old`; + +INSERT INTO `scraped_items` + ( + `id`, + `title`, + `description`, + `url`, + `date`, + `rating`, + `tags`, + `models`, + `episode`, + `gallery_filename`, + `gallery_url`, + `video_filename`, + `video_url`, + `studio_id`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `title`, + `description`, + `url`, + `date`, + `rating`, + `tags`, + `models`, + `episode`, + `gallery_filename`, + `gallery_url`, + `video_filename`, + `video_url`, + `studio_id`, + `created_at`, + `updated_at` + FROM `_scraped_items_old`; + +-- these tables are a direct copy +INSERT INTO `galleries` SELECT * from `_galleries_old`; +INSERT INTO `performers_scenes` SELECT * from `_performers_scenes_old`; +INSERT INTO `scene_markers` SELECT * from `_scene_markers_old`; +INSERT INTO `scene_markers_tags` SELECT * from `_scene_markers_tags_old`; +INSERT INTO `scenes_tags` SELECT * from `_scenes_tags_old`; +INSERT INTO `movies_scenes` SELECT * from `_movies_scenes_old`; + +-- populate covers in separate table +CREATE TABLE `scenes_cover` ( + `scene_id` integer, + `cover` blob not null, + foreign key(`scene_id`) references `scenes`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `index_scene_covers_on_scene_id` on `scenes_cover` (`scene_id`); + +INSERT INTO `scenes_cover` + ( + `scene_id`, + `cover` + ) + SELECT `id`, `cover` from `_scenes_old` where `cover` is not null; + +-- put performer images in separate table +CREATE TABLE `performers_image` ( + `performer_id` integer, + `image` blob not null, + foreign key(`performer_id`) references `performers`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `index_performer_image_on_performer_id` on `performers_image` (`performer_id`); + +INSERT INTO `performers_image` + ( + `performer_id`, + `image` + ) + SELECT `id`, `image` from `_performers_old` where `image` is not null; + +-- put studio images in separate table +CREATE TABLE `studios_image` ( + `studio_id` integer, + `image` blob not null, + foreign key(`studio_id`) references `studios`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `index_studio_image_on_studio_id` on `studios_image` (`studio_id`); + +INSERT INTO `studios_image` + ( + `studio_id`, + `image` + ) + SELECT `id`, `image` from `_studios_old` where `image` is not null; + +-- put movie images in separate table +CREATE TABLE `movies_images` ( + `movie_id` integer, + `front_image` blob not null, + `back_image` blob, + foreign key(`movie_id`) references `movies`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `index_movie_images_on_movie_id` on `movies_images` (`movie_id`); + +INSERT INTO `movies_images` + ( + `movie_id`, + `front_image`, + `back_image` + ) + SELECT `id`, `front_image`, `back_image` from `_movies_old` where `front_image` is not null; + +-- drop old tables +DROP TABLE `_scenes_old`; +DROP TABLE `_studios_old`; +DROP TABLE `_performers_old`; +DROP TABLE `_movies_old`; +DROP TABLE `_galleries_old`; +DROP TABLE `_performers_scenes_old`; +DROP TABLE `_scene_markers_old`; +DROP TABLE `_scene_markers_tags_old`; +DROP TABLE `_scenes_tags_old`; +DROP TABLE `_movies_scenes_old`; +DROP TABLE `_scraped_items_old`; diff --git a/pkg/database/migrations/11_tag_image.up.sql b/pkg/database/migrations/11_tag_image.up.sql new file mode 100644 index 000000000..e40c969a9 --- /dev/null +++ b/pkg/database/migrations/11_tag_image.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE `tags_image` ( + `tag_id` integer, + `image` blob not null, + foreign key(`tag_id`) references `tags`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `index_tag_image_on_tag_id` on `tags_image` (`tag_id`); diff --git a/pkg/database/migrations/12_oshash.up.sql b/pkg/database/migrations/12_oshash.up.sql new file mode 100644 index 000000000..206553d10 --- /dev/null +++ b/pkg/database/migrations/12_oshash.up.sql @@ -0,0 +1,219 @@ + +-- need to change scenes.checksum to be nullable +ALTER TABLE `scenes` rename to `_scenes_old`; + +CREATE TABLE `scenes` ( + `id` integer not null primary key autoincrement, + `path` varchar(510) not null, + -- nullable + `checksum` varchar(255), + -- add oshash + `oshash` varchar(255), + `title` varchar(255), + `details` text, + `url` varchar(255), + `date` date, + `rating` tinyint, + `size` varchar(255), + `duration` float, + `video_codec` varchar(255), + `audio_codec` varchar(255), + `width` tinyint, + `height` tinyint, + `framerate` float, + `bitrate` integer, + `studio_id` integer, + `o_counter` tinyint not null default 0, + `format` varchar(255), + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`studio_id`) references `studios`(`id`) on delete SET NULL, + -- add check to ensure at least one hash is set + CHECK (`checksum` is not null or `oshash` is not null) +); + +DROP INDEX IF EXISTS `scenes_path_unique`; +DROP INDEX IF EXISTS `scenes_checksum_unique`; +DROP INDEX IF EXISTS `index_scenes_on_studio_id`; + +CREATE UNIQUE INDEX `scenes_path_unique` on `scenes` (`path`); +CREATE UNIQUE INDEX `scenes_checksum_unique` on `scenes` (`checksum`); +CREATE UNIQUE INDEX `scenes_oshash_unique` on `scenes` (`oshash`); +CREATE INDEX `index_scenes_on_studio_id` on `scenes` (`studio_id`); + +-- recreate the tables referencing scenes to correct their references +ALTER TABLE `galleries` rename to `_galleries_old`; +ALTER TABLE `performers_scenes` rename to `_performers_scenes_old`; +ALTER TABLE `scene_markers` rename to `_scene_markers_old`; +ALTER TABLE `scene_markers_tags` rename to `_scene_markers_tags_old`; +ALTER TABLE `scenes_tags` rename to `_scenes_tags_old`; +ALTER TABLE `movies_scenes` rename to `_movies_scenes_old`; +ALTER TABLE `scenes_cover` rename to `_scenes_cover_old`; + +CREATE TABLE `galleries` ( + `id` integer not null primary key autoincrement, + `path` varchar(510) not null, + `checksum` varchar(255) not null, + `scene_id` integer, + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX IF EXISTS `index_galleries_on_scene_id`; +DROP INDEX IF EXISTS `galleries_path_unique`; +DROP INDEX IF EXISTS `galleries_checksum_unique`; + +CREATE INDEX `index_galleries_on_scene_id` on `galleries` (`scene_id`); +CREATE UNIQUE INDEX `galleries_path_unique` on `galleries` (`path`); +CREATE UNIQUE INDEX `galleries_checksum_unique` on `galleries` (`checksum`); + +CREATE TABLE `performers_scenes` ( + `performer_id` integer, + `scene_id` integer, + foreign key(`performer_id`) references `performers`(`id`), + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX `index_performers_scenes_on_scene_id`; +DROP INDEX `index_performers_scenes_on_performer_id`; + +CREATE INDEX `index_performers_scenes_on_scene_id` on `performers_scenes` (`scene_id`); +CREATE INDEX `index_performers_scenes_on_performer_id` on `performers_scenes` (`performer_id`); + +CREATE TABLE `scene_markers` ( + `id` integer not null primary key autoincrement, + `title` varchar(255) not null, + `seconds` float not null, + `primary_tag_id` integer not null, + `scene_id` integer, + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`primary_tag_id`) references `tags`(`id`), + foreign key(`scene_id`) references `scenes`(`id`) +); + +DROP INDEX `index_scene_markers_on_scene_id`; +DROP INDEX `index_scene_markers_on_primary_tag_id`; + +CREATE INDEX `index_scene_markers_on_scene_id` on `scene_markers` (`scene_id`); +CREATE INDEX `index_scene_markers_on_primary_tag_id` on `scene_markers` (`primary_tag_id`); + +CREATE TABLE `scene_markers_tags` ( + `scene_marker_id` integer, + `tag_id` integer, + foreign key(`scene_marker_id`) references `scene_markers`(`id`) on delete CASCADE, + foreign key(`tag_id`) references `tags`(`id`) +); + +DROP INDEX `index_scene_markers_tags_on_tag_id`; +DROP INDEX `index_scene_markers_tags_on_scene_marker_id`; + +CREATE INDEX `index_scene_markers_tags_on_tag_id` on `scene_markers_tags` (`tag_id`); +CREATE INDEX `index_scene_markers_tags_on_scene_marker_id` on `scene_markers_tags` (`scene_marker_id`); + +CREATE TABLE `scenes_tags` ( + `scene_id` integer, + `tag_id` integer, + foreign key(`scene_id`) references `scenes`(`id`) on delete CASCADE, + foreign key(`tag_id`) references `tags`(`id`) +); + +DROP INDEX `index_scenes_tags_on_tag_id`; +DROP INDEX `index_scenes_tags_on_scene_id`; + +CREATE INDEX `index_scenes_tags_on_tag_id` on `scenes_tags` (`tag_id`); +CREATE INDEX `index_scenes_tags_on_scene_id` on `scenes_tags` (`scene_id`); + +CREATE TABLE `movies_scenes` ( + `movie_id` integer, + `scene_id` integer, + `scene_index` tinyint, + foreign key(`movie_id`) references `movies`(`id`) on delete cascade, + foreign key(`scene_id`) references `scenes`(`id`) on delete cascade +); + +DROP INDEX `index_movies_scenes_on_movie_id`; +DROP INDEX `index_movies_scenes_on_scene_id`; + +CREATE INDEX `index_movies_scenes_on_movie_id` on `movies_scenes` (`movie_id`); +CREATE INDEX `index_movies_scenes_on_scene_id` on `movies_scenes` (`scene_id`); + +CREATE TABLE `scenes_cover` ( + `scene_id` integer, + `cover` blob not null, + foreign key(`scene_id`) references `scenes`(`id`) on delete CASCADE +); + +DROP INDEX `index_scene_covers_on_scene_id`; + +CREATE UNIQUE INDEX `index_scene_covers_on_scene_id` on `scenes_cover` (`scene_id`); + +-- now populate from the old tables +-- these tables are changed so require the full column def +INSERT INTO `scenes` + ( + `id`, + `path`, + `checksum`, + `title`, + `details`, + `url`, + `date`, + `rating`, + `size`, + `duration`, + `video_codec`, + `audio_codec`, + `width`, + `height`, + `framerate`, + `bitrate`, + `studio_id`, + `o_counter`, + `format`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `path`, + `checksum`, + `title`, + `details`, + `url`, + `date`, + `rating`, + `size`, + `duration`, + `video_codec`, + `audio_codec`, + `width`, + `height`, + `framerate`, + `bitrate`, + `studio_id`, + `o_counter`, + `format`, + `created_at`, + `updated_at` + FROM `_scenes_old`; + +-- these tables are a direct copy +INSERT INTO `galleries` SELECT * from `_galleries_old`; +INSERT INTO `performers_scenes` SELECT * from `_performers_scenes_old`; +INSERT INTO `scene_markers` SELECT * from `_scene_markers_old`; +INSERT INTO `scene_markers_tags` SELECT * from `_scene_markers_tags_old`; +INSERT INTO `scenes_tags` SELECT * from `_scenes_tags_old`; +INSERT INTO `movies_scenes` SELECT * from `_movies_scenes_old`; +INSERT INTO `scenes_cover` SELECT * from `_scenes_cover_old`; + +-- drop old tables +DROP TABLE `_scenes_old`; +DROP TABLE `_galleries_old`; +DROP TABLE `_performers_scenes_old`; +DROP TABLE `_scene_markers_old`; +DROP TABLE `_scene_markers_tags_old`; +DROP TABLE `_scenes_tags_old`; +DROP TABLE `_movies_scenes_old`; +DROP TABLE `_scenes_cover_old`; diff --git a/pkg/database/migrations/9_studios_parent_studio.up.sql b/pkg/database/migrations/9_studios_parent_studio.up.sql new file mode 100644 index 000000000..38ca11350 --- /dev/null +++ b/pkg/database/migrations/9_studios_parent_studio.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE studios + ADD COLUMN parent_id INTEGER DEFAULT NULL CHECK ( id IS NOT parent_id ) REFERENCES studios(id) on delete set null; + CREATE INDEX index_studios_on_parent_id on studios (parent_id); \ No newline at end of file diff --git a/pkg/database/packr_source.go b/pkg/database/packr_source.go index c7dac9b6c..284e33bf6 100644 --- a/pkg/database/packr_source.go +++ b/pkg/database/packr_source.go @@ -3,12 +3,13 @@ package database import ( "bytes" "fmt" - "github.com/gobuffalo/packr/v2" - "github.com/golang-migrate/migrate/v4" - "github.com/golang-migrate/migrate/v4/source" "io" "io/ioutil" "os" + + "github.com/gobuffalo/packr/v2" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/source" ) type Packr2Source struct { @@ -72,7 +73,7 @@ func (s *Packr2Source) ReadUp(version uint) (r io.ReadCloser, identifier string, if migration, ok := s.Migrations.Up(version); !ok { return nil, "", os.ErrNotExist } else { - b := s.Box.Bytes(migration.Raw) + b, _ := s.Box.Find(migration.Raw) return ioutil.NopCloser(bytes.NewBuffer(b)), migration.Identifier, nil @@ -83,7 +84,7 @@ func (s *Packr2Source) ReadDown(version uint) (r io.ReadCloser, identifier strin if migration, ok := s.Migrations.Down(version); !ok { return nil, "", migrate.ErrNilVersion } else { - b := s.Box.Bytes(migration.Raw) + b, _ := s.Box.Find(migration.Raw) return ioutil.NopCloser(bytes.NewBuffer(b)), migration.Identifier, nil diff --git a/pkg/ffmpeg/encoder.go b/pkg/ffmpeg/encoder.go index 46711d146..5beb09410 100644 --- a/pkg/ffmpeg/encoder.go +++ b/pkg/ffmpeg/encoder.go @@ -2,7 +2,6 @@ package ffmpeg import ( "fmt" - "io" "io/ioutil" "os" "os/exec" @@ -101,6 +100,7 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) { } buf := make([]byte, 80) + lastProgress := 0.0 var errBuilder strings.Builder for { n, err := stderr.Read(buf) @@ -109,7 +109,11 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) { time := GetTimeFromRegex(data) if time > 0 && probeResult.Duration > 0 { progress := time / probeResult.Duration - logger.Infof("Progress %.2f", progress) + + if progress > lastProgress+0.01 { + logger.Infof("Progress %.2f", progress) + lastProgress = progress + } } errBuilder.WriteString(data) @@ -133,21 +137,3 @@ func (e *Encoder) run(probeResult VideoFile, args []string) (string, error) { return stdoutString, nil } - -func (e *Encoder) stream(probeResult VideoFile, args []string) (io.ReadCloser, *os.Process, error) { - cmd := exec.Command(e.Path, args...) - - stdout, err := cmd.StdoutPipe() - if nil != err { - logger.Error("FFMPEG stdout not available: " + err.Error()) - } - - if err = cmd.Start(); err != nil { - return nil, nil, err - } - - registerRunningEncoder(probeResult.Path, cmd.Process) - go waitAndDeregister(probeResult.Path, cmd) - - return stdout, cmd.Process, nil -} diff --git a/pkg/ffmpeg/encoder_scene_preview_chunk.go b/pkg/ffmpeg/encoder_scene_preview_chunk.go index 4f6a8a6fd..3251e8e44 100644 --- a/pkg/ffmpeg/encoder_scene_preview_chunk.go +++ b/pkg/ffmpeg/encoder_scene_preview_chunk.go @@ -8,18 +8,58 @@ import ( ) type ScenePreviewChunkOptions struct { - Time int + StartTime float64 + Duration float64 Width int OutputPath string } -func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePreviewChunkOptions, preset string) { +func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePreviewChunkOptions, preset string, fallback bool) error { + var fastSeek float64 + var slowSeek float64 + fallbackMinSlowSeek := 20.0 + args := []string{ "-v", "error", - "-xerror", - "-ss", strconv.Itoa(options.Time), - "-i", probeResult.Path, - "-t", "0.75", + } + + // Non-fallback: enable xerror. + // "-xerror" causes ffmpeg to fail on warnings, often the preview is fine but could be broken. + if !fallback { + args = append(args, "-xerror") + fastSeek = options.StartTime + slowSeek = 0 + } else { + // In fallback mode, disable "-xerror" and try a combination of fast/slow seek instead of just fastseek + // Commonly with avi/wmv ffmpeg doesn't seem to always predict the right start point to begin decoding when + // using fast seek. If you force ffmpeg to decode more, it avoids the "blocky green artifact" issue. + if options.StartTime > fallbackMinSlowSeek { + // Handle seeks longer than fallbackMinSlowSeek with fast/slow seeks + // Allow for at least fallbackMinSlowSeek seconds of slow seek + fastSeek = options.StartTime - fallbackMinSlowSeek + slowSeek = fallbackMinSlowSeek + } else { + // Handle seeks shorter than fallbackMinSlowSeek with only slow seeks. + slowSeek = options.StartTime + fastSeek = 0 + } + } + + if fastSeek > 0 { + args = append(args, "-ss") + args = append(args, strconv.FormatFloat(fastSeek, 'f', 2, 64)) + } + + args = append(args, "-i") + args = append(args, probeResult.Path) + + if slowSeek > 0 { + args = append(args, "-ss") + args = append(args, strconv.FormatFloat(slowSeek, 'f', 2, 64)) + } + + args2 := []string{ + "-t", strconv.FormatFloat(options.Duration, 'f', 2, 64), "-max_muxing_queue_size", "1024", // https://trac.ffmpeg.org/ticket/6375 "-y", "-c:v", "libx264", @@ -35,10 +75,14 @@ func (e *Encoder) ScenePreviewVideoChunk(probeResult VideoFile, options ScenePre "-strict", "-2", options.OutputPath, } - _, _ = e.run(probeResult, args) + + finalArgs := append(args, args2...) + + _, err := e.run(probeResult, finalArgs) + return err } -func (e *Encoder) ScenePreviewVideoChunkCombine(probeResult VideoFile, concatFilePath string, outputPath string) { +func (e *Encoder) ScenePreviewVideoChunkCombine(probeResult VideoFile, concatFilePath string, outputPath string) error { args := []string{ "-v", "error", "-f", "concat", @@ -47,7 +91,8 @@ func (e *Encoder) ScenePreviewVideoChunkCombine(probeResult VideoFile, concatFil "-c", "copy", outputPath, } - _, _ = e.run(probeResult, args) + _, err := e.run(probeResult, args) + return err } func (e *Encoder) ScenePreviewVideoToImage(probeResult VideoFile, width int, videoPreviewPath string, outputPath string) error { diff --git a/pkg/ffmpeg/encoder_screenshot.go b/pkg/ffmpeg/encoder_screenshot.go index bd1926ba3..bd52273c8 100644 --- a/pkg/ffmpeg/encoder_screenshot.go +++ b/pkg/ffmpeg/encoder_screenshot.go @@ -21,7 +21,7 @@ func (e *Encoder) Screenshot(probeResult VideoFile, options ScreenshotOptions) e "-v", options.Verbosity, "-ss", fmt.Sprintf("%v", options.Time), "-y", - "-i", probeResult.Path, // TODO: Wrap in quotes? + "-i", probeResult.Path, "-vframes", "1", "-q:v", fmt.Sprintf("%v", options.Quality), "-vf", fmt.Sprintf("scale=%v:-1", options.Width), diff --git a/pkg/ffmpeg/encoder_transcode.go b/pkg/ffmpeg/encoder_transcode.go index 12b596396..1349bac70 100644 --- a/pkg/ffmpeg/encoder_transcode.go +++ b/pkg/ffmpeg/encoder_transcode.go @@ -1,8 +1,6 @@ package ffmpeg import ( - "io" - "os" "strconv" "github.com/stashapp/stash/pkg/models" @@ -111,77 +109,3 @@ func (e *Encoder) CopyVideo(probeResult VideoFile, options TranscodeOptions) { } _, _ = e.run(probeResult, args) } - -func (e *Encoder) StreamTranscode(probeResult VideoFile, startTime string, maxTranscodeSize models.StreamingResolutionEnum) (io.ReadCloser, *os.Process, error) { - scale := calculateTranscodeScale(probeResult, maxTranscodeSize) - args := []string{} - - if startTime != "" { - args = append(args, "-ss", startTime) - } - - args = append(args, - "-i", probeResult.Path, - "-c:v", "libvpx-vp9", - "-vf", "scale="+scale, - "-deadline", "realtime", - "-cpu-used", "5", - "-row-mt", "1", - "-crf", "30", - "-b:v", "0", - "-f", "webm", - "pipe:", - ) - - return e.stream(probeResult, args) -} - -//transcode the video, remove the audio -//in some videos where the audio codec is not supported by ffmpeg -//ffmpeg fails if you try to transcode the audio -func (e *Encoder) StreamTranscodeVideo(probeResult VideoFile, startTime string, maxTranscodeSize models.StreamingResolutionEnum) (io.ReadCloser, *os.Process, error) { - scale := calculateTranscodeScale(probeResult, maxTranscodeSize) - args := []string{} - - if startTime != "" { - args = append(args, "-ss", startTime) - } - - args = append(args, - "-i", probeResult.Path, - "-an", - "-c:v", "libvpx-vp9", - "-vf", "scale="+scale, - "-deadline", "realtime", - "-cpu-used", "5", - "-row-mt", "1", - "-crf", "30", - "-b:v", "0", - "-f", "webm", - "pipe:", - ) - - return e.stream(probeResult, args) -} - -//it is very common in MKVs to have just the audio codec unsupported -//copy the video stream, transcode the audio and serve as Matroska -func (e *Encoder) StreamMkvTranscodeAudio(probeResult VideoFile, startTime string, maxTranscodeSize models.StreamingResolutionEnum) (io.ReadCloser, *os.Process, error) { - args := []string{} - - if startTime != "" { - args = append(args, "-ss", startTime) - } - - args = append(args, - "-i", probeResult.Path, - "-c:v", "copy", - "-c:a", "libopus", - "-b:a", "96k", - "-vbr", "on", - "-f", "matroska", - "pipe:", - ) - - return e.stream(probeResult, args) -} diff --git a/pkg/ffmpeg/ffprobe.go b/pkg/ffmpeg/ffprobe.go index e309619e3..bc590771b 100644 --- a/pkg/ffmpeg/ffprobe.go +++ b/pkg/ffmpeg/ffprobe.go @@ -12,7 +12,6 @@ import ( "time" "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/manager/config" ) type Container string @@ -47,11 +46,17 @@ const ( Hevc string = "hevc" Vp8 string = "vp8" Vp9 string = "vp9" + Mkv string = "mkv" // only used from the browser to indicate mkv support + Hls string = "hls" // only used from the browser to indicate hls support MimeWebm string = "video/webm" MimeMkv string = "video/x-matroska" + MimeMp4 string = "video/mp4" + MimeHLS string = "application/vnd.apple.mpegurl" + MimeMpegts string = "video/MP2T" ) -var ValidCodecs = []string{H264, H265, Vp8, Vp9} +// only support H264 by default, since Safari does not support VP8/VP9 +var DefaultSupportedCodecs = []string{H264, H265} var validForH264Mkv = []Container{Mp4, Matroska} var validForH264 = []Container{Mp4} @@ -102,15 +107,8 @@ func MatchContainer(format string, filePath string) Container { // match ffprobe return container } -func IsValidCodec(codecName string) bool { - forceHEVC := config.GetForceHEVC() - if forceHEVC { - if codecName == Hevc { - return true - } - } - - for _, c := range ValidCodecs { +func IsValidCodec(codecName string, supportedCodecs []string) bool { + for _, c := range supportedCodecs { if c == codecName { return true } @@ -158,30 +156,31 @@ func IsValidForContainer(format Container, validContainers []Container) bool { } //extend stream validation check to take into account container -func IsValidCombo(codecName string, format Container) bool { - forceMKV := config.GetForceMKV() - forceHEVC := config.GetForceHEVC() +func IsValidCombo(codecName string, format Container, supportedVideoCodecs []string) bool { + supportMKV := IsValidCodec(Mkv, supportedVideoCodecs) + supportHEVC := IsValidCodec(Hevc, supportedVideoCodecs) + switch codecName { case H264: - if forceMKV { + if supportMKV { return IsValidForContainer(format, validForH264Mkv) } return IsValidForContainer(format, validForH264) case H265: - if forceMKV { + if supportMKV { return IsValidForContainer(format, validForH265Mkv) } return IsValidForContainer(format, validForH265) case Vp8: return IsValidForContainer(format, validForVp8) case Vp9: - if forceMKV { + if supportMKV { return IsValidForContainer(format, validForVp9Mkv) } return IsValidForContainer(format, validForVp9) case Hevc: - if forceHEVC { - if forceMKV { + if supportHEVC { + if supportMKV { return IsValidForContainer(format, validForHevcMkv) } return IsValidForContainer(format, validForHevc) @@ -190,6 +189,13 @@ func IsValidCombo(codecName string, format Container) bool { return false } +func IsStreamable(videoCodec string, audioCodec AudioCodec, container Container) bool { + supportedVideoCodecs := DefaultSupportedCodecs + + // check if the video codec matches the supported codecs + return IsValidCodec(videoCodec, supportedVideoCodecs) && IsValidCombo(videoCodec, container, supportedVideoCodecs) && IsValidAudioForContainer(audioCodec, container) +} + type VideoFile struct { JSON FFProbeJSON AudioStream *FFProbeStream diff --git a/pkg/ffmpeg/hls.go b/pkg/ffmpeg/hls.go new file mode 100644 index 000000000..4ac5788ec --- /dev/null +++ b/pkg/ffmpeg/hls.go @@ -0,0 +1,42 @@ +package ffmpeg + +import ( + "fmt" + "io" + "strings" +) + +const hlsSegmentLength = 10.0 + +func WriteHLSPlaylist(probeResult VideoFile, baseUrl string, w io.Writer) { + fmt.Fprint(w, "#EXTM3U\n") + fmt.Fprint(w, "#EXT-X-VERSION:3\n") + fmt.Fprint(w, "#EXT-X-MEDIA-SEQUENCE:0\n") + fmt.Fprint(w, "#EXT-X-ALLOW-CACHE:YES\n") + fmt.Fprintf(w, "#EXT-X-TARGETDURATION:%d\n", int(hlsSegmentLength)) + fmt.Fprint(w, "#EXT-X-PLAYLIST-TYPE:VOD\n") + + duration := probeResult.Duration + + leftover := duration + upTo := 0.0 + + tsURL := baseUrl + i := strings.LastIndex(baseUrl, ".m3u8") + tsURL = baseUrl[0:i] + ".ts" + + for leftover > 0 { + thisLength := hlsSegmentLength + if leftover < thisLength { + thisLength = leftover + } + + fmt.Fprintf(w, "#EXTINF: %f,\n", thisLength) + fmt.Fprintf(w, "%s?start=%f\n", tsURL, upTo) + + leftover -= thisLength + upTo += thisLength + } + + fmt.Fprint(w, "#EXT-X-ENDLIST\n") +} diff --git a/pkg/ffmpeg/stream.go b/pkg/ffmpeg/stream.go new file mode 100644 index 000000000..6af646500 --- /dev/null +++ b/pkg/ffmpeg/stream.go @@ -0,0 +1,245 @@ +package ffmpeg + +import ( + "io" + "io/ioutil" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" +) + +const CopyStreamCodec = "copy" + +type Stream struct { + Stdout io.ReadCloser + Process *os.Process + options TranscodeStreamOptions + mimeType string +} + +func (s *Stream) Serve(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", s.mimeType) + w.WriteHeader(http.StatusOK) + + logger.Infof("[stream] transcoding video file to %s", s.mimeType) + + // handle if client closes the connection + notify := r.Context().Done() + go func() { + <-notify + s.Process.Kill() + }() + + _, err := io.Copy(w, s.Stdout) + if err != nil { + logger.Errorf("[stream] error serving transcoded video file: %s", err.Error()) + } +} + +type Codec struct { + Codec string + format string + MimeType string + extraArgs []string + hls bool +} + +var CodecHLS = Codec{ + Codec: "libx264", + format: "mpegts", + MimeType: MimeMpegts, + extraArgs: []string{ + "-acodec", "aac", + "-pix_fmt", "yuv420p", + "-preset", "veryfast", + "-crf", "25", + }, + hls: true, +} + +var CodecH264 = Codec{ + Codec: "libx264", + format: "mp4", + MimeType: MimeMp4, + extraArgs: []string{ + "-movflags", "frag_keyframe", + "-pix_fmt", "yuv420p", + "-preset", "veryfast", + "-crf", "25", + }, +} + +var CodecVP9 = Codec{ + Codec: "libvpx-vp9", + format: "webm", + MimeType: MimeWebm, + extraArgs: []string{ + "-deadline", "realtime", + "-cpu-used", "5", + "-row-mt", "1", + "-crf", "30", + "-b:v", "0", + }, +} + +var CodecVP8 = Codec{ + Codec: "libvpx", + format: "webm", + MimeType: MimeWebm, + extraArgs: []string{ + "-deadline", "realtime", + "-cpu-used", "5", + "-crf", "12", + "-b:v", "3M", + "-pix_fmt", "yuv420p", + }, +} + +var CodecHEVC = Codec{ + Codec: "libx265", + format: "mp4", + MimeType: MimeMp4, + extraArgs: []string{ + "-movflags", "frag_keyframe", + "-preset", "veryfast", + "-crf", "30", + }, +} + +// it is very common in MKVs to have just the audio codec unsupported +// copy the video stream, transcode the audio and serve as Matroska +var CodecMKVAudio = Codec{ + Codec: CopyStreamCodec, + format: "matroska", + MimeType: MimeMkv, + extraArgs: []string{ + "-c:a", "libopus", + "-b:a", "96k", + "-vbr", "on", + }, +} + +type TranscodeStreamOptions struct { + ProbeResult VideoFile + Codec Codec + StartTime string + MaxTranscodeSize models.StreamingResolutionEnum + // transcode the video, remove the audio + // in some videos where the audio codec is not supported by ffmpeg + // ffmpeg fails if you try to transcode the audio + VideoOnly bool +} + +func GetTranscodeStreamOptions(probeResult VideoFile, videoCodec Codec, audioCodec AudioCodec) TranscodeStreamOptions { + options := TranscodeStreamOptions{ + ProbeResult: probeResult, + Codec: videoCodec, + } + + if audioCodec == MissingUnsupported { + // ffmpeg fails if it trys to transcode a non supported audio codec + options.VideoOnly = true + } + + return options +} + +func (o TranscodeStreamOptions) getStreamArgs() []string { + args := []string{ + "-hide_banner", + "-v", "error", + } + + if o.StartTime != "" { + args = append(args, "-ss", o.StartTime) + } + + if o.Codec.hls { + // we only serve a fixed segment length + args = append(args, "-t", strconv.Itoa(int(hlsSegmentLength))) + } + + args = append(args, + "-i", o.ProbeResult.Path, + ) + + if o.VideoOnly { + args = append(args, "-an") + } + + args = append(args, + "-c:v", o.Codec.Codec, + ) + + // don't set scale when copying video stream + if o.Codec.Codec != CopyStreamCodec { + scale := calculateTranscodeScale(o.ProbeResult, o.MaxTranscodeSize) + args = append(args, + "-vf", "scale="+scale, + ) + } + + if len(o.Codec.extraArgs) > 0 { + args = append(args, o.Codec.extraArgs...) + } + + args = append(args, + // this is needed for 5-channel ac3 files + "-ac", "2", + "-f", o.Codec.format, + "pipe:", + ) + + return args +} + +func (e *Encoder) GetTranscodeStream(options TranscodeStreamOptions) (*Stream, error) { + return e.stream(options.ProbeResult, options) +} + +func (e *Encoder) stream(probeResult VideoFile, options TranscodeStreamOptions) (*Stream, error) { + args := options.getStreamArgs() + cmd := exec.Command(e.Path, args...) + logger.Debugf("Streaming via: %s", strings.Join(cmd.Args, " ")) + + stdout, err := cmd.StdoutPipe() + if nil != err { + logger.Error("FFMPEG stdout not available: " + err.Error()) + return nil, err + } + + stderr, err := cmd.StderrPipe() + if nil != err { + logger.Error("FFMPEG stderr not available: " + err.Error()) + return nil, err + } + + if err = cmd.Start(); err != nil { + return nil, err + } + + registerRunningEncoder(probeResult.Path, cmd.Process) + go waitAndDeregister(probeResult.Path, cmd) + + // stderr must be consumed or the process deadlocks + go func() { + stderrData, _ := ioutil.ReadAll(stderr) + stderrString := string(stderrData) + if len(stderrString) > 0 { + logger.Debugf("[stream] ffmpeg stderr: %s", stderrString) + } + }() + + ret := &Stream{ + Stdout: stdout, + Process: cmd.Process, + options: options, + mimeType: options.Codec.MimeType, + } + return ret, nil +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 72fae7616..a01eb1629 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -65,6 +65,8 @@ func logLevelFromString(level string) logrus.Level { ret = logrus.WarnLevel } else if level == "Error" { ret = logrus.ErrorLevel + } else if level == "Trace" { + ret = logrus.TraceLevel } return ret @@ -178,6 +180,15 @@ func Trace(args ...interface{}) { logger.Trace(args...) } +func Tracef(format string, args ...interface{}) { + logger.Tracef(format, args...) + l := &LogItem{ + Type: "trace", + Message: fmt.Sprintf(format, args...), + } + addLogItem(l) +} + func Debug(args ...interface{}) { logger.Debug(args...) l := &LogItem{ diff --git a/pkg/manager/checksum.go b/pkg/manager/checksum.go new file mode 100644 index 000000000..87fd47962 --- /dev/null +++ b/pkg/manager/checksum.go @@ -0,0 +1,70 @@ +package manager + +import ( + "errors" + + "github.com/spf13/viper" + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/manager/config" + "github.com/stashapp/stash/pkg/models" +) + +func setInitialMD5Config() { + // if there are no scene files in the database, then default the + // VideoFileNamingAlgorithm config setting to oshash and calculateMD5 to + // false, otherwise set them to true for backwards compatibility purposes + sqb := models.NewSceneQueryBuilder() + count, err := sqb.Count() + if err != nil { + logger.Errorf("Error while counting scenes: %s", err.Error()) + return + } + + usingMD5 := count != 0 + defaultAlgorithm := models.HashAlgorithmOshash + + if usingMD5 { + defaultAlgorithm = models.HashAlgorithmMd5 + } + + viper.SetDefault(config.VideoFileNamingAlgorithm, defaultAlgorithm) + viper.SetDefault(config.CalculateMD5, usingMD5) + + if err := config.Write(); err != nil { + logger.Errorf("Error while writing configuration file: %s", err.Error()) + } +} + +// ValidateVideoFileNamingAlgorithm validates changing the +// VideoFileNamingAlgorithm configuration flag. +// +// If setting VideoFileNamingAlgorithm to MD5, then this function will ensure +// that all checksum values are set on all scenes. +// +// Likewise, if VideoFileNamingAlgorithm is set to oshash, then this function +// will ensure that all oshash values are set on all scenes. +func ValidateVideoFileNamingAlgorithm(newValue models.HashAlgorithm) error { + // if algorithm is being set to MD5, then all checksums must be present + qb := models.NewSceneQueryBuilder() + if newValue == models.HashAlgorithmMd5 { + missingMD5, err := qb.CountMissingChecksum() + if err != nil { + return err + } + + if missingMD5 > 0 { + return errors.New("some checksums are missing on scenes. Run Scan with calculateMD5 set to true") + } + } else if newValue == models.HashAlgorithmOshash { + missingOSHash, err := qb.CountMissingOSHash() + if err != nil { + return err + } + + if missingOSHash > 0 { + return errors.New("some oshash values are missing on scenes. Run Scan to populate") + } + } + + return nil +} diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index f21493329..383febe7a 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -27,9 +27,31 @@ const Database = "database" const Exclude = "exclude" +// CalculateMD5 is the config key used to determine if MD5 should be calculated +// for video files. +const CalculateMD5 = "calculate_md5" + +// VideoFileNamingAlgorithm is the config key used to determine what hash +// should be used when generating and using generated files for scenes. +const VideoFileNamingAlgorithm = "video_file_naming_algorithm" + +const PreviewPreset = "preview_preset" + const MaxTranscodeSize = "max_transcode_size" const MaxStreamingTranscodeSize = "max_streaming_transcode_size" +const PreviewSegmentDuration = "preview_segment_duration" +const previewSegmentDurationDefault = 0.75 + +const PreviewSegments = "preview_segments" +const previewSegmentsDefault = 12 + +const PreviewExcludeStart = "preview_exclude_start" +const previewExcludeStartDefault = "0" + +const PreviewExcludeEnd = "preview_exclude_end" +const previewExcludeEndDefault = "0" + const Host = "host" const Port = "port" const ExternalHost = "external_host" @@ -43,10 +65,18 @@ const SessionStoreKey = "session_store_key" // scraping options const ScrapersPath = "scrapers_path" const ScraperUserAgent = "scraper_user_agent" +const ScraperCDPPath = "scraper_cdp_path" + +// plugin options +const PluginsPath = "plugins_path" // i18n const Language = "language" +// served directories +// this should be manually configured only +const CustomServedFolders = "custom_served_folders" + // Interface options const SoundOnPreview = "sound_on_preview" const WallShowTitle = "wall_show_title" @@ -56,10 +86,6 @@ const ShowStudioAsText = "show_studio_as_text" const CSSEnabled = "cssEnabled" const WallPlayback = "wall_playback" -// Playback force codec,container -const ForceMKV = "forceMKV" -const ForceHEVC = "forceHEVC" - // Logging options const LogFile = "logFile" const LogOut = "logOut" @@ -83,6 +109,11 @@ func Write() error { return viper.WriteConfig() } +func GetConfigPath() string { + configFileUsed := viper.ConfigFileUsed() + return filepath.Dir(configFileUsed) +} + func GetStashPaths() []string { return viper.GetStringSlice(Stash) } @@ -113,10 +144,8 @@ func GetSessionStoreKey() []byte { func GetDefaultScrapersPath() string { // default to the same directory as the config file - configFileUsed := viper.ConfigFileUsed() - configDir := filepath.Dir(configFileUsed) - fn := filepath.Join(configDir, "scrapers") + fn := filepath.Join(GetConfigPath(), "scrapers") return fn } @@ -136,6 +165,25 @@ func GetLanguage() string { return ret } +// IsCalculateMD5 returns true if MD5 checksums should be generated for +// scene video files. +func IsCalculateMD5() bool { + return viper.GetBool(CalculateMD5) +} + +// GetVideoFileNamingAlgorithm returns what hash algorithm should be used for +// naming generated scene video files. +func GetVideoFileNamingAlgorithm() models.HashAlgorithm { + ret := viper.GetString(VideoFileNamingAlgorithm) + + // default to oshash + if ret == "" { + return models.HashAlgorithmOshash + } + + return models.HashAlgorithm(ret) +} + func GetScrapersPath() string { return viper.GetString(ScrapersPath) } @@ -144,6 +192,23 @@ func GetScraperUserAgent() string { return viper.GetString(ScraperUserAgent) } +// GetScraperCDPPath gets the path to the Chrome executable or remote address +// to an instance of Chrome. +func GetScraperCDPPath() string { + return viper.GetString(ScraperCDPPath) +} + +func GetDefaultPluginsPath() string { + // default to the same directory as the config file + fn := filepath.Join(GetConfigPath(), "plugins") + + return fn +} + +func GetPluginsPath() string { + return viper.GetString(PluginsPath) +} + func GetHost() string { return viper.GetString(Host) } @@ -156,6 +221,49 @@ func GetExternalHost() string { return viper.GetString(ExternalHost) } +// GetPreviewSegmentDuration returns the duration of a single segment in a +// scene preview file, in seconds. +func GetPreviewSegmentDuration() float64 { + return viper.GetFloat64(PreviewSegmentDuration) +} + +// GetPreviewSegments returns the amount of segments in a scene preview file. +func GetPreviewSegments() int { + return viper.GetInt(PreviewSegments) +} + +// GetPreviewExcludeStart returns the configuration setting string for +// excluding the start of scene videos for preview generation. This can +// be in two possible formats. A float value is interpreted as the amount +// of seconds to exclude from the start of the video before it is included +// in the preview. If the value is suffixed with a '%' character (for example +// '2%'), then it is interpreted as a proportion of the total video duration. +func GetPreviewExcludeStart() string { + return viper.GetString(PreviewExcludeStart) +} + +// GetPreviewExcludeEnd returns the configuration setting string for +// excluding the end of scene videos for preview generation. A float value +// is interpreted as the amount of seconds to exclude from the end of the video +// when generating previews. If the value is suffixed with a '%' character, +// then it is interpreted as a proportion of the total video duration. +func GetPreviewExcludeEnd() string { + return viper.GetString(PreviewExcludeEnd) +} + +// GetPreviewPreset returns the preset when generating previews. Defaults to +// Slow. +func GetPreviewPreset() models.PreviewPreset { + ret := viper.GetString(PreviewPreset) + + // default to slow + if ret == "" { + return models.PreviewPresetSlow + } + + return models.PreviewPreset(ret) +} + func GetMaxTranscodeSize() models.StreamingResolutionEnum { ret := viper.GetString(MaxTranscodeSize) @@ -231,6 +339,12 @@ func GetMaxSessionAge() int { return viper.GetInt(MaxSessionAge) } +// GetCustomServedFolders gets the map of custom paths to their applicable +// filesystem locations +func GetCustomServedFolders() URLMap { + return viper.GetStringMapString(CustomServedFolders) +} + // Interface options func GetSoundOnPreview() bool { viper.SetDefault(SoundOnPreview, true) @@ -301,15 +415,6 @@ func GetCSSEnabled() bool { return viper.GetBool(CSSEnabled) } -// force codec,container -func GetForceMKV() bool { - return viper.GetBool(ForceMKV) -} - -func GetForceHEVC() bool { - return viper.GetBool(ForceHEVC) -} - // GetLogFile returns the filename of the file to output logs to. // An empty string means that file logging will be disabled. func GetLogFile() string { @@ -334,7 +439,7 @@ func GetLogLevel() string { const defaultValue = "Info" value := viper.GetString(LogLevel) - if value != "Debug" && value != "Info" && value != "Warning" && value != "Error" { + if value != "Debug" && value != "Info" && value != "Warning" && value != "Error" && value != "Trace" { value = defaultValue } @@ -359,6 +464,13 @@ func IsValid() bool { return setPaths } +func setDefaultValues() { + viper.SetDefault(PreviewSegmentDuration, previewSegmentDurationDefault) + viper.SetDefault(PreviewSegments, previewSegmentsDefault) + viper.SetDefault(PreviewExcludeStart, previewExcludeStartDefault) + viper.SetDefault(PreviewExcludeEnd, previewExcludeEndDefault) +} + // SetInitialConfig fills in missing required config fields func SetInitialConfig() error { // generate some api keys @@ -374,5 +486,7 @@ func SetInitialConfig() error { Set(SessionStoreKey, sessionStoreKey) } + setDefaultValues() + return Write() } diff --git a/pkg/manager/config/urlmap.go b/pkg/manager/config/urlmap.go new file mode 100644 index 000000000..d7a26a42f --- /dev/null +++ b/pkg/manager/config/urlmap.go @@ -0,0 +1,21 @@ +package config + +import "strings" + +type URLMap map[string]string + +// GetFilesystemLocation returns the adjusted URL and the filesystem location +func (m URLMap) GetFilesystemLocation(url string) (string, string) { + root := m["/"] + for k, v := range m { + if k != "/" && strings.HasPrefix(url, k) { + return strings.TrimPrefix(url, k), v + } + } + + if root != "" { + return url, root + } + + return url, "" +} diff --git a/pkg/manager/config/urlmap_test.go b/pkg/manager/config/urlmap_test.go new file mode 100644 index 000000000..cdccf2254 --- /dev/null +++ b/pkg/manager/config/urlmap_test.go @@ -0,0 +1,28 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestURLMapGetFilesystemLocation(t *testing.T) { + // create the URLMap + urlMap := make(URLMap) + urlMap["/"] = "root" + urlMap["/foo"] = "bar" + + url, fs := urlMap.GetFilesystemLocation("/foo/bar") + assert.Equal(t, "/bar", url) + assert.Equal(t, urlMap["/foo"], fs) + + url, fs = urlMap.GetFilesystemLocation("/bar") + assert.Equal(t, "/bar", url) + assert.Equal(t, urlMap["/"], fs) + + delete(urlMap, "/") + + url, fs = urlMap.GetFilesystemLocation("/bar") + assert.Equal(t, "/bar", url) + assert.Equal(t, "", fs) +} diff --git a/pkg/manager/generator.go b/pkg/manager/generator.go index 53d5a92f5..63c2dfb7f 100644 --- a/pkg/manager/generator.go +++ b/pkg/manager/generator.go @@ -7,6 +7,7 @@ import ( "os/exec" "runtime" "strconv" + "strings" "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" @@ -17,7 +18,13 @@ type GeneratorInfo struct { ChunkCount int FrameRate float64 NumberOfFrames int - NthFrame int + + // NthFrame used for sprite generation + NthFrame int + + ChunkDuration float64 + ExcludeStart string + ExcludeEnd string VideoFile ffmpeg.VideoFile } @@ -33,12 +40,7 @@ func newGeneratorInfo(videoFile ffmpeg.VideoFile) (*GeneratorInfo, error) { return generator, nil } -func (g *GeneratorInfo) configure() error { - videoStream := g.VideoFile.VideoStream - if videoStream == nil { - return fmt.Errorf("missing video stream") - } - +func (g *GeneratorInfo) calculateFrameRate(videoStream *ffmpeg.FFProbeStream) error { var framerate float64 if g.VideoFile.FrameRate == 0 { framerate, _ = strconv.ParseFloat(videoStream.RFrameRate, 64) @@ -94,7 +96,54 @@ func (g *GeneratorInfo) configure() error { g.FrameRate = framerate g.NumberOfFrames = numberOfFrames + + return nil +} + +func (g *GeneratorInfo) configure() error { + videoStream := g.VideoFile.VideoStream + if videoStream == nil { + return fmt.Errorf("missing video stream") + } + + if err := g.calculateFrameRate(videoStream); err != nil { + return err + } + g.NthFrame = g.NumberOfFrames / g.ChunkCount return nil } + +func (g GeneratorInfo) getExcludeValue(v string) float64 { + if strings.HasSuffix(v, "%") && len(v) > 1 { + // proportion of video duration + v = v[0 : len(v)-1] + prop, _ := strconv.ParseFloat(v, 64) + return prop / 100.0 * g.VideoFile.Duration + } + + prop, _ := strconv.ParseFloat(v, 64) + return prop +} + +// getStepSizeAndOffset calculates the step size for preview generation and +// the starting offset. +// +// Step size is calculated based on the duration of the video file, minus the +// excluded duration. The offset is based on the ExcludeStart. If the total +// excluded duration exceeds the duration of the video, then offset is 0, and +// the video duration is used to calculate the step size. +func (g GeneratorInfo) getStepSizeAndOffset() (stepSize float64, offset float64) { + duration := g.VideoFile.Duration + excludeStart := g.getExcludeValue(g.ExcludeStart) + excludeEnd := g.getExcludeValue(g.ExcludeEnd) + + if duration > excludeStart+excludeEnd { + duration = duration - excludeStart - excludeEnd + offset = excludeStart + } + + stepSize = duration / float64(g.ChunkCount) + return +} diff --git a/pkg/manager/generator_preview.go b/pkg/manager/generator_preview.go index f91fdf956..df19981ab 100644 --- a/pkg/manager/generator_preview.go +++ b/pkg/manager/generator_preview.go @@ -3,11 +3,12 @@ package manager import ( "bufio" "fmt" + "os" + "path/filepath" + "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/utils" - "os" - "path/filepath" ) type PreviewGenerator struct { @@ -21,6 +22,8 @@ type PreviewGenerator struct { GenerateImage bool PreviewPreset string + + Overwrite bool } func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, imageFilename string, outputDirectory string, generateVideo bool, generateImage bool, previewPreset string) (*PreviewGenerator, error) { @@ -33,9 +36,6 @@ func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, image return nil, err } generator.ChunkCount = 12 // 12 segments to the preview - if err := generator.configure(); err != nil { - return nil, err - } return &PreviewGenerator{ Info: generator, @@ -50,6 +50,11 @@ func NewPreviewGenerator(videoFile ffmpeg.VideoFile, videoFilename string, image func (g *PreviewGenerator) Generate() error { logger.Infof("[generator] generating scene preview for %s", g.Info.VideoFile.Path) + + if err := g.Info.configure(); err != nil { + return err + } + encoder := ffmpeg.NewEncoder(instance.FFMPEGPath) if err := g.generateConcatFile(); err != nil { @@ -57,8 +62,11 @@ func (g *PreviewGenerator) Generate() error { } if g.GenerateVideo { - if err := g.generateVideo(&encoder); err != nil { - return err + if err := g.generateVideo(&encoder, false); err != nil { + logger.Warnf("[generator] failed generating scene preview, trying fallback") + if err := g.generateVideo(&encoder, true); err != nil { + return err + } } } if g.GenerateImage { @@ -85,30 +93,36 @@ func (g *PreviewGenerator) generateConcatFile() error { return w.Flush() } -func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder) error { +func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder, fallback bool) error { outputPath := filepath.Join(g.OutputDirectory, g.VideoFilename) outputExists, _ := utils.FileExists(outputPath) - if outputExists { + if !g.Overwrite && outputExists { return nil } - stepSize := int(g.Info.VideoFile.Duration / float64(g.Info.ChunkCount)) + stepSize, offset := g.Info.getStepSizeAndOffset() + for i := 0; i < g.Info.ChunkCount; i++ { - time := i * stepSize + time := offset + (float64(i) * stepSize) num := fmt.Sprintf("%.3d", i) filename := "preview" + num + ".mp4" chunkOutputPath := instance.Paths.Generated.GetTmpPath(filename) options := ffmpeg.ScenePreviewChunkOptions{ - Time: time, + StartTime: time, + Duration: g.Info.ChunkDuration, Width: 640, OutputPath: chunkOutputPath, } - encoder.ScenePreviewVideoChunk(g.Info.VideoFile, options, g.PreviewPreset) + if err := encoder.ScenePreviewVideoChunk(g.Info.VideoFile, options, g.PreviewPreset, fallback); err != nil { + return err + } } videoOutputPath := filepath.Join(g.OutputDirectory, g.VideoFilename) - encoder.ScenePreviewVideoChunkCombine(g.Info.VideoFile, g.getConcatFilePath(), videoOutputPath) + if err := encoder.ScenePreviewVideoChunkCombine(g.Info.VideoFile, g.getConcatFilePath(), videoOutputPath); err != nil { + return err + } logger.Debug("created video preview: ", videoOutputPath) return nil } @@ -116,7 +130,7 @@ func (g *PreviewGenerator) generateVideo(encoder *ffmpeg.Encoder) error { func (g *PreviewGenerator) generateImage(encoder *ffmpeg.Encoder) error { outputPath := filepath.Join(g.OutputDirectory, g.ImageFilename) outputExists, _ := utils.FileExists(outputPath) - if outputExists { + if !g.Overwrite && outputExists { return nil } @@ -125,7 +139,7 @@ func (g *PreviewGenerator) generateImage(encoder *ffmpeg.Encoder) error { if err := encoder.ScenePreviewVideoToImage(g.Info.VideoFile, 640, videoPreviewPath, tmpOutputPath); err != nil { return err } - if err := os.Rename(tmpOutputPath, outputPath); err != nil { + if err := utils.SafeMove(tmpOutputPath, outputPath); err != nil { return err } logger.Debug("created video preview image: ", outputPath) diff --git a/pkg/manager/generator_sprite.go b/pkg/manager/generator_sprite.go index cc3b57f95..66e16e559 100644 --- a/pkg/manager/generator_sprite.go +++ b/pkg/manager/generator_sprite.go @@ -2,17 +2,19 @@ package manager import ( "fmt" - "github.com/bmatcuk/doublestar" - "github.com/disintegration/imaging" - "github.com/stashapp/stash/pkg/ffmpeg" - "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/utils" "image" "image/color" "io/ioutil" "math" + "os" "path/filepath" "strings" + + "github.com/bmatcuk/doublestar/v2" + "github.com/disintegration/imaging" + "github.com/stashapp/stash/pkg/ffmpeg" + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/utils" ) type SpriteGenerator struct { @@ -22,6 +24,8 @@ type SpriteGenerator struct { VTTOutputPath string Rows int Columns int + + Overwrite bool } func NewSpriteGenerator(videoFile ffmpeg.VideoFile, imageOutputPath string, vttOutputPath string, rows int, cols int) (*SpriteGenerator, error) { @@ -60,7 +64,7 @@ func (g *SpriteGenerator) Generate() error { } func (g *SpriteGenerator) generateSpriteImage(encoder *ffmpeg.Encoder) error { - if g.imageExists() { + if !g.Overwrite && g.imageExists() { return nil } logger.Infof("[generator] generating sprite image for %s", g.Info.VideoFile.Path) @@ -112,18 +116,20 @@ func (g *SpriteGenerator) generateSpriteImage(encoder *ffmpeg.Encoder) error { } func (g *SpriteGenerator) generateSpriteVTT(encoder *ffmpeg.Encoder) error { - if g.vttExists() { + if !g.Overwrite && g.vttExists() { return nil } logger.Infof("[generator] generating sprite vtt for %s", g.Info.VideoFile.Path) - spriteImage, err := imaging.Open(g.ImageOutputPath) + spriteImage, err := os.Open(g.ImageOutputPath) if err != nil { return err } + defer spriteImage.Close() spriteImageName := filepath.Base(g.ImageOutputPath) - width := spriteImage.Bounds().Size().X / g.Columns - height := spriteImage.Bounds().Size().Y / g.Rows + image, _, err := image.DecodeConfig(spriteImage) + width := image.Width / g.Columns + height := image.Height / g.Rows stepSize := float64(g.Info.NthFrame) / g.Info.FrameRate diff --git a/pkg/manager/job_status.go b/pkg/manager/job_status.go index a1a57802e..4a6c7197a 100644 --- a/pkg/manager/job_status.go +++ b/pkg/manager/job_status.go @@ -3,14 +3,16 @@ package manager type JobStatus int const ( - Idle JobStatus = 0 - Import JobStatus = 1 - Export JobStatus = 2 - Scan JobStatus = 3 - Generate JobStatus = 4 - Clean JobStatus = 5 - Scrape JobStatus = 6 - AutoTag JobStatus = 7 + Idle JobStatus = 0 + Import JobStatus = 1 + Export JobStatus = 2 + Scan JobStatus = 3 + Generate JobStatus = 4 + Clean JobStatus = 5 + Scrape JobStatus = 6 + AutoTag JobStatus = 7 + Migrate JobStatus = 8 + PluginOperation JobStatus = 9 ) func (s JobStatus) String() string { @@ -29,6 +31,12 @@ func (s JobStatus) String() string { statusMessage = "Generate" case AutoTag: statusMessage = "Auto Tag" + case Migrate: + statusMessage = "Migrate" + case Clean: + statusMessage = "Clean" + case PluginOperation: + statusMessage = "Plugin Operation" } return statusMessage diff --git a/pkg/manager/json_utils.go b/pkg/manager/json_utils.go index 384f5937c..1d5dbc4a0 100644 --- a/pkg/manager/json_utils.go +++ b/pkg/manager/json_utils.go @@ -38,6 +38,14 @@ func (jp *jsonUtils) saveStudio(checksum string, studio *jsonschema.Studio) erro return jsonschema.SaveStudioFile(instance.Paths.JSON.StudioJSONPath(checksum), studio) } +func (jp *jsonUtils) getTag(checksum string) (*jsonschema.Tag, error) { + return jsonschema.LoadTagFile(instance.Paths.JSON.TagJSONPath(checksum)) +} + +func (jp *jsonUtils) saveTag(checksum string, tag *jsonschema.Tag) error { + return jsonschema.SaveTagFile(instance.Paths.JSON.TagJSONPath(checksum), tag) +} + func (jp *jsonUtils) getMovie(checksum string) (*jsonschema.Movie, error) { return jsonschema.LoadMovieFile(instance.Paths.JSON.MovieJSONPath(checksum)) } diff --git a/pkg/manager/jsonschema/mappings.go b/pkg/manager/jsonschema/mappings.go index 621ffc024..2e65f1023 100644 --- a/pkg/manager/jsonschema/mappings.go +++ b/pkg/manager/jsonschema/mappings.go @@ -2,8 +2,9 @@ package jsonschema import ( "fmt" - "github.com/json-iterator/go" "os" + + jsoniter "github.com/json-iterator/go" ) type NameMapping struct { @@ -17,6 +18,7 @@ type PathMapping struct { } type Mappings struct { + Tags []NameMapping `json:"tags"` Performers []NameMapping `json:"performers"` Studios []NameMapping `json:"studios"` Movies []NameMapping `json:"movies"` diff --git a/pkg/manager/jsonschema/movie.go b/pkg/manager/jsonschema/movie.go index ae062acb6..c76f05055 100644 --- a/pkg/manager/jsonschema/movie.go +++ b/pkg/manager/jsonschema/movie.go @@ -2,9 +2,9 @@ package jsonschema import ( "fmt" - "github.com/json-iterator/go" "os" + jsoniter "github.com/json-iterator/go" "github.com/stashapp/stash/pkg/models" ) @@ -19,6 +19,7 @@ type Movie struct { FrontImage string `json:"front_image,omitempty"` BackImage string `json:"back_image,omitempty"` URL string `json:"url,omitempty"` + Studio string `json:"studio,omitempty"` CreatedAt models.JSONTime `json:"created_at,omitempty"` UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } diff --git a/pkg/manager/jsonschema/scene.go b/pkg/manager/jsonschema/scene.go index b08c8a844..8118a47a1 100644 --- a/pkg/manager/jsonschema/scene.go +++ b/pkg/manager/jsonschema/scene.go @@ -2,9 +2,9 @@ package jsonschema import ( "fmt" - "github.com/json-iterator/go" "os" + jsoniter "github.com/json-iterator/go" "github.com/stashapp/stash/pkg/models" ) @@ -36,6 +36,8 @@ type SceneMovie struct { type Scene struct { Title string `json:"title,omitempty"` + Checksum string `json:"checksum,omitempty"` + OSHash string `json:"oshash,omitempty"` Studio string `json:"studio,omitempty"` URL string `json:"url,omitempty"` Date string `json:"date,omitempty"` diff --git a/pkg/manager/jsonschema/studio.go b/pkg/manager/jsonschema/studio.go index 246c36050..ed1f7dea0 100644 --- a/pkg/manager/jsonschema/studio.go +++ b/pkg/manager/jsonschema/studio.go @@ -2,17 +2,19 @@ package jsonschema import ( "fmt" - "github.com/json-iterator/go" - "github.com/stashapp/stash/pkg/models" "os" + + jsoniter "github.com/json-iterator/go" + "github.com/stashapp/stash/pkg/models" ) type Studio struct { - Name string `json:"name,omitempty"` - URL string `json:"url,omitempty"` - Image string `json:"image,omitempty"` - CreatedAt models.JSONTime `json:"created_at,omitempty"` - UpdatedAt models.JSONTime `json:"updated_at,omitempty"` + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + ParentStudio string `json:"parent_studio,omitempty"` + Image string `json:"image,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } func LoadStudioFile(filePath string) (*Studio, error) { diff --git a/pkg/manager/jsonschema/tag.go b/pkg/manager/jsonschema/tag.go new file mode 100644 index 000000000..06ca48be8 --- /dev/null +++ b/pkg/manager/jsonschema/tag.go @@ -0,0 +1,39 @@ +package jsonschema + +import ( + "fmt" + "os" + + jsoniter "github.com/json-iterator/go" + "github.com/stashapp/stash/pkg/models" +) + +type Tag struct { + Name string `json:"name,omitempty"` + Image string `json:"image,omitempty"` + CreatedAt models.JSONTime `json:"created_at,omitempty"` + UpdatedAt models.JSONTime `json:"updated_at,omitempty"` +} + +func LoadTagFile(filePath string) (*Tag, error) { + var tag Tag + file, err := os.Open(filePath) + defer file.Close() + if err != nil { + return nil, err + } + var json = jsoniter.ConfigCompatibleWithStandardLibrary + jsonParser := json.NewDecoder(file) + err = jsonParser.Decode(&tag) + if err != nil { + return nil, err + } + return &tag, nil +} + +func SaveTagFile(filePath string, tag *Tag) error { + if tag == nil { + return fmt.Errorf("tag must not be nil") + } + return marshalToFile(filePath, tag) +} diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index f3bef5530..cedcc278a 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -10,6 +10,8 @@ import ( "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/manager/paths" + "github.com/stashapp/stash/pkg/plugin" + "github.com/stashapp/stash/pkg/scraper" "github.com/stashapp/stash/pkg/utils" ) @@ -20,6 +22,9 @@ type singleton struct { FFMPEGPath string FFProbePath string + + PluginCache *plugin.Cache + ScraperCache *scraper.Cache } var instance *singleton @@ -47,6 +52,9 @@ func Initialize() *singleton { Status: TaskStatus{Status: Idle, Progress: -1}, Paths: paths.NewPaths(), JSON: &jsonUtils{}, + + PluginCache: initPluginCache(), + ScraperCache: initScraperCache(), } instance.RefreshConfig() @@ -76,13 +84,16 @@ func initConfig() { } logger.Infof("using config file: %s", viper.ConfigFileUsed()) + config.SetInitialConfig() + viper.SetDefault(config.Database, paths.GetDefaultDatabaseFilePath()) // Set generated to the metadata path for backwards compat viper.SetDefault(config.Generated, viper.GetString(config.Metadata)) - // Set default scrapers path + // Set default scrapers and plugins paths viper.SetDefault(config.ScrapersPath, config.GetDefaultScrapersPath()) + viper.SetDefault(config.PluginsPath, config.GetDefaultPluginsPath()) // Disabling config watching due to race condition issue // See: https://github.com/spf13/viper/issues/174 @@ -134,10 +145,12 @@ The FFMPEG and FFProbe binaries should be placed in %s The error was: %s ` logger.Fatalf(msg, configDirectory, err) + } else { + // After download get new paths for ffmpeg and ffprobe + ffmpegPath, ffprobePath = ffmpeg.GetPaths(configDirectory) } } - // TODO: is this valid after download? instance.FFMPEGPath = ffmpegPath instance.FFProbePath = ffprobePath } @@ -146,6 +159,32 @@ func initLog() { logger.Init(config.GetLogFile(), config.GetLogOut(), config.GetLogLevel()) } +func initPluginCache() *plugin.Cache { + ret, err := plugin.NewCache(config.GetPluginsPath()) + + if err != nil { + logger.Errorf("Error reading plugin configs: %s", err.Error()) + } + + return ret +} + +// initScraperCache initializes a new scraper cache and returns it. +func initScraperCache() *scraper.Cache { + scraperConfig := scraper.GlobalConfig{ + Path: config.GetScrapersPath(), + UserAgent: config.GetScraperUserAgent(), + CDPPath: config.GetScraperCDPPath(), + } + ret, err := scraper.NewCache(scraperConfig) + + if err != nil { + logger.Errorf("Error reading scraper configs: %s", err.Error()) + } + + return ret +} + func (s *singleton) RefreshConfig() { s.Paths = paths.NewPaths() if config.IsValid() { @@ -157,3 +196,9 @@ func (s *singleton) RefreshConfig() { paths.EnsureJSONDirs() } } + +// RefreshScraperCache refreshes the scraper cache. Call this when scraper +// configuration changes. +func (s *singleton) RefreshScraperCache() { + s.ScraperCache = initScraperCache() +} diff --git a/pkg/manager/manager_tasks.go b/pkg/manager/manager_tasks.go index 70d83c09a..a82faaa7f 100644 --- a/pkg/manager/manager_tasks.go +++ b/pkg/manager/manager_tasks.go @@ -3,36 +3,32 @@ package manager import ( "path/filepath" "strconv" + "strings" "sync" "time" - "github.com/bmatcuk/doublestar" + "github.com/bmatcuk/doublestar/v2" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) -var extensionsToScan = []string{"zip", "m4v", "mp4", "mov", "wmv", "avi", "mpg", "mpeg", "rmvb", "rm", "flv", "asf", "mkv", "webm"} -var extensionsGallery = []string{"zip"} +var extensionsToScan = []string{"zip", "cbz", "m4v", "mp4", "mov", "wmv", "avi", "mpg", "mpeg", "rmvb", "rm", "flv", "asf", "mkv", "webm"} +var extensionsGallery = []string{"zip", "cbz"} func constructGlob() string { // create a sequence for glob doublestar from our extensions - extLen := len(extensionsToScan) - glb := "{" - for i := 0; i < extLen-1; i++ { // append extensions and commas - glb += extensionsToScan[i] + "," + var extList []string + for _, ext := range extensionsToScan { + extList = append(extList, strings.ToLower(ext)) + extList = append(extList, strings.ToUpper(ext)) } - if extLen >= 1 { // append last extension without comma - glb += extensionsToScan[extLen-1] - } - glb += "}" - return glb - + return "{" + strings.Join(extList, ",") + "}" } func isGallery(pathname string) bool { for _, ext := range extensionsGallery { - if filepath.Ext(pathname) == "."+ext { + if strings.ToLower(filepath.Ext(pathname)) == "."+strings.ToLower(ext) { return true } } @@ -69,6 +65,13 @@ func (t *TaskStatus) setProgress(upTo int, total int) { t.updated() } +func (t *TaskStatus) setProgressPercent(progress float64) { + if progress != t.Progress { + t.Progress = progress + t.updated() + } +} + func (t *TaskStatus) incrementProgress() { t.setProgress(t.upTo+1, t.total) } @@ -110,6 +113,8 @@ func (s *singleton) Scan(useFileMetadata bool) { var wg sync.WaitGroup s.Status.Progress = 0 + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + calculateMD5 := config.IsCalculateMD5() for i, path := range results { s.Status.setProgress(i, total) if s.Status.stopping { @@ -117,7 +122,7 @@ func (s *singleton) Scan(useFileMetadata bool) { return } wg.Add(1) - task := ScanTask{FilePath: path, UseFileMetadata: useFileMetadata} + task := ScanTask{FilePath: path, UseFileMetadata: useFileMetadata, fileNamingAlgorithm: fileNamingAlgo, calculateMD5: calculateMD5} go task.Start(&wg) wg.Wait() } @@ -147,7 +152,7 @@ func (s *singleton) Import() { var wg sync.WaitGroup wg.Add(1) - task := ImportTask{} + task := ImportTask{fileNamingAlgorithm: config.GetVideoFileNamingAlgorithm()} go task.Start(&wg) wg.Wait() }() @@ -165,13 +170,40 @@ func (s *singleton) Export() { var wg sync.WaitGroup wg.Add(1) - task := ExportTask{} + task := ExportTask{fileNamingAlgorithm: config.GetVideoFileNamingAlgorithm()} go task.Start(&wg) wg.Wait() }() } -func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models.PreviewPreset, imagePreviews bool, markers bool, transcodes bool, thumbnails bool) { +func setGeneratePreviewOptionsInput(optionsInput *models.GeneratePreviewOptionsInput) { + if optionsInput.PreviewSegments == nil { + val := config.GetPreviewSegments() + optionsInput.PreviewSegments = &val + } + + if optionsInput.PreviewSegmentDuration == nil { + val := config.GetPreviewSegmentDuration() + optionsInput.PreviewSegmentDuration = &val + } + + if optionsInput.PreviewExcludeStart == nil { + val := config.GetPreviewExcludeStart() + optionsInput.PreviewExcludeStart = &val + } + + if optionsInput.PreviewExcludeEnd == nil { + val := config.GetPreviewExcludeEnd() + optionsInput.PreviewExcludeEnd = &val + } + + if optionsInput.PreviewPreset == nil { + val := config.GetPreviewPreset() + optionsInput.PreviewPreset = &val + } +} + +func (s *singleton) Generate(input models.GenerateMetadataInput) { if s.Status.Status != Idle { return } @@ -180,32 +212,47 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models. qb := models.NewSceneQueryBuilder() qg := models.NewGalleryQueryBuilder() + mqb := models.NewSceneMarkerQueryBuilder() + //this.job.total = await ObjectionUtils.getCount(Scene); instance.Paths.Generated.EnsureTmpDir() - preset := string(models.PreviewPresetSlow) - if previewPreset != nil && previewPreset.IsValid() { - preset = string(*previewPreset) - } + galleryIDs := utils.StringSliceToIntSlice(input.GalleryIDs) + sceneIDs := utils.StringSliceToIntSlice(input.SceneIDs) + markerIDs := utils.StringSliceToIntSlice(input.MarkerIDs) go func() { defer s.returnToIdleState() - scenes, err := qb.All() - var galleries []*models.Gallery + var scenes []*models.Scene + var err error + + if len(sceneIDs) > 0 { + scenes, err = qb.FindMany(sceneIDs) + } else { + scenes, err = qb.All() + } if err != nil { logger.Errorf("failed to get scenes for generate") return } - delta := utils.Btoi(sprites) + utils.Btoi(previews) + utils.Btoi(markers) + utils.Btoi(transcodes) + delta := utils.Btoi(input.Sprites) + utils.Btoi(input.Previews) + utils.Btoi(input.Markers) + utils.Btoi(input.Transcodes) var wg sync.WaitGroup + s.Status.Progress = 0 lenScenes := len(scenes) total := lenScenes - if thumbnails { - galleries, err = qg.All() + + var galleries []*models.Gallery + if input.Thumbnails { + if len(galleryIDs) > 0 { + galleries, err = qg.FindMany(galleryIDs) + } else { + galleries, err = qg.All() + } + if err != nil { logger.Errorf("failed to get galleries for generate") return @@ -213,17 +260,39 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models. total += len(galleries) } + var markers []*models.SceneMarker + if len(markerIDs) > 0 { + markers, err = mqb.FindMany(markerIDs) + + total += len(markers) + } + if s.Status.stopping { logger.Info("Stopping due to user request") return } - totalsNeeded := s.neededGenerate(scenes, sprites, previews, imagePreviews, markers, transcodes) + + totalsNeeded := s.neededGenerate(scenes, input) if totalsNeeded == nil { logger.Infof("Taking too long to count content. Skipping...") logger.Infof("Generating content") } else { logger.Infof("Generating %d sprites %d previews %d image previews %d markers %d transcodes", totalsNeeded.sprites, totalsNeeded.previews, totalsNeeded.imagePreviews, totalsNeeded.markers, totalsNeeded.transcodes) } + + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + + overwrite := false + if input.Overwrite != nil { + overwrite = *input.Overwrite + } + + generatePreviewOptions := input.PreviewOptions + if generatePreviewOptions == nil { + generatePreviewOptions = &models.GeneratePreviewOptionsInput{} + } + setGeneratePreviewOptionsInput(generatePreviewOptions) + for i, scene := range scenes { s.Status.setProgress(i, total) if s.Status.stopping { @@ -239,34 +308,40 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models. wg.Add(delta) // Clear the tmp directory for each scene - if sprites || previews || markers { + if input.Sprites || input.Previews || input.Markers { instance.Paths.Generated.EmptyTmpDir() } - if sprites { - task := GenerateSpriteTask{Scene: *scene} + if input.Sprites { + task := GenerateSpriteTask{Scene: *scene, Overwrite: overwrite, fileNamingAlgorithm: fileNamingAlgo} go task.Start(&wg) } - if previews { - task := GeneratePreviewTask{Scene: *scene, ImagePreview: imagePreviews, PreviewPreset: preset} + if input.Previews { + task := GeneratePreviewTask{ + Scene: *scene, + ImagePreview: input.ImagePreviews, + Options: *generatePreviewOptions, + Overwrite: overwrite, + fileNamingAlgorithm: fileNamingAlgo, + } go task.Start(&wg) } - if markers { - task := GenerateMarkersTask{Scene: *scene} + if input.Markers { + task := GenerateMarkersTask{Scene: scene, Overwrite: overwrite, fileNamingAlgorithm: fileNamingAlgo} go task.Start(&wg) } - if transcodes { - task := GenerateTranscodeTask{Scene: *scene} + if input.Transcodes { + task := GenerateTranscodeTask{Scene: *scene, Overwrite: overwrite, fileNamingAlgorithm: fileNamingAlgo} go task.Start(&wg) } wg.Wait() } - if thumbnails { + if input.Thumbnails { logger.Infof("Generating thumbnails for the galleries") for i, gallery := range galleries { s.Status.setProgress(lenScenes+i, total) @@ -281,12 +356,30 @@ func (s *singleton) Generate(sprites bool, previews bool, previewPreset *models. } wg.Add(1) - task := GenerateGthumbsTask{Gallery: *gallery} + task := GenerateGthumbsTask{Gallery: *gallery, Overwrite: overwrite} go task.Start(&wg) wg.Wait() } } + for i, marker := range markers { + s.Status.setProgress(lenScenes+len(galleries)+i, total) + if s.Status.stopping { + logger.Info("Stopping due to user request") + return + } + + if marker == nil { + logger.Errorf("nil marker, skipping generate") + continue + } + + wg.Add(1) + task := GenerateMarkersTask{Marker: marker, Overwrite: overwrite, fileNamingAlgorithm: fileNamingAlgo} + go task.Start(&wg) + wg.Wait() + } + logger.Infof("Generate finished") }() } @@ -326,8 +419,9 @@ func (s *singleton) generateScreenshot(sceneId string, at *float64) { } task := GenerateScreenshotTask{ - Scene: *scene, - ScreenshotAt: at, + Scene: *scene, + ScreenshotAt: at, + fileNamingAlgorithm: config.GetVideoFileNamingAlgorithm(), } var wg sync.WaitGroup @@ -539,6 +633,7 @@ func (s *singleton) Clean() { var wg sync.WaitGroup s.Status.Progress = 0 total := len(scenes) + len(galleries) + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() for i, scene := range scenes { s.Status.setProgress(i, total) if s.Status.stopping { @@ -553,7 +648,7 @@ func (s *singleton) Clean() { wg.Add(1) - task := CleanTask{Scene: scene} + task := CleanTask{Scene: scene, fileNamingAlgorithm: fileNamingAlgo} go task.Start(&wg) wg.Wait() } @@ -581,6 +676,54 @@ func (s *singleton) Clean() { }() } +func (s *singleton) MigrateHash() { + if s.Status.Status != Idle { + return + } + s.Status.SetStatus(Migrate) + s.Status.indefiniteProgress() + + qb := models.NewSceneQueryBuilder() + + go func() { + defer s.returnToIdleState() + + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + logger.Infof("Migrating generated files for %s naming hash", fileNamingAlgo.String()) + + scenes, err := qb.All() + if err != nil { + logger.Errorf("failed to fetch list of scenes for migration") + return + } + + var wg sync.WaitGroup + s.Status.Progress = 0 + total := len(scenes) + + for i, scene := range scenes { + s.Status.setProgress(i, total) + if s.Status.stopping { + logger.Info("Stopping due to user request") + return + } + + if scene == nil { + logger.Errorf("nil scene, skipping migrate") + continue + } + + wg.Add(1) + + task := MigrateHashTask{Scene: scene, fileNamingAlgorithm: fileNamingAlgo} + go task.Start(&wg) + wg.Wait() + } + + logger.Info("Finished migrating") + }() +} + func (s *singleton) returnToIdleState() { if r := recover(); r != nil { logger.Info("recovered from ", r) @@ -614,7 +757,7 @@ type totalsGenerate struct { transcodes int64 } -func (s *singleton) neededGenerate(scenes []*models.Scene, sprites, previews, imagePreviews, markers, transcodes bool) *totalsGenerate { +func (s *singleton) neededGenerate(scenes []*models.Scene, input models.GenerateMetadataInput) *totalsGenerate { var totals totalsGenerate const timeout = 90 * time.Second @@ -628,33 +771,58 @@ func (s *singleton) neededGenerate(scenes []*models.Scene, sprites, previews, im chTimeout <- struct{}{} }() + fileNamingAlgo := config.GetVideoFileNamingAlgorithm() + overwrite := false + if input.Overwrite != nil { + overwrite = *input.Overwrite + } + logger.Infof("Counting content to generate...") for _, scene := range scenes { if scene != nil { - if sprites { - task := GenerateSpriteTask{Scene: *scene} - if !task.doesSpriteExist(task.Scene.Checksum) { + if input.Sprites { + task := GenerateSpriteTask{ + Scene: *scene, + fileNamingAlgorithm: fileNamingAlgo, + } + + if overwrite || task.required() { totals.sprites++ } } - if previews { - task := GeneratePreviewTask{Scene: *scene, ImagePreview: imagePreviews} - if !task.doesVideoPreviewExist(task.Scene.Checksum) { + if input.Previews { + task := GeneratePreviewTask{ + Scene: *scene, + ImagePreview: input.ImagePreviews, + fileNamingAlgorithm: fileNamingAlgo, + } + + sceneHash := scene.GetHash(task.fileNamingAlgorithm) + if overwrite || !task.doesVideoPreviewExist(sceneHash) { totals.previews++ } - if imagePreviews && !task.doesImagePreviewExist(task.Scene.Checksum) { + + if input.ImagePreviews && (overwrite || !task.doesImagePreviewExist(sceneHash)) { totals.imagePreviews++ } } - if markers { - task := GenerateMarkersTask{Scene: *scene} + if input.Markers { + task := GenerateMarkersTask{ + Scene: scene, + Overwrite: overwrite, + fileNamingAlgorithm: fileNamingAlgo, + } totals.markers += int64(task.isMarkerNeeded()) - } - if transcodes { - task := GenerateTranscodeTask{Scene: *scene} + + if input.Transcodes { + task := GenerateTranscodeTask{ + Scene: *scene, + Overwrite: overwrite, + fileNamingAlgorithm: fileNamingAlgo, + } if task.isTranscodeNeeded() { totals.transcodes++ } diff --git a/pkg/manager/paths/paths_json.go b/pkg/manager/paths/paths_json.go index 3f2ccbcb1..448b3734c 100644 --- a/pkg/manager/paths/paths_json.go +++ b/pkg/manager/paths/paths_json.go @@ -1,9 +1,10 @@ package paths import ( + "path/filepath" + "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/utils" - "path/filepath" ) type jsonPaths struct { @@ -16,6 +17,7 @@ type jsonPaths struct { Scenes string Galleries string Studios string + Tags string Movies string } @@ -29,6 +31,7 @@ func newJSONPaths() *jsonPaths { jp.Galleries = filepath.Join(config.GetMetadataPath(), "galleries") jp.Studios = filepath.Join(config.GetMetadataPath(), "studios") jp.Movies = filepath.Join(config.GetMetadataPath(), "movies") + jp.Tags = filepath.Join(config.GetMetadataPath(), "tags") return &jp } @@ -45,6 +48,7 @@ func EnsureJSONDirs() { utils.EnsureDir(jsonPaths.Performers) utils.EnsureDir(jsonPaths.Studios) utils.EnsureDir(jsonPaths.Movies) + utils.EnsureDir(jsonPaths.Tags) } func (jp *jsonPaths) PerformerJSONPath(checksum string) string { @@ -59,6 +63,10 @@ func (jp *jsonPaths) StudioJSONPath(checksum string) string { return filepath.Join(jp.Studios, checksum+".json") } +func (jp *jsonPaths) TagJSONPath(checksum string) string { + return filepath.Join(jp.Tags, checksum+".json") +} + func (jp *jsonPaths) MovieJSONPath(checksum string) string { return filepath.Join(jp.Movies, checksum+".json") } diff --git a/pkg/manager/post_migrate.go b/pkg/manager/post_migrate.go new file mode 100644 index 000000000..c6c8e1419 --- /dev/null +++ b/pkg/manager/post_migrate.go @@ -0,0 +1,6 @@ +package manager + +// PostMigrate is executed after migrations have been executed. +func (s *singleton) PostMigrate() { + setInitialMD5Config() +} diff --git a/pkg/manager/scene.go b/pkg/manager/scene.go index 9afb07221..5cb99c9c4 100644 --- a/pkg/manager/scene.go +++ b/pkg/manager/scene.go @@ -1,17 +1,22 @@ package manager import ( + "fmt" "os" "path/filepath" "strconv" "github.com/jmoiron/sqlx" + "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) +// DestroyScene deletes a scene and its associated relationships from the +// database. func DestroyScene(sceneID int, tx *sqlx.Tx) error { qb := models.NewSceneQueryBuilder() jqb := models.NewJoinsQueryBuilder() @@ -44,18 +49,25 @@ func DestroyScene(sceneID int, tx *sqlx.Tx) error { return nil } -func DeleteGeneratedSceneFiles(scene *models.Scene) { - markersFolder := filepath.Join(GetInstance().Paths.Generated.Markers, scene.Checksum) +// DeleteGeneratedSceneFiles deletes generated files for the provided scene. +func DeleteGeneratedSceneFiles(scene *models.Scene, fileNamingAlgo models.HashAlgorithm) { + sceneHash := scene.GetHash(fileNamingAlgo) + + if sceneHash == "" { + return + } + + markersFolder := filepath.Join(GetInstance().Paths.Generated.Markers, sceneHash) exists, _ := utils.FileExists(markersFolder) if exists { err := os.RemoveAll(markersFolder) if err != nil { - logger.Warnf("Could not delete file %s: %s", scene.Path, err.Error()) + logger.Warnf("Could not delete folder %s: %s", markersFolder, err.Error()) } } - thumbPath := GetInstance().Paths.Scene.GetThumbnailScreenshotPath(scene.Checksum) + thumbPath := GetInstance().Paths.Scene.GetThumbnailScreenshotPath(sceneHash) exists, _ = utils.FileExists(thumbPath) if exists { err := os.Remove(thumbPath) @@ -64,7 +76,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - normalPath := GetInstance().Paths.Scene.GetScreenshotPath(scene.Checksum) + normalPath := GetInstance().Paths.Scene.GetScreenshotPath(sceneHash) exists, _ = utils.FileExists(normalPath) if exists { err := os.Remove(normalPath) @@ -73,7 +85,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - streamPreviewPath := GetInstance().Paths.Scene.GetStreamPreviewPath(scene.Checksum) + streamPreviewPath := GetInstance().Paths.Scene.GetStreamPreviewPath(sceneHash) exists, _ = utils.FileExists(streamPreviewPath) if exists { err := os.Remove(streamPreviewPath) @@ -82,7 +94,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - streamPreviewImagePath := GetInstance().Paths.Scene.GetStreamPreviewImagePath(scene.Checksum) + streamPreviewImagePath := GetInstance().Paths.Scene.GetStreamPreviewImagePath(sceneHash) exists, _ = utils.FileExists(streamPreviewImagePath) if exists { err := os.Remove(streamPreviewImagePath) @@ -91,7 +103,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - transcodePath := GetInstance().Paths.Scene.GetTranscodePath(scene.Checksum) + transcodePath := GetInstance().Paths.Scene.GetTranscodePath(sceneHash) exists, _ = utils.FileExists(transcodePath) if exists { // kill any running streams @@ -103,7 +115,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - spritePath := GetInstance().Paths.Scene.GetSpriteImageFilePath(scene.Checksum) + spritePath := GetInstance().Paths.Scene.GetSpriteImageFilePath(sceneHash) exists, _ = utils.FileExists(spritePath) if exists { err := os.Remove(spritePath) @@ -112,7 +124,7 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } - vttPath := GetInstance().Paths.Scene.GetSpriteVttFilePath(scene.Checksum) + vttPath := GetInstance().Paths.Scene.GetSpriteVttFilePath(sceneHash) exists, _ = utils.FileExists(vttPath) if exists { err := os.Remove(vttPath) @@ -122,9 +134,11 @@ func DeleteGeneratedSceneFiles(scene *models.Scene) { } } -func DeleteSceneMarkerFiles(scene *models.Scene, seconds int) { - videoPath := GetInstance().Paths.SceneMarkers.GetStreamPath(scene.Checksum, seconds) - imagePath := GetInstance().Paths.SceneMarkers.GetStreamPreviewImagePath(scene.Checksum, seconds) +// DeleteSceneMarkerFiles deletes generated files for a scene marker with the +// provided scene and timestamp. +func DeleteSceneMarkerFiles(scene *models.Scene, seconds int, fileNamingAlgo models.HashAlgorithm) { + videoPath := GetInstance().Paths.SceneMarkers.GetStreamPath(scene.GetHash(fileNamingAlgo), seconds) + imagePath := GetInstance().Paths.SceneMarkers.GetStreamPreviewImagePath(scene.GetHash(fileNamingAlgo), seconds) exists, _ := utils.FileExists(videoPath) if exists { @@ -143,6 +157,7 @@ func DeleteSceneMarkerFiles(scene *models.Scene, seconds int) { } } +// DeleteSceneFile deletes the scene video file from the filesystem. func DeleteSceneFile(scene *models.Scene) { // kill any running encoders KillRunningStreams(scene.Path) @@ -152,3 +167,101 @@ func DeleteSceneFile(scene *models.Scene) { logger.Warnf("Could not delete file %s: %s", scene.Path, err.Error()) } } + +func GetSceneFileContainer(scene *models.Scene) (ffmpeg.Container, error) { + var container ffmpeg.Container + if scene.Format.Valid { + container = ffmpeg.Container(scene.Format.String) + } else { // container isn't in the DB + // shouldn't happen, fallback to ffprobe + tmpVideoFile, err := ffmpeg.NewVideoFile(GetInstance().FFProbePath, scene.Path) + if err != nil { + return ffmpeg.Container(""), fmt.Errorf("error reading video file: %s", err.Error()) + } + + container = ffmpeg.MatchContainer(tmpVideoFile.Container, scene.Path) + } + + return container, nil +} + +func GetSceneStreamPaths(scene *models.Scene, directStreamURL string) ([]*models.SceneStreamEndpoint, error) { + if scene == nil { + return nil, fmt.Errorf("nil scene") + } + + var ret []*models.SceneStreamEndpoint + mimeWebm := ffmpeg.MimeWebm + mimeHLS := ffmpeg.MimeHLS + mimeMp4 := ffmpeg.MimeMp4 + + labelWebm := "webm" + labelHLS := "HLS" + + // direct stream should only apply when the audio codec is supported + audioCodec := ffmpeg.MissingUnsupported + if scene.AudioCodec.Valid { + audioCodec = ffmpeg.AudioCodec(scene.AudioCodec.String) + } + container, err := GetSceneFileContainer(scene) + if err != nil { + return nil, err + } + + if HasTranscode(scene, config.GetVideoFileNamingAlgorithm()) || ffmpeg.IsValidAudioForContainer(audioCodec, container) { + label := "Direct stream" + ret = append(ret, &models.SceneStreamEndpoint{ + URL: directStreamURL, + MimeType: &mimeMp4, + Label: &label, + }) + } + + // only add mkv stream endpoint if the scene container is an mkv already + if container == ffmpeg.Matroska { + label := "mkv" + ret = append(ret, &models.SceneStreamEndpoint{ + URL: directStreamURL + ".mkv", + // set mkv to mp4 to trick the client, since many clients won't try mkv + MimeType: &mimeMp4, + Label: &label, + }) + } + + defaultStreams := []*models.SceneStreamEndpoint{ + { + URL: directStreamURL + ".webm", + MimeType: &mimeWebm, + Label: &labelWebm, + }, + { + URL: directStreamURL + ".m3u8", + MimeType: &mimeHLS, + Label: &labelHLS, + }, + } + + ret = append(ret, defaultStreams...) + + // TODO - at some point, look at streaming at various resolutions + + return ret, nil +} + +// HasTranscode returns true if a transcoded video exists for the provided +// scene. It will check using the OSHash of the scene first, then fall back +// to the checksum. +func HasTranscode(scene *models.Scene, fileNamingAlgo models.HashAlgorithm) bool { + if scene == nil { + return false + } + + sceneHash := scene.GetHash(fileNamingAlgo) + if sceneHash == "" { + return false + } + + transcodePath := instance.Paths.Scene.GetTranscodePath(sceneHash) + ret, _ := utils.FileExists(transcodePath) + return ret +} diff --git a/pkg/manager/studio.go b/pkg/manager/studio.go new file mode 100644 index 000000000..6de1ee572 --- /dev/null +++ b/pkg/manager/studio.go @@ -0,0 +1,36 @@ +package manager + +import ( + "errors" + "fmt" + + "github.com/jmoiron/sqlx" + "github.com/stashapp/stash/pkg/models" +) + +func ValidateModifyStudio(studio models.StudioPartial, tx *sqlx.Tx) error { + if studio.ParentID == nil || !studio.ParentID.Valid { + return nil + } + + // ensure there is no cyclic dependency + thisID := studio.ID + qb := models.NewStudioQueryBuilder() + + currentParentID := *studio.ParentID + + for currentParentID.Valid { + if currentParentID.Int64 == int64(thisID) { + return errors.New("studio cannot be an ancestor of itself") + } + + currentStudio, err := qb.Find(int(currentParentID.Int64), tx) + if err != nil { + return fmt.Errorf("error finding parent studio: %s", err.Error()) + } + + currentParentID = currentStudio.ParentID + } + + return nil +} diff --git a/pkg/manager/tag.go b/pkg/manager/tag.go new file mode 100644 index 000000000..d26591f8c --- /dev/null +++ b/pkg/manager/tag.go @@ -0,0 +1,25 @@ +package manager + +import ( + "fmt" + + "github.com/jmoiron/sqlx" + "github.com/stashapp/stash/pkg/models" +) + +func EnsureTagNameUnique(tag models.Tag, tx *sqlx.Tx) error { + qb := models.NewTagQueryBuilder() + + // ensure name is unique + sameNameTag, err := qb.FindByName(tag.Name, tx, true) + if err != nil { + _ = tx.Rollback() + return err + } + + if sameNameTag != nil && tag.ID != sameNameTag.ID { + return fmt.Errorf("Tag with name '%s' already exists", tag.Name) + } + + return nil +} diff --git a/pkg/manager/task_autotag_test.go b/pkg/manager/task_autotag_test.go index 1b302a94e..690c056c3 100644 --- a/pkg/manager/task_autotag_test.go +++ b/pkg/manager/task_autotag_test.go @@ -111,7 +111,6 @@ func createPerformer(tx *sqlx.Tx) error { pqb := models.NewPerformerQueryBuilder() performer := models.Performer{ - Image: []byte{0, 1, 2}, Checksum: testName, Name: sql.NullString{Valid: true, String: testName}, Favorite: sql.NullBool{Valid: true, Bool: false}, @@ -130,7 +129,6 @@ func createStudio(tx *sqlx.Tx, name string) (*models.Studio, error) { qb := models.NewStudioQueryBuilder() studio := models.Studio{ - Image: []byte{0, 1, 2}, Checksum: name, Name: sql.NullString{Valid: true, String: testName}, } @@ -202,7 +200,7 @@ func createScenes(tx *sqlx.Tx) error { func makeScene(name string, expectedResult bool) *models.Scene { scene := &models.Scene{ - Checksum: utils.MD5FromString(name), + Checksum: sql.NullString{String: utils.MD5FromString(name), Valid: true}, Path: name, } diff --git a/pkg/manager/task_clean.go b/pkg/manager/task_clean.go index 8cca80bbd..80b5f72e0 100644 --- a/pkg/manager/task_clean.go +++ b/pkg/manager/task_clean.go @@ -15,8 +15,9 @@ import ( ) type CleanTask struct { - Scene *models.Scene - Gallery *models.Gallery + Scene *models.Scene + Gallery *models.Gallery + fileNamingAlgorithm models.HashAlgorithm } func (t *CleanTask) Start(wg *sync.WaitGroup) { @@ -26,13 +27,19 @@ func (t *CleanTask) Start(wg *sync.WaitGroup) { t.deleteScene(t.Scene.ID) } - if t.Gallery != nil && t.shouldClean(t.Gallery.Path) { + if t.Gallery != nil && t.shouldCleanGallery(t.Gallery) { t.deleteGallery(t.Gallery.ID) } } func (t *CleanTask) shouldClean(path string) bool { - if t.fileExists(path) && t.pathInStash(path) { + fileExists, err := t.fileExists(path) + if err != nil { + logger.Errorf("Error checking existence of %s: %s", path, err.Error()) + return false + } + + if fileExists && t.pathInStash(path) { logger.Debugf("File Found: %s", path) if matchFile(path, config.GetExcludes()) { logger.Infof("File matched regex. Cleaning: \"%s\"", path) @@ -46,6 +53,19 @@ func (t *CleanTask) shouldClean(path string) bool { return false } +func (t *CleanTask) shouldCleanGallery(g *models.Gallery) bool { + if t.shouldClean(g.Path) { + return true + } + + if t.Gallery.CountFiles() == 0 { + logger.Infof("Gallery has 0 images. Cleaning: \"%s\"", g.Path) + return true + } + + return false +} + func (t *CleanTask) deleteScene(sceneID int) { ctx := context.TODO() qb := models.NewSceneQueryBuilder() @@ -65,7 +85,7 @@ func (t *CleanTask) deleteScene(sceneID int) { return } - DeleteGeneratedSceneFiles(scene) + DeleteGeneratedSceneFiles(scene, t.fileNamingAlgorithm) } func (t *CleanTask) deleteGallery(galleryID int) { @@ -92,12 +112,18 @@ func (t *CleanTask) deleteGallery(galleryID int) { } } -func (t *CleanTask) fileExists(filename string) bool { +func (t *CleanTask) fileExists(filename string) (bool, error) { info, err := os.Stat(filename) if os.IsNotExist(err) { - return false + return false, nil } - return !info.IsDir() + + // handle if error is something else + if err != nil { + return false, err + } + + return !info.IsDir(), nil } func (t *CleanTask) pathInStash(pathToCheck string) bool { diff --git a/pkg/manager/task_export.go b/pkg/manager/task_export.go index 27d8bb6cc..098f6cfc1 100644 --- a/pkg/manager/task_export.go +++ b/pkg/manager/task_export.go @@ -3,6 +3,12 @@ package manager import ( "context" "fmt" + "math" + "runtime" + "strconv" + "sync" + "time" + "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/logger" @@ -10,16 +16,12 @@ import ( "github.com/stashapp/stash/pkg/manager/paths" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "math" - "runtime" - "strconv" - "sync" - "time" ) type ExportTask struct { - Mappings *jsonschema.Mappings - Scraped []jsonschema.ScrapedItem + Mappings *jsonschema.Mappings + Scraped []jsonschema.ScrapedItem + fileNamingAlgorithm models.HashAlgorithm } func (t *ExportTask) Start(wg *sync.WaitGroup) { @@ -40,6 +42,7 @@ func (t *ExportTask) Start(wg *sync.WaitGroup) { t.ExportPerformers(ctx, workerCount) t.ExportStudios(ctx, workerCount) t.ExportMovies(ctx, workerCount) + t.ExportTags(ctx, workerCount) if err := instance.JSON.saveMappings(t.Mappings); err != nil { logger.Errorf("[mappings] failed to save json: %s", err.Error()) @@ -75,7 +78,7 @@ func (t *ExportTask) ExportScenes(ctx context.Context, workers int) { if (i % 100) == 0 { // make progress easier to read logger.Progressf("[scenes] %d of %d", index, len(scenes)) } - t.Mappings.Scenes = append(t.Mappings.Scenes, jsonschema.PathMapping{Path: scene.Path, Checksum: scene.Checksum}) + t.Mappings.Scenes = append(t.Mappings.Scenes, jsonschema.PathMapping{Path: scene.Path, Checksum: scene.GetHash(t.fileNamingAlgorithm)}) jobCh <- scene // feed workers } @@ -86,6 +89,7 @@ func (t *ExportTask) ExportScenes(ctx context.Context, workers int) { } func exportScene(wg *sync.WaitGroup, jobChan <-chan *models.Scene, t *ExportTask, tx *sqlx.Tx) { defer wg.Done() + sceneQB := models.NewSceneQueryBuilder() studioQB := models.NewStudioQueryBuilder() movieQB := models.NewMovieQueryBuilder() galleryQB := models.NewGalleryQueryBuilder() @@ -100,6 +104,14 @@ func exportScene(wg *sync.WaitGroup, jobChan <-chan *models.Scene, t *ExportTask UpdatedAt: models.JSONTime{Time: scene.UpdatedAt.Timestamp}, } + if scene.Checksum.Valid { + newSceneJSON.Checksum = scene.Checksum.String + } + + if scene.OSHash.Valid { + newSceneJSON.OSHash = scene.OSHash.String + } + var studioName string if scene.StudioID.Valid { studio, _ := studioQB.Find(int(scene.StudioID.Int64), tx) @@ -147,18 +159,20 @@ func exportScene(wg *sync.WaitGroup, jobChan <-chan *models.Scene, t *ExportTask newSceneJSON.Performers = t.getPerformerNames(performers) newSceneJSON.Tags = t.getTagNames(tags) + sceneHash := scene.GetHash(t.fileNamingAlgorithm) + for _, sceneMarker := range sceneMarkers { primaryTag, err := tagQB.Find(sceneMarker.PrimaryTagID, tx) if err != nil { - logger.Errorf("[scenes] <%s> invalid primary tag for scene marker: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> invalid primary tag for scene marker: %s", sceneHash, err.Error()) continue } sceneMarkerTags, err := tagQB.FindBySceneMarkerID(sceneMarker.ID, tx) if err != nil { - logger.Errorf("[scenes] <%s> invalid tags for scene marker: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> invalid tags for scene marker: %s", sceneHash, err.Error()) continue } - if sceneMarker.Title == "" || sceneMarker.Seconds == 0 || primaryTag.Name == "" { + if sceneMarker.Seconds == 0 || primaryTag.Name == "" { logger.Errorf("[scenes] invalid scene marker: %v", sceneMarker) } @@ -215,19 +229,25 @@ func exportScene(wg *sync.WaitGroup, jobChan <-chan *models.Scene, t *ExportTask newSceneJSON.File.Bitrate = int(scene.Bitrate.Int64) } - if len(scene.Cover) > 0 { - newSceneJSON.Cover = utils.GetBase64StringFromData(scene.Cover) + cover, err := sceneQB.GetSceneCover(scene.ID, tx) + if err != nil { + logger.Errorf("[scenes] <%s> error getting scene cover: %s", sceneHash, err.Error()) + continue } - sceneJSON, err := instance.JSON.getScene(scene.Checksum) + if len(cover) > 0 { + newSceneJSON.Cover = utils.GetBase64StringFromData(cover) + } + + sceneJSON, err := instance.JSON.getScene(sceneHash) if err != nil { logger.Debugf("[scenes] error reading scene json: %s", err.Error()) } else if jsonschema.CompareJSON(*sceneJSON, newSceneJSON) { continue } - if err := instance.JSON.saveScene(scene.Checksum, &newSceneJSON); err != nil { - logger.Errorf("[scenes] <%s> failed to save json: %s", scene.Checksum, err.Error()) + if err := instance.JSON.saveScene(sceneHash, &newSceneJSON); err != nil { + logger.Errorf("[scenes] <%s> failed to save json: %s", sceneHash, err.Error()) } } @@ -286,6 +306,8 @@ func (t *ExportTask) ExportPerformers(ctx context.Context, workers int) { func exportPerformer(wg *sync.WaitGroup, jobChan <-chan *models.Performer) { defer wg.Done() + performerQB := models.NewPerformerQueryBuilder() + for performer := range jobChan { newPerformerJSON := jsonschema.Performer{ CreatedAt: models.JSONTime{Time: performer.CreatedAt.Timestamp}, @@ -344,7 +366,15 @@ func exportPerformer(wg *sync.WaitGroup, jobChan <-chan *models.Performer) { newPerformerJSON.Favorite = performer.Favorite.Bool } - newPerformerJSON.Image = utils.GetBase64StringFromData(performer.Image) + image, err := performerQB.GetPerformerImage(performer.ID, nil) + if err != nil { + logger.Errorf("[performers] <%s> error getting performers image: %s", performer.Checksum, err.Error()) + continue + } + + if len(image) > 0 { + newPerformerJSON.Image = utils.GetBase64StringFromData(image) + } performerJSON, err := instance.JSON.getPerformer(performer.Checksum) if err != nil { @@ -395,6 +425,8 @@ func (t *ExportTask) ExportStudios(ctx context.Context, workers int) { func exportStudio(wg *sync.WaitGroup, jobChan <-chan *models.Studio) { defer wg.Done() + studioQB := models.NewStudioQueryBuilder() + for studio := range jobChan { newStudioJSON := jsonschema.Studio{ @@ -408,8 +440,22 @@ func exportStudio(wg *sync.WaitGroup, jobChan <-chan *models.Studio) { if studio.URL.Valid { newStudioJSON.URL = studio.URL.String } + if studio.ParentID.Valid { + parent, _ := studioQB.Find(int(studio.ParentID.Int64), nil) + if parent != nil { + newStudioJSON.ParentStudio = parent.Name.String + } + } - newStudioJSON.Image = utils.GetBase64StringFromData(studio.Image) + image, err := studioQB.GetStudioImage(studio.ID, nil) + if err != nil { + logger.Errorf("[studios] <%s> error getting studio image: %s", studio.Checksum, err.Error()) + continue + } + + if len(image) > 0 { + newStudioJSON.Image = utils.GetBase64StringFromData(image) + } studioJSON, err := instance.JSON.getStudio(studio.Checksum) if err != nil { @@ -424,6 +470,81 @@ func exportStudio(wg *sync.WaitGroup, jobChan <-chan *models.Studio) { } } +func (t *ExportTask) ExportTags(ctx context.Context, workers int) { + var tagsWg sync.WaitGroup + + qb := models.NewTagQueryBuilder() + tags, err := qb.All() + if err != nil { + logger.Errorf("[tags] failed to fetch all tags: %s", err.Error()) + } + + logger.Info("[tags] exporting") + startTime := time.Now() + + jobCh := make(chan *models.Tag, workers*2) // make a buffered channel to feed workers + + for w := 0; w < workers; w++ { // create export Tag workers + tagsWg.Add(1) + go exportTag(&tagsWg, jobCh) + } + + for i, tag := range tags { + index := i + 1 + logger.Progressf("[tags] %d of %d", index, len(tags)) + + // generate checksum on the fly by name, since we don't store it + checksum := utils.MD5FromString(tag.Name) + + t.Mappings.Tags = append(t.Mappings.Tags, jsonschema.NameMapping{Name: tag.Name, Checksum: checksum}) + jobCh <- tag // feed workers + } + + close(jobCh) + tagsWg.Wait() + + logger.Infof("[tags] export complete in %s. %d workers used.", time.Since(startTime), workers) +} + +func exportTag(wg *sync.WaitGroup, jobChan <-chan *models.Tag) { + defer wg.Done() + + tagQB := models.NewTagQueryBuilder() + + for tag := range jobChan { + + newTagJSON := jsonschema.Tag{ + Name: tag.Name, + CreatedAt: models.JSONTime{Time: tag.CreatedAt.Timestamp}, + UpdatedAt: models.JSONTime{Time: tag.UpdatedAt.Timestamp}, + } + + image, err := tagQB.GetTagImage(tag.ID, nil) + if err != nil { + logger.Errorf("[tags] <%s> error getting tag image: %s", tag.Name, err.Error()) + continue + } + + if len(image) > 0 { + newTagJSON.Image = utils.GetBase64StringFromData(image) + } + + // generate checksum on the fly by name, since we don't store it + checksum := utils.MD5FromString(tag.Name) + + tagJSON, err := instance.JSON.getTag(checksum) + if err != nil { + logger.Debugf("[tags] error reading tag json: %s", err.Error()) + } else if jsonschema.CompareJSON(*tagJSON, newTagJSON) { + continue + } + + if err := instance.JSON.saveTag(checksum, &newTagJSON); err != nil { + logger.Errorf("[tags] <%s> failed to save json: %s", checksum, err.Error()) + } + } +} + func (t *ExportTask) ExportMovies(ctx context.Context, workers int) { var moviesWg sync.WaitGroup @@ -460,6 +581,9 @@ func (t *ExportTask) ExportMovies(ctx context.Context, workers int) { func exportMovie(wg *sync.WaitGroup, jobChan <-chan *models.Movie) { defer wg.Done() + movieQB := models.NewMovieQueryBuilder() + studioQB := models.NewStudioQueryBuilder() + for movie := range jobChan { newMovieJSON := jsonschema.Movie{ CreatedAt: models.JSONTime{Time: movie.CreatedAt.Timestamp}, @@ -494,8 +618,33 @@ func exportMovie(wg *sync.WaitGroup, jobChan <-chan *models.Movie) { newMovieJSON.URL = movie.URL.String } - newMovieJSON.FrontImage = utils.GetBase64StringFromData(movie.FrontImage) - newMovieJSON.BackImage = utils.GetBase64StringFromData(movie.BackImage) + if movie.StudioID.Valid { + studio, _ := studioQB.Find(int(movie.StudioID.Int64), nil) + if studio != nil { + newMovieJSON.Studio = studio.Name.String + } + } + + frontImage, err := movieQB.GetFrontImage(movie.ID, nil) + if err != nil { + logger.Errorf("[movies] <%s> error getting movie front image: %s", movie.Checksum, err.Error()) + continue + } + + if len(frontImage) > 0 { + newMovieJSON.FrontImage = utils.GetBase64StringFromData(frontImage) + } + + backImage, err := movieQB.GetBackImage(movie.ID, nil) + if err != nil { + logger.Errorf("[movies] <%s> error getting movie back image: %s", movie.Checksum, err.Error()) + continue + } + + if len(backImage) > 0 { + newMovieJSON.BackImage = utils.GetBase64StringFromData(backImage) + } + movieJSON, err := instance.JSON.getMovie(movie.Checksum) if err != nil { logger.Debugf("[movies] error reading movie json: %s", err.Error()) diff --git a/pkg/manager/task_generate_gallery_thumbs.go b/pkg/manager/task_generate_gallery_thumbs.go index 2079e980d..6aad9c80d 100644 --- a/pkg/manager/task_generate_gallery_thumbs.go +++ b/pkg/manager/task_generate_gallery_thumbs.go @@ -1,15 +1,17 @@ package manager import ( + "sync" + "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager/paths" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "sync" ) type GenerateGthumbsTask struct { - Gallery models.Gallery + Gallery models.Gallery + Overwrite bool } func (t *GenerateGthumbsTask) Start(wg *sync.WaitGroup) { @@ -19,7 +21,7 @@ func (t *GenerateGthumbsTask) Start(wg *sync.WaitGroup) { for i := 0; i < count; i++ { thumbPath := paths.GetGthumbPath(t.Gallery.Checksum, i, models.DefaultGthumbWidth) exists, _ := utils.FileExists(thumbPath) - if exists { + if !t.Overwrite && exists { continue } data := t.Gallery.GetThumbnail(i, models.DefaultGthumbWidth) diff --git a/pkg/manager/task_generate_markers.go b/pkg/manager/task_generate_markers.go index 2b6cde2c6..deae76ec0 100644 --- a/pkg/manager/task_generate_markers.go +++ b/pkg/manager/task_generate_markers.go @@ -1,7 +1,6 @@ package manager import ( - "os" "path/filepath" "strconv" "sync" @@ -13,12 +12,38 @@ import ( ) type GenerateMarkersTask struct { - Scene models.Scene + Scene *models.Scene + Marker *models.SceneMarker + Overwrite bool + fileNamingAlgorithm models.HashAlgorithm } func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) { defer wg.Done() + if t.Scene != nil { + t.generateSceneMarkers() + } + + if t.Marker != nil { + qb := models.NewSceneQueryBuilder() + scene, err := qb.Find(int(t.Marker.SceneID.Int64)) + if err != nil { + logger.Errorf("error finding scene for marker: %s", err.Error()) + return + } + + videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path) + if err != nil { + logger.Errorf("error reading video file: %s", err.Error()) + return + } + + t.generateMarker(videoFile, scene, t.Marker) + } +} + +func (t *GenerateMarkersTask) generateSceneMarkers() { qb := models.NewSceneMarkerQueryBuilder() sceneMarkers, _ := qb.FindBySceneID(t.Scene.ID, nil) if len(sceneMarkers) == 0 { @@ -31,53 +56,65 @@ func (t *GenerateMarkersTask) Start(wg *sync.WaitGroup) { return } - // Make the folder for the scenes markers - markersFolder := filepath.Join(instance.Paths.Generated.Markers, t.Scene.Checksum) - _ = utils.EnsureDir(markersFolder) + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + + // Make the folder for the scenes markers + markersFolder := filepath.Join(instance.Paths.Generated.Markers, sceneHash) + utils.EnsureDir(markersFolder) - encoder := ffmpeg.NewEncoder(instance.FFMPEGPath) for i, sceneMarker := range sceneMarkers { index := i + 1 - logger.Progressf("[generator] <%s> scene marker %d of %d", t.Scene.Checksum, index, len(sceneMarkers)) + logger.Progressf("[generator] <%s> scene marker %d of %d", sceneHash, index, len(sceneMarkers)) - seconds := int(sceneMarker.Seconds) - baseFilename := strconv.Itoa(seconds) + t.generateMarker(videoFile, t.Scene, sceneMarker) + } +} + +func (t *GenerateMarkersTask) generateMarker(videoFile *ffmpeg.VideoFile, scene *models.Scene, sceneMarker *models.SceneMarker) { + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + seconds := int(sceneMarker.Seconds) + + videoExists := t.videoExists(sceneHash, seconds) + imageExists := t.imageExists(sceneHash, seconds) + + baseFilename := strconv.Itoa(seconds) + + options := ffmpeg.SceneMarkerOptions{ + ScenePath: scene.Path, + Seconds: seconds, + Width: 640, + } + + encoder := ffmpeg.NewEncoder(instance.FFMPEGPath) + + if t.Overwrite || !videoExists { videoFilename := baseFilename + ".mp4" + videoPath := instance.Paths.SceneMarkers.GetStreamPath(sceneHash, seconds) + + options.OutputPath = instance.Paths.Generated.GetTmpPath(videoFilename) // tmp output in case the process ends abruptly + if err := encoder.SceneMarkerVideo(*videoFile, options); err != nil { + logger.Errorf("[generator] failed to generate marker video: %s", err) + } else { + _ = utils.SafeMove(options.OutputPath, videoPath) + logger.Debug("created marker video: ", videoPath) + } + } + + if t.Overwrite || !imageExists { imageFilename := baseFilename + ".webp" - videoPath := instance.Paths.SceneMarkers.GetStreamPath(t.Scene.Checksum, seconds) - imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(t.Scene.Checksum, seconds) - videoExists, _ := utils.FileExists(videoPath) - imageExists, _ := utils.FileExists(imagePath) + imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(sceneHash, seconds) - options := ffmpeg.SceneMarkerOptions{ - ScenePath: t.Scene.Path, - Seconds: seconds, - Width: 640, - } - if !videoExists { - options.OutputPath = instance.Paths.Generated.GetTmpPath(videoFilename) // tmp output in case the process ends abruptly - if err := encoder.SceneMarkerVideo(*videoFile, options); err != nil { - logger.Errorf("[generator] failed to generate marker video: %s", err) - } else { - _ = os.Rename(options.OutputPath, videoPath) - logger.Debug("created marker video: ", videoPath) - } - } - - if !imageExists { - options.OutputPath = instance.Paths.Generated.GetTmpPath(imageFilename) // tmp output in case the process ends abruptly - if err := encoder.SceneMarkerImage(*videoFile, options); err != nil { - logger.Errorf("[generator] failed to generate marker image: %s", err) - } else { - _ = os.Rename(options.OutputPath, imagePath) - logger.Debug("created marker image: ", videoPath) - } + options.OutputPath = instance.Paths.Generated.GetTmpPath(imageFilename) // tmp output in case the process ends abruptly + if err := encoder.SceneMarkerImage(*videoFile, options); err != nil { + logger.Errorf("[generator] failed to generate marker image: %s", err) + } else { + _ = utils.SafeMove(options.OutputPath, imagePath) + logger.Debug("created marker image: ", imagePath) } } } func (t *GenerateMarkersTask) isMarkerNeeded() int { - markers := 0 qb := models.NewSceneMarkerQueryBuilder() sceneMarkers, _ := qb.FindBySceneID(t.Scene.ID, nil) @@ -85,17 +122,49 @@ func (t *GenerateMarkersTask) isMarkerNeeded() int { return 0 } + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) for _, sceneMarker := range sceneMarkers { seconds := int(sceneMarker.Seconds) - videoPath := instance.Paths.SceneMarkers.GetStreamPath(t.Scene.Checksum, seconds) - imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(t.Scene.Checksum, seconds) - videoExists, _ := utils.FileExists(videoPath) - imageExists, _ := utils.FileExists(imagePath) - if (!videoExists) || (!imageExists) { + if t.Overwrite || !t.markerExists(sceneHash, seconds) { markers++ } - } + return markers } + +func (t *GenerateMarkersTask) markerExists(sceneChecksum string, seconds int) bool { + if sceneChecksum == "" { + return false + } + + videoPath := instance.Paths.SceneMarkers.GetStreamPath(sceneChecksum, seconds) + imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(sceneChecksum, seconds) + videoExists, _ := utils.FileExists(videoPath) + imageExists, _ := utils.FileExists(imagePath) + + return videoExists && imageExists +} + +func (t *GenerateMarkersTask) videoExists(sceneChecksum string, seconds int) bool { + if sceneChecksum == "" { + return false + } + + videoPath := instance.Paths.SceneMarkers.GetStreamPath(sceneChecksum, seconds) + videoExists, _ := utils.FileExists(videoPath) + + return videoExists +} + +func (t *GenerateMarkersTask) imageExists(sceneChecksum string, seconds int) bool { + if sceneChecksum == "" { + return false + } + + imagePath := instance.Paths.SceneMarkers.GetStreamPreviewImagePath(sceneChecksum, seconds) + imageExists, _ := utils.FileExists(imagePath) + + return imageExists +} diff --git a/pkg/manager/task_generate_preview.go b/pkg/manager/task_generate_preview.go index 4aba82095..d39240fbd 100644 --- a/pkg/manager/task_generate_preview.go +++ b/pkg/manager/task_generate_preview.go @@ -1,17 +1,22 @@ package manager import ( + "sync" + "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "sync" ) type GeneratePreviewTask struct { - Scene models.Scene - ImagePreview bool - PreviewPreset string + Scene models.Scene + ImagePreview bool + + Options models.GeneratePreviewOptionsInput + + Overwrite bool + fileNamingAlgorithm models.HashAlgorithm } func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) { @@ -19,8 +24,7 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) { videoFilename := t.videoFilename() imageFilename := t.imageFilename() - videoExists := t.doesVideoPreviewExist(t.Scene.Checksum) - if (!t.ImagePreview || t.doesImagePreviewExist(t.Scene.Checksum)) && videoExists { + if !t.Overwrite && !t.required() { return } @@ -30,11 +34,19 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) { return } - generator, err := NewPreviewGenerator(*videoFile, videoFilename, imageFilename, instance.Paths.Generated.Screenshots, !videoExists, t.ImagePreview, t.PreviewPreset) + const generateVideo = true + generator, err := NewPreviewGenerator(*videoFile, videoFilename, imageFilename, instance.Paths.Generated.Screenshots, generateVideo, t.ImagePreview, t.Options.PreviewPreset.String()) if err != nil { logger.Errorf("error creating preview generator: %s", err.Error()) return } + generator.Overwrite = t.Overwrite + + // set the preview generation configuration from the global config + generator.Info.ChunkCount = *t.Options.PreviewSegments + generator.Info.ChunkDuration = *t.Options.PreviewSegmentDuration + generator.Info.ExcludeStart = *t.Options.PreviewExcludeStart + generator.Info.ExcludeEnd = *t.Options.PreviewExcludeEnd if err := generator.Generate(); err != nil { logger.Errorf("error generating preview: %s", err.Error()) @@ -42,20 +54,35 @@ func (t *GeneratePreviewTask) Start(wg *sync.WaitGroup) { } } +func (t GeneratePreviewTask) required() bool { + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + videoExists := t.doesVideoPreviewExist(sceneHash) + imageExists := !t.ImagePreview || t.doesImagePreviewExist(sceneHash) + return !imageExists || !videoExists +} + func (t *GeneratePreviewTask) doesVideoPreviewExist(sceneChecksum string) bool { + if sceneChecksum == "" { + return false + } + videoExists, _ := utils.FileExists(instance.Paths.Scene.GetStreamPreviewPath(sceneChecksum)) return videoExists } func (t *GeneratePreviewTask) doesImagePreviewExist(sceneChecksum string) bool { + if sceneChecksum == "" { + return false + } + imageExists, _ := utils.FileExists(instance.Paths.Scene.GetStreamPreviewImagePath(sceneChecksum)) return imageExists } func (t *GeneratePreviewTask) videoFilename() string { - return t.Scene.Checksum + ".mp4" + return t.Scene.GetHash(t.fileNamingAlgorithm) + ".mp4" } func (t *GeneratePreviewTask) imageFilename() string { - return t.Scene.Checksum + ".webp" + return t.Scene.GetHash(t.fileNamingAlgorithm) + ".webp" } diff --git a/pkg/manager/task_generate_screenshot.go b/pkg/manager/task_generate_screenshot.go index c4360b482..f6009fd82 100644 --- a/pkg/manager/task_generate_screenshot.go +++ b/pkg/manager/task_generate_screenshot.go @@ -14,8 +14,9 @@ import ( ) type GenerateScreenshotTask struct { - Scene models.Scene - ScreenshotAt *float64 + Scene models.Scene + ScreenshotAt *float64 + fileNamingAlgorithm models.HashAlgorithm } func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) { @@ -36,7 +37,7 @@ func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) { at = *t.ScreenshotAt } - checksum := t.Scene.Checksum + checksum := t.Scene.GetHash(t.fileNamingAlgorithm) normalPath := instance.Paths.Scene.GetScreenshotPath(checksum) // we'll generate the screenshot, grab the generated data and set it @@ -69,11 +70,24 @@ func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) { UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime}, } - updatedScene.Cover = &coverImageData - err = SetSceneScreenshot(t.Scene.Checksum, coverImageData) + if err := SetSceneScreenshot(checksum, coverImageData); err != nil { + logger.Errorf("Error writing screenshot: %s", err.Error()) + tx.Rollback() + return + } + + // update the scene cover table + if err := qb.UpdateSceneCover(t.Scene.ID, coverImageData, tx); err != nil { + logger.Errorf("Error setting screenshot: %s", err.Error()) + tx.Rollback() + return + } + + // update the scene with the update date _, err = qb.Update(updatedScene, tx) if err != nil { - logger.Errorf("Error setting screenshot: %s", err.Error()) + logger.Errorf("Error updating scene: %s", err.Error()) + tx.Rollback() return } diff --git a/pkg/manager/task_generate_sprite.go b/pkg/manager/task_generate_sprite.go index 274098045..b43232557 100644 --- a/pkg/manager/task_generate_sprite.go +++ b/pkg/manager/task_generate_sprite.go @@ -1,21 +1,24 @@ package manager import ( + "sync" + "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" - "sync" ) type GenerateSpriteTask struct { - Scene models.Scene + Scene models.Scene + Overwrite bool + fileNamingAlgorithm models.HashAlgorithm } func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) { defer wg.Done() - if t.doesSpriteExist(t.Scene.Checksum) { + if !t.Overwrite && !t.required() { return } @@ -25,13 +28,15 @@ func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) { return } - imagePath := instance.Paths.Scene.GetSpriteImageFilePath(t.Scene.Checksum) - vttPath := instance.Paths.Scene.GetSpriteVttFilePath(t.Scene.Checksum) + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + imagePath := instance.Paths.Scene.GetSpriteImageFilePath(sceneHash) + vttPath := instance.Paths.Scene.GetSpriteVttFilePath(sceneHash) generator, err := NewSpriteGenerator(*videoFile, imagePath, vttPath, 9, 9) if err != nil { logger.Errorf("error creating sprite generator: %s", err.Error()) return } + generator.Overwrite = t.Overwrite if err := generator.Generate(); err != nil { logger.Errorf("error generating sprite: %s", err.Error()) @@ -39,7 +44,17 @@ func (t *GenerateSpriteTask) Start(wg *sync.WaitGroup) { } } +// required returns true if the sprite needs to be generated +func (t GenerateSpriteTask) required() bool { + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + return !t.doesSpriteExist(sceneHash) +} + func (t *GenerateSpriteTask) doesSpriteExist(sceneChecksum string) bool { + if sceneChecksum == "" { + return false + } + imageExists, _ := utils.FileExists(instance.Paths.Scene.GetSpriteImageFilePath(sceneChecksum)) vttExists, _ := utils.FileExists(instance.Paths.Scene.GetSpriteVttFilePath(sceneChecksum)) return imageExists && vttExists diff --git a/pkg/manager/task_import.go b/pkg/manager/task_import.go index efc2d62c2..c721414bc 100644 --- a/pkg/manager/task_import.go +++ b/pkg/manager/task_import.go @@ -3,6 +3,7 @@ package manager import ( "context" "database/sql" + "fmt" "strconv" "sync" "time" @@ -17,8 +18,9 @@ import ( ) type ImportTask struct { - Mappings *jsonschema.Mappings - Scraped []jsonschema.ScrapedItem + Mappings *jsonschema.Mappings + Scraped []jsonschema.ScrapedItem + fileNamingAlgorithm models.HashAlgorithm } func (t *ImportTask) Start(wg *sync.WaitGroup) { @@ -44,11 +46,11 @@ func (t *ImportTask) Start(wg *sync.WaitGroup) { ctx := context.TODO() + t.ImportTags(ctx) t.ImportPerformers(ctx) t.ImportStudios(ctx) t.ImportMovies(ctx) t.ImportGalleries(ctx) - t.ImportTags(ctx) t.ImportScrapedItems(ctx) t.ImportScenes(ctx) @@ -75,16 +77,18 @@ func (t *ImportTask) ImportPerformers(ctx context.Context) { checksum := utils.MD5FromString(performerJSON.Name) // Process the base 64 encoded image string - _, imageData, err := utils.ProcessBase64Image(performerJSON.Image) - if err != nil { - _ = tx.Rollback() - logger.Errorf("[performers] <%s> invalid image: %s", mappingJSON.Checksum, err.Error()) - return + var imageData []byte + if len(performerJSON.Image) > 0 { + _, imageData, err = utils.ProcessBase64Image(performerJSON.Image) + if err != nil { + _ = tx.Rollback() + logger.Errorf("[performers] <%s> invalid image: %s", mappingJSON.Checksum, err.Error()) + return + } } // Populate a new performer from the input newPerformer := models.Performer{ - Image: imageData, Checksum: checksum, Favorite: sql.NullBool{Bool: performerJSON.Favorite, Valid: true}, CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(performerJSON.CreatedAt)}, @@ -140,12 +144,21 @@ func (t *ImportTask) ImportPerformers(ctx context.Context) { newPerformer.Instagram = sql.NullString{String: performerJSON.Instagram, Valid: true} } - _, err = qb.Create(newPerformer, tx) + createdPerformer, err := qb.Create(newPerformer, tx) if err != nil { _ = tx.Rollback() logger.Errorf("[performers] <%s> failed to create: %s", mappingJSON.Checksum, err.Error()) return } + + // Add the performer image if set + if len(imageData) > 0 { + if err := qb.UpdatePerformerImage(createdPerformer.ID, imageData, tx); err != nil { + _ = tx.Rollback() + logger.Errorf("[performers] <%s> error setting performer image: %s", mappingJSON.Checksum, err.Error()) + return + } + } } logger.Info("[performers] importing") @@ -157,7 +170,8 @@ func (t *ImportTask) ImportPerformers(ctx context.Context) { func (t *ImportTask) ImportStudios(ctx context.Context) { tx := database.DB.MustBeginTx(ctx, nil) - qb := models.NewStudioQueryBuilder() + + pendingParent := make(map[string][]*jsonschema.Studio) for i, mappingJSON := range t.Mappings.Studios { index := i + 1 @@ -172,35 +186,28 @@ func (t *ImportTask) ImportStudios(ctx context.Context) { logger.Progressf("[studios] %d of %d", index, len(t.Mappings.Studios)) - // generate checksum from studio name rather than image - checksum := utils.MD5FromString(studioJSON.Name) - - // Process the base 64 encoded image string - _, imageData, err := utils.ProcessBase64Image(studioJSON.Image) - if err != nil { - _ = tx.Rollback() - logger.Errorf("[studios] <%s> invalid image: %s", mappingJSON.Checksum, err.Error()) - return - } - - // Populate a new studio from the input - newStudio := models.Studio{ - Image: imageData, - Checksum: checksum, - Name: sql.NullString{String: studioJSON.Name, Valid: true}, - URL: sql.NullString{String: studioJSON.URL, Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.CreatedAt)}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.UpdatedAt)}, - } - - _, err = qb.Create(newStudio, tx) - if err != nil { - _ = tx.Rollback() + if err := t.ImportStudio(studioJSON, pendingParent, tx); err != nil { + tx.Rollback() logger.Errorf("[studios] <%s> failed to create: %s", mappingJSON.Checksum, err.Error()) return } } + // create the leftover studios, warning for missing parents + if len(pendingParent) > 0 { + logger.Warnf("[studios] importing studios with missing parents") + + for _, s := range pendingParent { + for _, orphanStudioJSON := range s { + if err := t.ImportStudio(orphanStudioJSON, nil, tx); err != nil { + tx.Rollback() + logger.Errorf("[studios] <%s> failed to create: %s", orphanStudioJSON.Name, err.Error()) + return + } + } + } + } + logger.Info("[studios] importing") if err := tx.Commit(); err != nil { logger.Errorf("[studios] import failed to commit: %s", err.Error()) @@ -208,6 +215,83 @@ func (t *ImportTask) ImportStudios(ctx context.Context) { logger.Info("[studios] import complete") } +func (t *ImportTask) ImportStudio(studioJSON *jsonschema.Studio, pendingParent map[string][]*jsonschema.Studio, tx *sqlx.Tx) error { + qb := models.NewStudioQueryBuilder() + + // generate checksum from studio name rather than image + checksum := utils.MD5FromString(studioJSON.Name) + + // Process the base 64 encoded image string + var imageData []byte + var err error + if len(studioJSON.Image) > 0 { + _, imageData, err = utils.ProcessBase64Image(studioJSON.Image) + if err != nil { + return fmt.Errorf("invalid image: %s", err.Error()) + } + } + + // Populate a new studio from the input + newStudio := models.Studio{ + Checksum: checksum, + Name: sql.NullString{String: studioJSON.Name, Valid: true}, + URL: sql.NullString{String: studioJSON.URL, Valid: true}, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(studioJSON.UpdatedAt)}, + } + + // Populate the parent ID + if studioJSON.ParentStudio != "" { + studio, err := qb.FindByName(studioJSON.ParentStudio, tx, false) + if err != nil { + return fmt.Errorf("error finding studio by name <%s>: %s", studioJSON.ParentStudio, err.Error()) + } + + if studio == nil { + // its possible that the parent hasn't been created yet + // do it after it is created + if pendingParent == nil { + logger.Warnf("[studios] studio <%s> does not exist", studioJSON.ParentStudio) + } else { + // add to the pending parent list so that it is created after the parent + s := pendingParent[studioJSON.ParentStudio] + s = append(s, studioJSON) + pendingParent[studioJSON.ParentStudio] = s + + // skip + return nil + } + } else { + newStudio.ParentID = sql.NullInt64{Int64: int64(studio.ID), Valid: true} + } + } + + createdStudio, err := qb.Create(newStudio, tx) + if err != nil { + return err + } + + if len(imageData) > 0 { + if err := qb.UpdateStudioImage(createdStudio.ID, imageData, tx); err != nil { + return fmt.Errorf("error setting studio image: %s", err.Error()) + } + } + + // now create the studios pending this studios creation + s := pendingParent[studioJSON.Name] + for _, childStudioJSON := range s { + // map is nil since we're not checking parent studios at this point + if err := t.ImportStudio(childStudioJSON, nil, tx); err != nil { + return fmt.Errorf("failed to create child studio <%s>: %s", childStudioJSON.Name, err.Error()) + } + } + + // delete the entry from the map so that we know its not left over + delete(pendingParent, studioJSON.Name) + + return err +} + func (t *ImportTask) ImportMovies(ctx context.Context) { tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewMovieQueryBuilder() @@ -229,32 +313,36 @@ func (t *ImportTask) ImportMovies(ctx context.Context) { checksum := utils.MD5FromString(movieJSON.Name) // Process the base 64 encoded image string - _, frontimageData, err := utils.ProcessBase64Image(movieJSON.FrontImage) - if err != nil { - _ = tx.Rollback() - logger.Errorf("[movies] <%s> invalid front_image: %s", mappingJSON.Checksum, err.Error()) - return + var frontimageData []byte + var backimageData []byte + if len(movieJSON.FrontImage) > 0 { + _, frontimageData, err = utils.ProcessBase64Image(movieJSON.FrontImage) + if err != nil { + _ = tx.Rollback() + logger.Errorf("[movies] <%s> invalid front_image: %s", mappingJSON.Checksum, err.Error()) + return + } } - _, backimageData, err := utils.ProcessBase64Image(movieJSON.BackImage) - if err != nil { - _ = tx.Rollback() - logger.Errorf("[movies] <%s> invalid back_image: %s", mappingJSON.Checksum, err.Error()) - return + if len(movieJSON.BackImage) > 0 { + _, backimageData, err = utils.ProcessBase64Image(movieJSON.BackImage) + if err != nil { + _ = tx.Rollback() + logger.Errorf("[movies] <%s> invalid back_image: %s", mappingJSON.Checksum, err.Error()) + return + } } // Populate a new movie from the input newMovie := models.Movie{ - FrontImage: frontimageData, - BackImage: backimageData, - Checksum: checksum, - Name: sql.NullString{String: movieJSON.Name, Valid: true}, - Aliases: sql.NullString{String: movieJSON.Aliases, Valid: true}, - Date: models.SQLiteDate{String: movieJSON.Date, Valid: true}, - Director: sql.NullString{String: movieJSON.Director, Valid: true}, - Synopsis: sql.NullString{String: movieJSON.Synopsis, Valid: true}, - URL: sql.NullString{String: movieJSON.URL, Valid: true}, - CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(movieJSON.CreatedAt)}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(movieJSON.UpdatedAt)}, + Checksum: checksum, + Name: sql.NullString{String: movieJSON.Name, Valid: true}, + Aliases: sql.NullString{String: movieJSON.Aliases, Valid: true}, + Date: models.SQLiteDate{String: movieJSON.Date, Valid: true}, + Director: sql.NullString{String: movieJSON.Director, Valid: true}, + Synopsis: sql.NullString{String: movieJSON.Synopsis, Valid: true}, + URL: sql.NullString{String: movieJSON.URL, Valid: true}, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(movieJSON.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(movieJSON.UpdatedAt)}, } if movieJSON.Rating != 0 { @@ -264,12 +352,34 @@ func (t *ImportTask) ImportMovies(ctx context.Context) { newMovie.Duration = sql.NullInt64{Int64: int64(movieJSON.Duration), Valid: true} } - _, err = qb.Create(newMovie, tx) + // Populate the studio ID + if movieJSON.Studio != "" { + sqb := models.NewStudioQueryBuilder() + studio, err := sqb.FindByName(movieJSON.Studio, tx, false) + if err != nil { + logger.Warnf("[movies] error getting studio <%s>: %s", movieJSON.Studio, err.Error()) + } else if studio == nil { + logger.Warnf("[movies] studio <%s> does not exist", movieJSON.Studio) + } else { + newMovie.StudioID = sql.NullInt64{Int64: int64(studio.ID), Valid: true} + } + } + + createdMovie, err := qb.Create(newMovie, tx) if err != nil { _ = tx.Rollback() logger.Errorf("[movies] <%s> failed to create: %s", mappingJSON.Checksum, err.Error()) return } + + // Add the movie images if set + if len(frontimageData) > 0 { + if err := qb.UpdateMovieImages(createdMovie.ID, frontimageData, backimageData, tx); err != nil { + _ = tx.Rollback() + logger.Errorf("[movies] <%s> error setting movie images: %s", mappingJSON.Checksum, err.Error()) + return + } + } } logger.Info("[movies] importing") @@ -319,61 +429,52 @@ func (t *ImportTask) ImportTags(ctx context.Context) { tx := database.DB.MustBeginTx(ctx, nil) qb := models.NewTagQueryBuilder() - var tagNames []string - - for i, mappingJSON := range t.Mappings.Scenes { + for i, mappingJSON := range t.Mappings.Tags { index := i + 1 - if mappingJSON.Checksum == "" || mappingJSON.Path == "" { - _ = tx.Rollback() - logger.Warn("[tags] scene mapping without checksum or path: ", mappingJSON) + tagJSON, err := instance.JSON.getTag(mappingJSON.Checksum) + if err != nil { + logger.Errorf("[tags] failed to read json: %s", err.Error()) + continue + } + if mappingJSON.Checksum == "" || mappingJSON.Name == "" || tagJSON == nil { return } - logger.Progressf("[tags] %d of %d scenes", index, len(t.Mappings.Scenes)) + logger.Progressf("[tags] %d of %d", index, len(t.Mappings.Tags)) - sceneJSON, err := instance.JSON.getScene(mappingJSON.Checksum) - if err != nil { - logger.Infof("[tags] <%s> json parse failure: %s", mappingJSON.Checksum, err.Error()) - } - // Return early if we are missing a json file. - if sceneJSON == nil { - continue - } - - // Get the tags from the tags json if we have it - if len(sceneJSON.Tags) > 0 { - tagNames = append(tagNames, sceneJSON.Tags...) - } - - // Get the tags from the markers if we have marker json - if len(sceneJSON.Markers) == 0 { - continue - } - for _, markerJSON := range sceneJSON.Markers { - if markerJSON.PrimaryTag != "" { - tagNames = append(tagNames, markerJSON.PrimaryTag) - } - if len(markerJSON.Tags) > 0 { - tagNames = append(tagNames, markerJSON.Tags...) + // Process the base 64 encoded image string + var imageData []byte + if len(tagJSON.Image) > 0 { + _, imageData, err = utils.ProcessBase64Image(tagJSON.Image) + if err != nil { + _ = tx.Rollback() + logger.Errorf("[tags] <%s> invalid image: %s", mappingJSON.Checksum, err.Error()) + return } } - } - uniqueTagNames := t.getUnique(tagNames) - for _, tagName := range uniqueTagNames { - currentTime := time.Now() + // Populate a new tag from the input newTag := models.Tag{ - Name: tagName, - CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, - UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, + Name: tagJSON.Name, + CreatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(tagJSON.CreatedAt)}, + UpdatedAt: models.SQLiteTimestamp{Timestamp: t.getTimeFromJSONTime(tagJSON.UpdatedAt)}, } - _, err := qb.Create(newTag, tx) + createdTag, err := qb.Create(newTag, tx) if err != nil { _ = tx.Rollback() - logger.Errorf("[tags] <%s> failed to create: %s", tagName, err.Error()) + logger.Errorf("[tags] <%s> failed to create: %s", mappingJSON.Checksum, err.Error()) return } + + // Add the tag image if set + if len(imageData) > 0 { + if err := qb.UpdateTagImage(createdTag.ID, imageData, tx); err != nil { + _ = tx.Rollback() + logger.Errorf("[tags] <%s> error setting tag image: %s", mappingJSON.Checksum, err.Error()) + return + } + } } logger.Info("[tags] importing") @@ -446,29 +547,33 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { logger.Progressf("[scenes] %d of %d", index, len(t.Mappings.Scenes)) - newScene := models.Scene{ - Checksum: mappingJSON.Checksum, - Path: mappingJSON.Path, - } - sceneJSON, err := instance.JSON.getScene(mappingJSON.Checksum) if err != nil { logger.Infof("[scenes] <%s> json parse failure: %s", mappingJSON.Checksum, err.Error()) continue } + sceneHash := mappingJSON.Checksum + + newScene := models.Scene{ + Checksum: sql.NullString{String: sceneJSON.Checksum, Valid: sceneJSON.Checksum != ""}, + OSHash: sql.NullString{String: sceneJSON.OSHash, Valid: sceneJSON.OSHash != ""}, + Path: mappingJSON.Path, + } + // Process the base 64 encoded cover image string + var coverImageData []byte if sceneJSON.Cover != "" { - _, coverImageData, err := utils.ProcessBase64Image(sceneJSON.Cover) + _, coverImageData, err = utils.ProcessBase64Image(sceneJSON.Cover) if err != nil { - logger.Warnf("[scenes] <%s> invalid cover image: %s", mappingJSON.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> invalid cover image: %s", sceneHash, err.Error()) } if len(coverImageData) > 0 { - if err = SetSceneScreenshot(mappingJSON.Checksum, coverImageData); err != nil { - logger.Warnf("[scenes] <%s> failed to create cover image: %s", mappingJSON.Checksum, err.Error()) - } else { - newScene.Cover = coverImageData + if err = SetSceneScreenshot(sceneHash, coverImageData); err != nil { + logger.Warnf("[scenes] <%s> failed to create cover image: %s", sceneHash, err.Error()) } + + // write the cover image data after creating the scene } } @@ -534,7 +639,9 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { sqb := models.NewStudioQueryBuilder() studio, err := sqb.FindByName(sceneJSON.Studio, tx, false) if err != nil { - logger.Warnf("[scenes] studio <%s> does not exist: %s", sceneJSON.Studio, err.Error()) + logger.Warnf("[scenes] error getting studio <%s>: %s", sceneJSON.Studio, err.Error()) + } else if studio == nil { + logger.Warnf("[scenes] studio <%s> does not exist", sceneJSON.Studio) } else { newScene.StudioID = sql.NullInt64{Int64: int64(studio.ID), Valid: true} } @@ -544,15 +651,24 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { scene, err := qb.Create(newScene, tx) if err != nil { _ = tx.Rollback() - logger.Errorf("[scenes] <%s> failed to create: %s", mappingJSON.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to create: %s", sceneHash, err.Error()) return } if scene.ID == 0 { _ = tx.Rollback() - logger.Errorf("[scenes] <%s> invalid id after scene creation", mappingJSON.Checksum) + logger.Errorf("[scenes] <%s> invalid id after scene creation", sceneHash) return } + // Add the scene cover if set + if len(coverImageData) > 0 { + if err := qb.UpdateSceneCover(scene.ID, coverImageData, tx); err != nil { + _ = tx.Rollback() + logger.Errorf("[scenes] <%s> error setting scene cover: %s", sceneHash, err.Error()) + return + } + } + // Relate the scene to the gallery if sceneJSON.Gallery != "" { gqb := models.NewGalleryQueryBuilder() @@ -563,7 +679,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { gallery.SceneID = sql.NullInt64{Int64: int64(scene.ID), Valid: true} _, err := gqb.Update(*gallery, tx) if err != nil { - logger.Errorf("[scenes] <%s> failed to update gallery: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to update gallery: %s", sceneHash, err.Error()) } } } @@ -572,7 +688,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { if len(sceneJSON.Performers) > 0 { performers, err := t.getPerformers(sceneJSON.Performers, tx) if err != nil { - logger.Warnf("[scenes] <%s> failed to fetch performers: %s", scene.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> failed to fetch performers: %s", sceneHash, err.Error()) } else { var performerJoins []models.PerformersScenes for _, performer := range performers { @@ -583,7 +699,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { performerJoins = append(performerJoins, join) } if err := jqb.CreatePerformersScenes(performerJoins, tx); err != nil { - logger.Errorf("[scenes] <%s> failed to associate performers: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to associate performers: %s", sceneHash, err.Error()) } } } @@ -592,19 +708,19 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { if len(sceneJSON.Movies) > 0 { moviesScenes, err := t.getMoviesScenes(sceneJSON.Movies, scene.ID, tx) if err != nil { - logger.Warnf("[scenes] <%s> failed to fetch movies: %s", scene.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> failed to fetch movies: %s", sceneHash, err.Error()) } else { if err := jqb.CreateMoviesScenes(moviesScenes, tx); err != nil { - logger.Errorf("[scenes] <%s> failed to associate movies: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to associate movies: %s", sceneHash, err.Error()) } } } // Relate the scene to the tags if len(sceneJSON.Tags) > 0 { - tags, err := t.getTags(scene.Checksum, sceneJSON.Tags, tx) + tags, err := t.getTags(sceneHash, sceneJSON.Tags, tx) if err != nil { - logger.Warnf("[scenes] <%s> failed to fetch tags: %s", scene.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> failed to fetch tags: %s", sceneHash, err.Error()) } else { var tagJoins []models.ScenesTags for _, tag := range tags { @@ -615,7 +731,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { tagJoins = append(tagJoins, join) } if err := jqb.CreateScenesTags(tagJoins, tx); err != nil { - logger.Errorf("[scenes] <%s> failed to associate tags: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to associate tags: %s", sceneHash, err.Error()) } } } @@ -636,7 +752,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { primaryTag, err := tqb.FindByName(marker.PrimaryTag, tx, false) if err != nil { - logger.Errorf("[scenes] <%s> failed to find primary tag for marker: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to find primary tag for marker: %s", sceneHash, err.Error()) } else { newSceneMarker.PrimaryTagID = primaryTag.ID } @@ -644,18 +760,18 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { // Create the scene marker in the DB sceneMarker, err := smqb.Create(newSceneMarker, tx) if err != nil { - logger.Warnf("[scenes] <%s> failed to create scene marker: %s", scene.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> failed to create scene marker: %s", sceneHash, err.Error()) continue } if sceneMarker.ID == 0 { - logger.Warnf("[scenes] <%s> invalid scene marker id after scene marker creation", scene.Checksum) + logger.Warnf("[scenes] <%s> invalid scene marker id after scene marker creation", sceneHash) continue } // Get the scene marker tags and create the joins - tags, err := t.getTags(scene.Checksum, marker.Tags, tx) + tags, err := t.getTags(sceneHash, marker.Tags, tx) if err != nil { - logger.Warnf("[scenes] <%s> failed to fetch scene marker tags: %s", scene.Checksum, err.Error()) + logger.Warnf("[scenes] <%s> failed to fetch scene marker tags: %s", sceneHash, err.Error()) } else { var tagJoins []models.SceneMarkersTags for _, tag := range tags { @@ -666,7 +782,7 @@ func (t *ImportTask) ImportScenes(ctx context.Context) { tagJoins = append(tagJoins, join) } if err := jqb.CreateSceneMarkersTags(tagJoins, tx); err != nil { - logger.Errorf("[scenes] <%s> failed to associate scene marker tags: %s", scene.Checksum, err.Error()) + logger.Errorf("[scenes] <%s> failed to associate scene marker tags: %s", sceneHash, err.Error()) } } } diff --git a/pkg/manager/task_migrate_hash.go b/pkg/manager/task_migrate_hash.go new file mode 100644 index 000000000..b11de548c --- /dev/null +++ b/pkg/manager/task_migrate_hash.go @@ -0,0 +1,86 @@ +package manager + +import ( + "os" + "path/filepath" + "sync" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" +) + +// MigrateHashTask renames generated files between oshash and MD5 based on the +// value of the fileNamingAlgorithm flag. +type MigrateHashTask struct { + Scene *models.Scene + fileNamingAlgorithm models.HashAlgorithm +} + +// Start starts the task. +func (t *MigrateHashTask) Start(wg *sync.WaitGroup) { + defer wg.Done() + + if !t.Scene.OSHash.Valid || !t.Scene.Checksum.Valid { + // nothing to do + return + } + + oshash := t.Scene.OSHash.String + checksum := t.Scene.Checksum.String + + oldHash := oshash + newHash := checksum + if t.fileNamingAlgorithm == models.HashAlgorithmOshash { + oldHash = checksum + newHash = oshash + } + + oldPath := filepath.Join(instance.Paths.Generated.Markers, oldHash) + newPath := filepath.Join(instance.Paths.Generated.Markers, newHash) + t.migrate(oldPath, newPath) + + scenePaths := GetInstance().Paths.Scene + oldPath = scenePaths.GetThumbnailScreenshotPath(oldHash) + newPath = scenePaths.GetThumbnailScreenshotPath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetScreenshotPath(oldHash) + newPath = scenePaths.GetScreenshotPath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetStreamPreviewPath(oldHash) + newPath = scenePaths.GetStreamPreviewPath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetStreamPreviewImagePath(oldHash) + newPath = scenePaths.GetStreamPreviewImagePath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetTranscodePath(oldHash) + newPath = scenePaths.GetTranscodePath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetSpriteVttFilePath(oldHash) + newPath = scenePaths.GetSpriteVttFilePath(newHash) + t.migrate(oldPath, newPath) + + oldPath = scenePaths.GetSpriteImageFilePath(oldHash) + newPath = scenePaths.GetSpriteImageFilePath(newHash) + t.migrate(oldPath, newPath) +} + +func (t *MigrateHashTask) migrate(oldName, newName string) { + oldExists, err := utils.FileExists(oldName) + if err != nil && !os.IsNotExist(err) { + logger.Errorf("Error checking existence of %s: %s", oldName, err.Error()) + return + } + + if oldExists { + logger.Infof("renaming %s to %s", oldName, newName) + if err := os.Rename(oldName, newName); err != nil { + logger.Errorf("error renaming %s to %s: %s", oldName, newName, err.Error()) + } + } +} diff --git a/pkg/manager/task_plugin.go b/pkg/manager/task_plugin.go new file mode 100644 index 000000000..1f8be17cb --- /dev/null +++ b/pkg/manager/task_plugin.go @@ -0,0 +1,71 @@ +package manager + +import ( + "time" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/plugin/common" +) + +func (s *singleton) RunPluginTask(pluginID string, taskName string, args []*models.PluginArgInput, serverConnection common.StashServerConnection) { + if s.Status.Status != Idle { + return + } + s.Status.SetStatus(PluginOperation) + s.Status.indefiniteProgress() + + go func() { + defer s.returnToIdleState() + + progress := make(chan float64) + task, err := s.PluginCache.CreateTask(pluginID, taskName, serverConnection, args, progress) + if err != nil { + logger.Errorf("Error creating plugin task: %s", err.Error()) + return + } + + err = task.Start() + if err != nil { + logger.Errorf("Error running plugin task: %s", err.Error()) + return + } + + done := make(chan bool) + go func() { + defer close(done) + task.Wait() + + output := task.GetResult() + if output == nil { + logger.Debug("Plugin returned no result") + } else { + if output.Error != nil { + logger.Errorf("Plugin returned error: %s", *output.Error) + } else if output.Output != nil { + logger.Debugf("Plugin returned: %v", output.Output) + } + } + }() + + // TODO - refactor stop to use channels + // check for stop every five seconds + pollingTime := time.Second * 5 + stopPoller := time.Tick(pollingTime) + for { + select { + case <-done: + return + case p := <-progress: + s.Status.setProgressPercent(p) + case <-stopPoller: + if s.Status.stopping { + if err := task.Stop(); err != nil { + logger.Errorf("Error stopping plugin operation: %s", err.Error()) + } + return + } + } + } + }() +} diff --git a/pkg/manager/task_scan.go b/pkg/manager/task_scan.go index 3e784ae56..1474b6b96 100644 --- a/pkg/manager/task_scan.go +++ b/pkg/manager/task_scan.go @@ -17,8 +17,10 @@ import ( ) type ScanTask struct { - FilePath string - UseFileMetadata bool + FilePath string + UseFileMetadata bool + calculateMD5 bool + fileNamingAlgorithm models.HashAlgorithm } func (t *ScanTask) Start(wg *sync.WaitGroup) { @@ -40,6 +42,11 @@ func (t *ScanTask) scanGallery() { return } + // Ignore directories. + if isDir, _ := utils.DirExists(t.FilePath); isDir { + return + } + ok, err := utils.IsZipFileUncompressed(t.FilePath) if err == nil && !ok { logger.Warnf("%s is using above store (0) level compression.", t.FilePath) @@ -64,7 +71,6 @@ func (t *ScanTask) scanGallery() { _, err = qb.Update(*gallery, tx) } } else { - logger.Infof("%s doesn't exist. Creating new item...", t.FilePath) currentTime := time.Now() newGallery := models.Gallery{ @@ -73,7 +79,12 @@ func (t *ScanTask) scanGallery() { CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, } - _, err = qb.Create(newGallery, tx) + + // don't create gallery if it has no images + if newGallery.CountFiles() > 0 { + logger.Infof("%s doesn't exist. Creating new item...", t.FilePath) + _, err = qb.Create(newGallery, tx) + } } if err != nil { @@ -89,8 +100,9 @@ func (t *ScanTask) associateGallery(wg *sync.WaitGroup) { qb := models.NewGalleryQueryBuilder() gallery, _ := qb.FindByPath(t.FilePath) if gallery == nil { - // shouldn't happen , associate is run after scan is finished - logger.Errorf("associate: gallery %s not found in DB", t.FilePath) + // associate is run after scan is finished + // should only happen if gallery is a directory or an io error occurs during hashing + logger.Warnf("associate: gallery %s not found in DB", t.FilePath) wg.Done() return } @@ -139,10 +151,10 @@ func (t *ScanTask) scanScene() { scene, _ := qb.FindByPath(t.FilePath) if scene != nil { // We already have this item in the database - //check for thumbnails,screenshots - t.makeScreenshots(nil, scene.Checksum) + // check for thumbnails,screenshots + t.makeScreenshots(nil, scene.GetHash(t.fileNamingAlgorithm)) - //check for container + // check for container if !scene.Format.Valid { videoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.FilePath) if err != nil { @@ -161,8 +173,67 @@ func (t *ScanTask) scanScene() { } else if err := tx.Commit(); err != nil { logger.Error(err.Error()) } - } + + // check if oshash is set + if !scene.OSHash.Valid { + logger.Infof("Calculating oshash for existing file %s ...", t.FilePath) + oshash, err := utils.OSHashFromFilePath(t.FilePath) + if err != nil { + logger.Error(err.Error()) + return + } + + // check if oshash clashes with existing scene + dupe, _ := qb.FindByOSHash(oshash) + if dupe != nil { + logger.Errorf("OSHash for file %s is the same as that of %s", t.FilePath, dupe.Path) + return + } + + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + err = qb.UpdateOSHash(scene.ID, oshash, tx) + if err != nil { + logger.Error(err.Error()) + tx.Rollback() + return + } else if err := tx.Commit(); err != nil { + logger.Error(err.Error()) + } + } + + // check if MD5 is set, if calculateMD5 is true + if t.calculateMD5 && !scene.Checksum.Valid { + checksum, err := t.calculateChecksum() + if err != nil { + logger.Error(err.Error()) + return + } + + // check if checksum clashes with existing scene + dupe, _ := qb.FindByChecksum(checksum) + if dupe != nil { + logger.Errorf("MD5 for file %s is the same as that of %s", t.FilePath, dupe.Path) + return + } + + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + err = qb.UpdateChecksum(scene.ID, checksum, tx) + if err != nil { + logger.Error(err.Error()) + _ = tx.Rollback() + } else if err := tx.Commit(); err != nil { + logger.Error(err.Error()) + } + } + + return + } + + // Ignore directories. + if isDir, _ := utils.DirExists(t.FilePath); isDir { return } @@ -178,15 +249,41 @@ func (t *ScanTask) scanScene() { videoFile.SetTitleFromPath() } - checksum, err := t.calculateChecksum() + var checksum string + + logger.Infof("%s not found. Calculating oshash...", t.FilePath) + oshash, err := utils.OSHashFromFilePath(t.FilePath) if err != nil { logger.Error(err.Error()) return } - t.makeScreenshots(videoFile, checksum) + if t.fileNamingAlgorithm == models.HashAlgorithmMd5 || t.calculateMD5 { + checksum, err = t.calculateChecksum() + if err != nil { + logger.Error(err.Error()) + return + } + } + + // check for scene by checksum and oshash - MD5 should be + // redundant, but check both + if checksum != "" { + scene, _ = qb.FindByChecksum(checksum) + } + + if scene == nil { + scene, _ = qb.FindByOSHash(oshash) + } + + sceneHash := oshash + + if t.fileNamingAlgorithm == models.HashAlgorithmMd5 { + sceneHash = checksum + } + + t.makeScreenshots(videoFile, sceneHash) - scene, _ = qb.FindByChecksum(checksum) ctx := context.TODO() tx := database.DB.MustBeginTx(ctx, nil) if scene != nil { @@ -205,7 +302,8 @@ func (t *ScanTask) scanScene() { logger.Infof("%s doesn't exist. Creating new item...", t.FilePath) currentTime := time.Now() newScene := models.Scene{ - Checksum: checksum, + Checksum: sql.NullString{String: checksum, Valid: checksum != ""}, + OSHash: sql.NullString{String: oshash, Valid: oshash != ""}, Path: t.FilePath, Title: sql.NullString{String: videoFile.Title, Valid: true}, Duration: sql.NullFloat64{Float64: videoFile.Duration, Valid: true}, @@ -273,7 +371,7 @@ func (t *ScanTask) makeScreenshots(probeResult *ffmpeg.VideoFile, checksum strin } func (t *ScanTask) calculateChecksum() (string, error) { - logger.Infof("%s not found. Calculating checksum...", t.FilePath) + logger.Infof("Calculating checksum for %s...", t.FilePath) checksum, err := utils.MD5FromFilePath(t.FilePath) if err != nil { return "", err diff --git a/pkg/manager/task_transcode.go b/pkg/manager/task_transcode.go index 7db3be0e3..d21ec4059 100644 --- a/pkg/manager/task_transcode.go +++ b/pkg/manager/task_transcode.go @@ -1,24 +1,25 @@ package manager import ( - "os" - "sync" - "github.com/stashapp/stash/pkg/ffmpeg" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" + "sync" ) type GenerateTranscodeTask struct { - Scene models.Scene + Scene models.Scene + Overwrite bool + fileNamingAlgorithm models.HashAlgorithm } func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { defer wg.Done() - hasTranscode, _ := HasTranscode(&t.Scene) - if hasTranscode { + hasTranscode := HasTranscode(&t.Scene, t.fileNamingAlgorithm) + if !t.Overwrite && hasTranscode { return } @@ -26,7 +27,6 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { if t.Scene.Format.Valid { container = ffmpeg.Container(t.Scene.Format.String) - } else { // container isn't in the DB // shouldn't happen unless user hasn't scanned after updating to PR#384+ version tmpVideoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, t.Scene.Path) @@ -44,7 +44,7 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { audioCodec = ffmpeg.AudioCodec(t.Scene.AudioCodec.String) } - if ffmpeg.IsValidCodec(videoCodec) && ffmpeg.IsValidCombo(videoCodec, container) && ffmpeg.IsValidAudioForContainer(audioCodec, container) { + if ffmpeg.IsStreamable(videoCodec, audioCodec, container) { return } @@ -54,7 +54,8 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { return } - outputPath := instance.Paths.Generated.GetTmpPath(t.Scene.Checksum + ".mp4") + sceneHash := t.Scene.GetHash(t.fileNamingAlgorithm) + outputPath := instance.Paths.Generated.GetTmpPath(sceneHash + ".mp4") transcodeSize := config.GetMaxTranscodeSize() options := ffmpeg.TranscodeOptions{ OutputPath: outputPath, @@ -77,12 +78,12 @@ func (t *GenerateTranscodeTask) Start(wg *sync.WaitGroup) { } } - if err := os.Rename(outputPath, instance.Paths.Scene.GetTranscodePath(t.Scene.Checksum)); err != nil { + if err := utils.SafeMove(outputPath, instance.Paths.Scene.GetTranscodePath(sceneHash)); err != nil { logger.Errorf("[transcode] error generating transcode: %s", err.Error()) return } - logger.Debugf("[transcode] <%s> created transcode: %s", t.Scene.Checksum, outputPath) + logger.Debugf("[transcode] <%s> created transcode: %s", sceneHash, outputPath) return } @@ -102,12 +103,12 @@ func (t *GenerateTranscodeTask) isTranscodeNeeded() bool { container = t.Scene.Format.String } - if ffmpeg.IsValidCodec(videoCodec) && ffmpeg.IsValidCombo(videoCodec, ffmpeg.Container(container)) && ffmpeg.IsValidAudioForContainer(audioCodec, ffmpeg.Container(container)) { + if ffmpeg.IsStreamable(videoCodec, audioCodec, ffmpeg.Container(container)) { return false } - hasTranscode, _ := HasTranscode(&t.Scene) - if hasTranscode { + hasTranscode := HasTranscode(&t.Scene, t.fileNamingAlgorithm) + if !t.Overwrite && hasTranscode { return false } return true diff --git a/pkg/manager/utils.go b/pkg/manager/utils.go deleted file mode 100644 index 38f747d95..000000000 --- a/pkg/manager/utils.go +++ /dev/null @@ -1,50 +0,0 @@ -package manager - -import ( - "fmt" - - "github.com/stashapp/stash/pkg/ffmpeg" - "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/models" - "github.com/stashapp/stash/pkg/utils" -) - -func IsStreamable(scene *models.Scene) (bool, error) { - if scene == nil { - return false, fmt.Errorf("nil scene") - } - var container ffmpeg.Container - if scene.Format.Valid { - container = ffmpeg.Container(scene.Format.String) - } else { // container isn't in the DB - // shouldn't happen, fallback to ffprobe reading from file - tmpVideoFile, err := ffmpeg.NewVideoFile(instance.FFProbePath, scene.Path) - if err != nil { - return false, fmt.Errorf("error reading video file: %s", err.Error()) - } - container = ffmpeg.MatchContainer(tmpVideoFile.Container, scene.Path) - } - - videoCodec := scene.VideoCodec.String - audioCodec := ffmpeg.MissingUnsupported - if scene.AudioCodec.Valid { - audioCodec = ffmpeg.AudioCodec(scene.AudioCodec.String) - } - - if ffmpeg.IsValidCodec(videoCodec) && ffmpeg.IsValidCombo(videoCodec, container) && ffmpeg.IsValidAudioForContainer(audioCodec, container) { - logger.Debugf("File is streamable %s, %s, %s\n", videoCodec, audioCodec, container) - return true, nil - } else { - hasTranscode, _ := HasTranscode(scene) - logger.Debugf("File is not streamable , transcode is needed %s, %s, %s\n", videoCodec, audioCodec, container) - return hasTranscode, nil - } -} - -func HasTranscode(scene *models.Scene) (bool, error) { - if scene == nil { - return false, fmt.Errorf("nil scene") - } - transcodePath := instance.Paths.Scene.GetTranscodePath(scene.Checksum) - return utils.FileExists(transcodePath) -} diff --git a/pkg/models/model_gallery.go b/pkg/models/model_gallery.go index 9f8a83f5d..fe864b866 100644 --- a/pkg/models/model_gallery.go +++ b/pkg/models/model_gallery.go @@ -4,17 +4,18 @@ import ( "archive/zip" "bytes" "database/sql" - "github.com/disintegration/imaging" - "github.com/stashapp/stash/pkg/api/urlbuilders" - "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/utils" - _ "golang.org/x/image/webp" "image" "image/jpeg" "io/ioutil" "path/filepath" "sort" "strings" + + "github.com/disintegration/imaging" + "github.com/stashapp/stash/pkg/api/urlbuilders" + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/utils" + _ "golang.org/x/image/webp" ) type Gallery struct { @@ -28,6 +29,16 @@ type Gallery struct { const DefaultGthumbWidth int = 200 +func (g *Gallery) CountFiles() int { + filteredFiles, readCloser, err := g.listZipContents() + if err != nil { + return 0 + } + defer readCloser.Close() + + return len(filteredFiles) +} + func (g *Gallery) GetFiles(baseURL string) []*GalleryFilesType { var galleryFiles []*GalleryFilesType filteredFiles, readCloser, err := g.listZipContents() diff --git a/pkg/models/model_movie.go b/pkg/models/model_movie.go index 865fcab0e..bc9939b25 100644 --- a/pkg/models/model_movie.go +++ b/pkg/models/model_movie.go @@ -5,39 +5,35 @@ import ( ) type Movie struct { - ID int `db:"id" json:"id"` - FrontImage []byte `db:"front_image" json:"front_image"` - BackImage []byte `db:"back_image" json:"back_image"` - Checksum string `db:"checksum" json:"checksum"` - Name sql.NullString `db:"name" json:"name"` - Aliases sql.NullString `db:"aliases" json:"aliases"` - Duration sql.NullInt64 `db:"duration" json:"duration"` - Date SQLiteDate `db:"date" json:"date"` - Rating sql.NullInt64 `db:"rating" json:"rating"` - StudioID sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"` - Director sql.NullString `db:"director" json:"director"` - Synopsis sql.NullString `db:"synopsis" json:"synopsis"` - URL sql.NullString `db:"url" json:"url"` - CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"` - UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` + ID int `db:"id" json:"id"` + Checksum string `db:"checksum" json:"checksum"` + Name sql.NullString `db:"name" json:"name"` + Aliases sql.NullString `db:"aliases" json:"aliases"` + Duration sql.NullInt64 `db:"duration" json:"duration"` + Date SQLiteDate `db:"date" json:"date"` + Rating sql.NullInt64 `db:"rating" json:"rating"` + StudioID sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"` + Director sql.NullString `db:"director" json:"director"` + Synopsis sql.NullString `db:"synopsis" json:"synopsis"` + URL sql.NullString `db:"url" json:"url"` + CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"` + UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` } type MoviePartial struct { - ID int `db:"id" json:"id"` - FrontImage *[]byte `db:"front_image" json:"front_image"` - BackImage *[]byte `db:"back_image" json:"back_image"` - Checksum *string `db:"checksum" json:"checksum"` - Name *sql.NullString `db:"name" json:"name"` - Aliases *sql.NullString `db:"aliases" json:"aliases"` - Duration *sql.NullInt64 `db:"duration" json:"duration"` - Date *SQLiteDate `db:"date" json:"date"` - Rating *sql.NullInt64 `db:"rating" json:"rating"` - StudioID *sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"` - Director *sql.NullString `db:"director" json:"director"` - Synopsis *sql.NullString `db:"synopsis" json:"synopsis"` - URL *sql.NullString `db:"url" json:"url"` - CreatedAt *SQLiteTimestamp `db:"created_at" json:"created_at"` - UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"` + ID int `db:"id" json:"id"` + Checksum *string `db:"checksum" json:"checksum"` + Name *sql.NullString `db:"name" json:"name"` + Aliases *sql.NullString `db:"aliases" json:"aliases"` + Duration *sql.NullInt64 `db:"duration" json:"duration"` + Date *SQLiteDate `db:"date" json:"date"` + Rating *sql.NullInt64 `db:"rating" json:"rating"` + StudioID *sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"` + Director *sql.NullString `db:"director" json:"director"` + Synopsis *sql.NullString `db:"synopsis" json:"synopsis"` + URL *sql.NullString `db:"url" json:"url"` + CreatedAt *SQLiteTimestamp `db:"created_at" json:"created_at"` + UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"` } var DefaultMovieImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wgVBQsJl1CMZAAAASJJREFUeNrt3N0JwyAYhlEj3cj9R3Cm5rbkqtAP+qrnGaCYHPwJpLlaa++mmLpbAERAgAgIEAEBIiBABERAgAgIEAEBIiBABERAgAgIEAHZuVflj40x4i94zhk9vqsVvEq6AsQqMP1EjORx20OACAgQRRx7T+zzcFBxcjNDfoB4ntQqTm5Awo7MlqywZxcgYQ+RlqywJ3ozJAQCSBiEJSsQA0gYBpDAgAARECACAkRAgAgIEAERECACAmSjUv6eAOSB8m8YIGGzBUjYbAESBgMkbBkDEjZbgITBAClcxiqQvEoatreYIWEBASIgJ4Gkf11ntXH3nS9uxfGWfJ5J9hAgAgJEQAQEiIAAERAgAgJEQAQEiIAAERAgAgJEQAQEiL7qBuc6RKLHxr0CAAAAAElFTkSuQmCC" diff --git a/pkg/models/model_performer.go b/pkg/models/model_performer.go index 71818a025..12348bc75 100644 --- a/pkg/models/model_performer.go +++ b/pkg/models/model_performer.go @@ -6,7 +6,6 @@ import ( type Performer struct { ID int `db:"id" json:"id"` - Image []byte `db:"image" json:"image"` Checksum string `db:"checksum" json:"checksum"` Name sql.NullString `db:"name" json:"name"` Gender sql.NullString `db:"gender" json:"gender"` diff --git a/pkg/models/model_scene.go b/pkg/models/model_scene.go index 1623533be..a38d7960a 100644 --- a/pkg/models/model_scene.go +++ b/pkg/models/model_scene.go @@ -5,11 +5,12 @@ import ( "path/filepath" ) +// Scene stores the metadata for a single video scene. type Scene struct { ID int `db:"id" json:"id"` - Checksum string `db:"checksum" json:"checksum"` + Checksum sql.NullString `db:"checksum" json:"checksum"` + OSHash sql.NullString `db:"oshash" json:"oshash"` Path string `db:"path" json:"path"` - Cover []byte `db:"cover" json:"cover"` Title sql.NullString `db:"title" json:"title"` Details sql.NullString `db:"details" json:"details"` URL sql.NullString `db:"url" json:"url"` @@ -30,11 +31,13 @@ type Scene struct { UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` } +// ScenePartial represents part of a Scene object. It is used to update +// the database entry. Only non-nil fields will be updated. type ScenePartial struct { ID int `db:"id" json:"id"` - Checksum *string `db:"checksum" json:"checksum"` + Checksum *sql.NullString `db:"checksum" json:"checksum"` + OSHash *sql.NullString `db:"oshash" json:"oshash"` Path *string `db:"path" json:"path"` - Cover *[]byte `db:"cover" json:"cover"` Title *sql.NullString `db:"title" json:"title"` Details *sql.NullString `db:"details" json:"details"` URL *sql.NullString `db:"url" json:"url"` @@ -54,6 +57,8 @@ type ScenePartial struct { UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"` } +// GetTitle returns the title of the scene. If the Title field is empty, +// then the base filename is returned. func (s Scene) GetTitle() string { if s.Title.String != "" { return s.Title.String @@ -62,6 +67,19 @@ func (s Scene) GetTitle() string { return filepath.Base(s.Path) } +// GetHash returns the hash of the scene, based on the hash algorithm provided. If +// hash algorithm is MD5, then Checksum is returned. Otherwise, OSHash is returned. +func (s Scene) GetHash(hashAlgorithm HashAlgorithm) string { + if hashAlgorithm == HashAlgorithmMd5 { + return s.Checksum.String + } else if hashAlgorithm == HashAlgorithmOshash { + return s.OSHash.String + } + + panic("unknown hash algorithm") +} + +// SceneFileType represents the file metadata for a scene. type SceneFileType struct { Size *string `graphql:"size" json:"size"` Duration *float64 `graphql:"duration" json:"duration"` diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index 719274b5f..fd3314197 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -132,6 +132,27 @@ type ScrapedSceneMovie struct { type ScrapedSceneTag struct { // Set if tag matched - ID *string `graphql:"id" json:"id"` + ID *string `graphql:"stored_id" json:"stored_id"` Name string `graphql:"name" json:"name"` } + +type ScrapedMovie struct { + Name *string `graphql:"name" json:"name"` + Aliases *string `graphql:"aliases" json:"aliases"` + Duration *string `graphql:"duration" json:"duration"` + Date *string `graphql:"date" json:"date"` + Rating *string `graphql:"rating" json:"rating"` + Director *string `graphql:"director" json:"director"` + Studio *ScrapedMovieStudio `graphql:"studio" json:"studio"` + Synopsis *string `graphql:"synopsis" json:"synopsis"` + URL *string `graphql:"url" json:"url"` + FrontImage *string `graphql:"front_image" json:"front_image"` + BackImage *string `graphql:"back_image" json:"back_image"` +} + +type ScrapedMovieStudio struct { + // Set if studio matched + ID *string `graphql:"id" json:"id"` + Name string `graphql:"name" json:"name"` + URL *string `graphql:"url" json:"url"` +} diff --git a/pkg/models/model_studio.go b/pkg/models/model_studio.go index e978bb439..6880d3986 100644 --- a/pkg/models/model_studio.go +++ b/pkg/models/model_studio.go @@ -6,12 +6,22 @@ import ( type Studio struct { ID int `db:"id" json:"id"` - Image []byte `db:"image" json:"image"` Checksum string `db:"checksum" json:"checksum"` Name sql.NullString `db:"name" json:"name"` URL sql.NullString `db:"url" json:"url"` + ParentID sql.NullInt64 `db:"parent_id,omitempty" json:"parent_id"` CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"` UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` } +type StudioPartial struct { + ID int `db:"id" json:"id"` + Checksum *string `db:"checksum" json:"checksum"` + Name *sql.NullString `db:"name" json:"name"` + URL *sql.NullString `db:"url" json:"url"` + ParentID *sql.NullInt64 `db:"parent_id,omitempty" json:"parent_id"` + CreatedAt *SQLiteTimestamp `db:"created_at" json:"created_at"` + UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"` +} + var DefaultStudioImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wgVBQsJl1CMZAAAASJJREFUeNrt3N0JwyAYhlEj3cj9R3Cm5rbkqtAP+qrnGaCYHPwJpLlaa++mmLpbAERAgAgIEAEBIiBABERAgAgIEAEBIiBABERAgAgIEAHZuVflj40x4i94zhk9vqsVvEq6AsQqMP1EjORx20OACAgQRRx7T+zzcFBxcjNDfoB4ntQqTm5Awo7MlqywZxcgYQ+RlqywJ3ozJAQCSBiEJSsQA0gYBpDAgAARECACAkRAgAgIEAERECACAmSjUv6eAOSB8m8YIGGzBUjYbAESBgMkbBkDEjZbgITBAClcxiqQvEoatreYIWEBASIgJ4Gkf11ntXH3nS9uxfGWfJ5J9hAgAgJEQAQEiIAAERAgAgJEQAQEiIAAERAgAgJEQAQEiL7qBuc6RKLHxr0CAAAAAElFTkSuQmCC" diff --git a/pkg/models/model_tag.go b/pkg/models/model_tag.go index ee3c79d32..d62d83784 100644 --- a/pkg/models/model_tag.go +++ b/pkg/models/model_tag.go @@ -6,3 +6,132 @@ type Tag struct { CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"` UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` } + +// Original Tag image from: https://fontawesome.com/icons/tag?style=solid +// Modified to change color and rotate +// Licensed under CC Attribution 4.0: https://fontawesome.com/license +var DefaultTagImage = []byte(` + + + + + + image/svg+xml + + + + + + + + +`) + +// var DefaultTagImage = []byte(` +// +// +// +// +// +// image/svg+xml +// +// +// +// +// +// +// +// +// `) diff --git a/pkg/models/querybuilder_gallery.go b/pkg/models/querybuilder_gallery.go index 0f2b06e06..c5fdf532c 100644 --- a/pkg/models/querybuilder_gallery.go +++ b/pkg/models/querybuilder_gallery.go @@ -2,6 +2,7 @@ package models import ( "database/sql" + "fmt" "path/filepath" "strconv" @@ -9,6 +10,8 @@ import ( "github.com/stashapp/stash/pkg/database" ) +const galleryTable = "galleries" + type GalleryQueryBuilder struct{} func NewGalleryQueryBuilder() GalleryQueryBuilder { @@ -80,6 +83,24 @@ func (qb *GalleryQueryBuilder) Find(id int) (*Gallery, error) { return qb.queryGallery(query, args, nil) } +func (qb *GalleryQueryBuilder) FindMany(ids []int) ([]*Gallery, error) { + var galleries []*Gallery + for _, id := range ids { + gallery, err := qb.Find(id) + if err != nil { + return nil, err + } + + if gallery == nil { + return nil, fmt.Errorf("gallery with id %d not found", id) + } + + galleries = append(galleries, gallery) + } + + return galleries, nil +} + func (qb *GalleryQueryBuilder) FindByChecksum(checksum string, tx *sqlx.Tx) (*Gallery, error) { query := "SELECT * FROM galleries WHERE checksum = ? LIMIT 1" args := []interface{}{checksum} @@ -112,25 +133,36 @@ func (qb *GalleryQueryBuilder) All() ([]*Gallery, error) { return qb.queryGalleries(selectAll("galleries")+qb.getGallerySort(nil), nil, nil) } -func (qb *GalleryQueryBuilder) Query(findFilter *FindFilterType) ([]*Gallery, int) { +func (qb *GalleryQueryBuilder) Query(galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int) { + if galleryFilter == nil { + galleryFilter = &GalleryFilterType{} + } if findFilter == nil { findFilter = &FindFilterType{} } - var whereClauses []string - var havingClauses []string - var args []interface{} - body := selectDistinctIDs("galleries") + query := queryBuilder{ + tableName: galleryTable, + } + + query.body = selectDistinctIDs("galleries") if q := findFilter.Q; q != nil && *q != "" { searchColumns := []string{"galleries.path", "galleries.checksum"} clause, thisArgs := getSearchBinding(searchColumns, *q, false) - whereClauses = append(whereClauses, clause) - args = append(args, thisArgs...) + query.addWhere(clause) + query.addArg(thisArgs...) } - sortAndPagination := qb.getGallerySort(findFilter) + getPagination(findFilter) - idsResult, countResult := executeFindQuery("galleries", body, args, sortAndPagination, whereClauses, havingClauses) + if isMissingFilter := galleryFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { + switch *isMissingFilter { + case "scene": + query.addWhere("galleries.scene_id IS NULL") + } + } + + query.sortAndPagination = qb.getGallerySort(findFilter) + getPagination(findFilter) + idsResult, countResult := query.executeFind() var galleries []*Gallery for _, id := range idsResult { diff --git a/pkg/models/querybuilder_gallery_test.go b/pkg/models/querybuilder_gallery_test.go index d3a409d08..ad2234092 100644 --- a/pkg/models/querybuilder_gallery_test.go +++ b/pkg/models/querybuilder_gallery_test.go @@ -98,6 +98,58 @@ func TestGalleryFindBySceneID(t *testing.T) { assert.Nil(t, gallery) } +func TestGalleryQueryQ(t *testing.T) { + const galleryIdx = 0 + + q := getGalleryStringValue(galleryIdx, pathField) + + sqb := models.NewGalleryQueryBuilder() + + galleryQueryQ(t, sqb, q, galleryIdx) +} + +func galleryQueryQ(t *testing.T, qb models.GalleryQueryBuilder, q string, expectedGalleryIdx int) { + filter := models.FindFilterType{ + Q: &q, + } + galleries, _ := qb.Query(nil, &filter) + + assert.Len(t, galleries, 1) + gallery := galleries[0] + assert.Equal(t, galleryIDs[expectedGalleryIdx], gallery.ID) + + // no Q should return all results + filter.Q = nil + galleries, _ = qb.Query(nil, &filter) + + assert.Len(t, galleries, totalGalleries) +} + +func TestGalleryQueryIsMissingScene(t *testing.T) { + qb := models.NewGalleryQueryBuilder() + isMissing := "scene" + galleryFilter := models.GalleryFilterType{ + IsMissing: &isMissing, + } + + q := getGalleryStringValue(galleryIdxWithScene, titleField) + findFilter := models.FindFilterType{ + Q: &q, + } + + galleries, _ := qb.Query(&galleryFilter, &findFilter) + + assert.Len(t, galleries, 0) + + findFilter.Q = nil + galleries, _ = qb.Query(&galleryFilter, &findFilter) + + // ensure non of the ids equal the one with gallery + for _, gallery := range galleries { + assert.NotEqual(t, galleryIDs[galleryIdxWithScene], gallery.ID) + } +} + // TODO ValidGalleriesForScenePath // TODO Count // TODO All diff --git a/pkg/models/querybuilder_movies.go b/pkg/models/querybuilder_movies.go index cccd4d180..311548a31 100644 --- a/pkg/models/querybuilder_movies.go +++ b/pkg/models/querybuilder_movies.go @@ -2,7 +2,6 @@ package models import ( "database/sql" - "strconv" "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/database" @@ -17,8 +16,8 @@ func NewMovieQueryBuilder() MovieQueryBuilder { func (qb *MovieQueryBuilder) Create(newMovie Movie, tx *sqlx.Tx) (*Movie, error) { ensureTx(tx) result, err := tx.NamedExec( - `INSERT INTO movies (front_image, back_image, checksum, name, aliases, duration, date, rating, studio_id, director, synopsis, url, created_at, updated_at) - VALUES (:front_image, :back_image, :checksum, :name, :aliases, :duration, :date, :rating, :studio_id, :director, :synopsis, :url, :created_at, :updated_at) + `INSERT INTO movies (checksum, name, aliases, duration, date, rating, studio_id, director, synopsis, url, created_at, updated_at) + VALUES (:checksum, :name, :aliases, :duration, :date, :rating, :studio_id, :director, :synopsis, :url, :created_at, :updated_at) `, newMovie, ) @@ -148,11 +147,26 @@ func (qb *MovieQueryBuilder) Query(movieFilter *MovieFilterType, findFilter *Fin args = append(args, studioID) } - whereClause, havingClause := qb.getMultiCriterionClause("studio", "", "studio_id", studiosFilter) + whereClause, havingClause := getMultiCriterionClause("movies", "studio", "", "", "studio_id", studiosFilter) whereClauses = appendClause(whereClauses, whereClause) havingClauses = appendClause(havingClauses, havingClause) } + if isMissingFilter := movieFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { + switch *isMissingFilter { + case "front_image": + body += `left join movies_images on movies_images.movie_id = movies.id + ` + whereClauses = appendClause(whereClauses, "movies_images.front_image IS NULL") + case "back_image": + body += `left join movies_images on movies_images.movie_id = movies.id + ` + whereClauses = appendClause(whereClauses, "movies_images.back_image IS NULL") + default: + whereClauses = appendClause(whereClauses, "movies."+*isMissingFilter+" IS NULL") + } + } + sortAndPagination := qb.getMovieSort(findFilter) + getPagination(findFilter) idsResult, countResult := executeFindQuery("movies", body, args, sortAndPagination, whereClauses, havingClauses) @@ -165,29 +179,6 @@ func (qb *MovieQueryBuilder) Query(movieFilter *MovieFilterType, findFilter *Fin return movies, countResult } -// returns where clause and having clause -func (qb *MovieQueryBuilder) getMultiCriterionClause(table string, joinTable string, joinTableField string, criterion *MultiCriterionInput) (string, string) { - whereClause := "" - havingClause := "" - if criterion.Modifier == CriterionModifierIncludes { - // includes any of the provided ids - whereClause = table + ".id IN " + getInBinding(len(criterion.Value)) - } else if criterion.Modifier == CriterionModifierIncludesAll { - // includes all of the provided ids - whereClause = table + ".id IN " + getInBinding(len(criterion.Value)) - havingClause = "count(distinct " + table + ".id) IS " + strconv.Itoa(len(criterion.Value)) - } else if criterion.Modifier == CriterionModifierExcludes { - // excludes all of the provided ids - if joinTable != "" { - whereClause = "not exists (select " + joinTable + ".movie_id from " + joinTable + " where " + joinTable + ".movie_id = movies.id and " + joinTable + "." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")" - } else { - whereClause = "not exists (select m.id from movies as m where m.id = movies.id and m." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")" - } - } - - return whereClause, havingClause -} - func (qb *MovieQueryBuilder) getMovieSort(findFilter *FindFilterType) string { var sort string var direction string @@ -238,3 +229,42 @@ func (qb *MovieQueryBuilder) queryMovies(query string, args []interface{}, tx *s return movies, nil } + +func (qb *MovieQueryBuilder) UpdateMovieImages(movieID int, frontImage []byte, backImage []byte, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing cover and then create new + if err := qb.DestroyMovieImages(movieID, tx); err != nil { + return err + } + + _, err := tx.Exec( + `INSERT INTO movies_images (movie_id, front_image, back_image) VALUES (?, ?, ?)`, + movieID, + frontImage, + backImage, + ) + + return err +} + +func (qb *MovieQueryBuilder) DestroyMovieImages(movieID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM movies_images WHERE movie_id = ?", movieID) + if err != nil { + return err + } + return err +} + +func (qb *MovieQueryBuilder) GetFrontImage(movieID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT front_image from movies_images WHERE movie_id = ?` + return getImage(tx, query, movieID) +} + +func (qb *MovieQueryBuilder) GetBackImage(movieID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT back_image from movies_images WHERE movie_id = ?` + return getImage(tx, query, movieID) +} diff --git a/pkg/models/querybuilder_movies_test.go b/pkg/models/querybuilder_movies_test.go index a11f94f18..407350e36 100644 --- a/pkg/models/querybuilder_movies_test.go +++ b/pkg/models/querybuilder_movies_test.go @@ -3,12 +3,17 @@ package models_test import ( + "context" + "database/sql" + "strconv" "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) func TestMovieFindBySceneID(t *testing.T) { @@ -86,6 +91,182 @@ func TestMovieFindByNames(t *testing.T) { assert.Equal(t, strings.ToLower(movieNames[movieIdxWithScene]), strings.ToLower(movies[1].Name.String)) } +func TestMovieQueryStudio(t *testing.T) { + mqb := models.NewMovieQueryBuilder() + studioCriterion := models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(studioIDs[studioIdxWithMovie]), + }, + Modifier: models.CriterionModifierIncludes, + } + + movieFilter := models.MovieFilterType{ + Studios: &studioCriterion, + } + + movies, _ := mqb.Query(&movieFilter, nil) + + assert.Len(t, movies, 1) + + // ensure id is correct + assert.Equal(t, movieIDs[movieIdxWithStudio], movies[0].ID) + + studioCriterion = models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(studioIDs[studioIdxWithMovie]), + }, + Modifier: models.CriterionModifierExcludes, + } + + q := getMovieStringValue(movieIdxWithStudio, titleField) + findFilter := models.FindFilterType{ + Q: &q, + } + + movies, _ = mqb.Query(&movieFilter, &findFilter) + assert.Len(t, movies, 0) +} + +func TestMovieUpdateMovieImages(t *testing.T) { + mqb := models.NewMovieQueryBuilder() + + // create movie to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestMovieUpdateMovieImages" + movie := models.Movie{ + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), + } + created, err := mqb.Create(movie, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating movie: %s", err.Error()) + } + + frontImage := []byte("frontImage") + backImage := []byte("backImage") + err = mqb.UpdateMovieImages(created.ID, frontImage, backImage, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating movie images: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // ensure images are set + storedFront, err := mqb.GetFrontImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting front image: %s", err.Error()) + } + assert.Equal(t, storedFront, frontImage) + + storedBack, err := mqb.GetBackImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting back image: %s", err.Error()) + } + assert.Equal(t, storedBack, backImage) + + // set front image only + newImage := []byte("newImage") + tx = database.DB.MustBeginTx(ctx, nil) + err = mqb.UpdateMovieImages(created.ID, newImage, nil, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating movie images: %s", err.Error()) + } + + storedFront, err = mqb.GetFrontImage(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error getting front image: %s", err.Error()) + } + assert.Equal(t, storedFront, newImage) + + // back image should be nil + storedBack, err = mqb.GetBackImage(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error getting back image: %s", err.Error()) + } + assert.Nil(t, nil) + + // set back image only + err = mqb.UpdateMovieImages(created.ID, nil, newImage, tx) + if err == nil { + tx.Rollback() + t.Fatalf("Expected error setting nil front image") + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } +} + +func TestMovieDestroyMovieImages(t *testing.T) { + mqb := models.NewMovieQueryBuilder() + + // create movie to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestMovieDestroyMovieImages" + movie := models.Movie{ + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), + } + created, err := mqb.Create(movie, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating movie: %s", err.Error()) + } + + frontImage := []byte("frontImage") + backImage := []byte("backImage") + err = mqb.UpdateMovieImages(created.ID, frontImage, backImage, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating movie images: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + tx = database.DB.MustBeginTx(ctx, nil) + + err = mqb.DestroyMovieImages(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying movie images: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // front image should be nil + storedFront, err := mqb.GetFrontImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting front image: %s", err.Error()) + } + assert.Nil(t, storedFront) + + // back image should be nil + storedBack, err := mqb.GetBackImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting back image: %s", err.Error()) + } + assert.Nil(t, storedBack) +} + // TODO Update // TODO Destroy // TODO Find diff --git a/pkg/models/querybuilder_performer.go b/pkg/models/querybuilder_performer.go index 5b0dca723..a2cdefd52 100644 --- a/pkg/models/querybuilder_performer.go +++ b/pkg/models/querybuilder_performer.go @@ -18,10 +18,10 @@ func NewPerformerQueryBuilder() PerformerQueryBuilder { func (qb *PerformerQueryBuilder) Create(newPerformer Performer, tx *sqlx.Tx) (*Performer, error) { ensureTx(tx) result, err := tx.NamedExec( - `INSERT INTO performers (image, checksum, name, url, gender, twitter, instagram, birthdate, ethnicity, country, + `INSERT INTO performers (checksum, name, url, gender, twitter, instagram, birthdate, ethnicity, country, eye_color, height, measurements, fake_tits, career_length, tattoos, piercings, aliases, favorite, created_at, updated_at) - VALUES (:image, :checksum, :name, :url, :gender, :twitter, :instagram, :birthdate, :ethnicity, :country, + VALUES (:checksum, :name, :url, :gender, :twitter, :instagram, :birthdate, :ethnicity, :country, :eye_color, :height, :measurements, :fake_tits, :career_length, :tattoos, :piercings, :aliases, :favorite, :created_at, :updated_at) `, @@ -178,6 +178,10 @@ func (qb *PerformerQueryBuilder) Query(performerFilter *PerformerFilterType, fin switch *isMissingFilter { case "scenes": query.addWhere("scenes_join.scene_id IS NULL") + case "image": + query.body += `left join performers_image on performers_image.performer_id = performers.id + ` + query.addWhere("performers_image.performer_id IS NULL") default: query.addWhere("performers." + *isMissingFilter + " IS NULL") } @@ -342,3 +346,36 @@ func (qb *PerformerQueryBuilder) queryPerformers(query string, args []interface{ return performers, nil } + +func (qb *PerformerQueryBuilder) UpdatePerformerImage(performerID int, image []byte, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing cover and then create new + if err := qb.DestroyPerformerImage(performerID, tx); err != nil { + return err + } + + _, err := tx.Exec( + `INSERT INTO performers_image (performer_id, image) VALUES (?, ?)`, + performerID, + image, + ) + + return err +} + +func (qb *PerformerQueryBuilder) DestroyPerformerImage(performerID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM performers_image WHERE performer_id = ?", performerID) + if err != nil { + return err + } + return err +} + +func (qb *PerformerQueryBuilder) GetPerformerImage(performerID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT image from performers_image WHERE performer_id = ?` + return getImage(tx, query, performerID) +} diff --git a/pkg/models/querybuilder_performer_test.go b/pkg/models/querybuilder_performer_test.go index 1c2b26979..eb65b55bf 100644 --- a/pkg/models/querybuilder_performer_test.go +++ b/pkg/models/querybuilder_performer_test.go @@ -3,12 +3,17 @@ package models_test import ( + "context" + "database/sql" "strings" "testing" + "time" "github.com/stretchr/testify/assert" + "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) func TestPerformerFindBySceneID(t *testing.T) { @@ -103,6 +108,146 @@ func TestPerformerFindByNames(t *testing.T) { } +func TestPerformerUpdatePerformerImage(t *testing.T) { + qb := models.NewPerformerQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestPerformerUpdatePerformerImage" + performer := models.Performer{ + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), + Favorite: sql.NullBool{Bool: false, Valid: true}, + } + created, err := qb.Create(performer, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating performer: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdatePerformerImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating performer image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // ensure image set + storedImage, err := qb.GetPerformerImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Equal(t, storedImage, image) + + // set nil image + tx = database.DB.MustBeginTx(ctx, nil) + err = qb.UpdatePerformerImage(created.ID, nil, tx) + if err == nil { + t.Fatalf("Expected error setting nil image") + } + + tx.Rollback() +} + +func TestPerformerDestroyPerformerImage(t *testing.T) { + qb := models.NewPerformerQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestPerformerDestroyPerformerImage" + performer := models.Performer{ + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), + Favorite: sql.NullBool{Bool: false, Valid: true}, + } + created, err := qb.Create(performer, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating performer: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdatePerformerImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating performer image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + tx = database.DB.MustBeginTx(ctx, nil) + + err = qb.DestroyPerformerImage(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying performer image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // image should be nil + storedImage, err := qb.GetPerformerImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Nil(t, storedImage) +} + +func TestPerformerQueryAge(t *testing.T) { + const age = 19 + ageCriterion := models.IntCriterionInput{ + Value: age, + Modifier: models.CriterionModifierEquals, + } + + verifyPerformerAge(t, ageCriterion) + + ageCriterion.Modifier = models.CriterionModifierNotEquals + verifyPerformerAge(t, ageCriterion) + + ageCriterion.Modifier = models.CriterionModifierGreaterThan + verifyPerformerAge(t, ageCriterion) + + ageCriterion.Modifier = models.CriterionModifierLessThan + verifyPerformerAge(t, ageCriterion) +} + +func verifyPerformerAge(t *testing.T, ageCriterion models.IntCriterionInput) { + qb := models.NewPerformerQueryBuilder() + performerFilter := models.PerformerFilterType{ + Age: &ageCriterion, + } + + performers, _ := qb.Query(&performerFilter, nil) + + now := time.Now() + for _, performer := range performers { + bd := performer.Birthdate.String + d, _ := time.Parse("2006-01-02", bd) + age := now.Year() - d.Year() + if now.YearDay() < d.YearDay() { + age = age - 1 + } + + verifyInt(t, age, ageCriterion) + } +} + // TODO Update // TODO Destroy // TODO Find diff --git a/pkg/models/querybuilder_scene.go b/pkg/models/querybuilder_scene.go index 2a8d162c4..e2cbca00a 100644 --- a/pkg/models/querybuilder_scene.go +++ b/pkg/models/querybuilder_scene.go @@ -2,7 +2,7 @@ package models import ( "database/sql" - "strconv" + "fmt" "strings" "github.com/jmoiron/sqlx" @@ -41,6 +41,16 @@ WHERE scenes_tags.tag_id = ? GROUP BY scenes_tags.scene_id ` +var countScenesForMissingChecksumQuery = ` +SELECT id FROM scenes +WHERE scenes.checksum is null +` + +var countScenesForMissingOSHashQuery = ` +SELECT id FROM scenes +WHERE scenes.oshash is null +` + type SceneQueryBuilder struct{} func NewSceneQueryBuilder() SceneQueryBuilder { @@ -50,12 +60,10 @@ func NewSceneQueryBuilder() SceneQueryBuilder { func (qb *SceneQueryBuilder) Create(newScene Scene, tx *sqlx.Tx) (*Scene, error) { ensureTx(tx) result, err := tx.NamedExec( - `INSERT INTO scenes (checksum, path, title, details, url, date, rating, o_counter, size, duration, video_codec, - audio_codec, format, width, height, framerate, bitrate, studio_id, cover, - created_at, updated_at) - VALUES (:checksum, :path, :title, :details, :url, :date, :rating, :o_counter, :size, :duration, :video_codec, - :audio_codec, :format, :width, :height, :framerate, :bitrate, :studio_id, :cover, - :created_at, :updated_at) + `INSERT INTO scenes (oshash, checksum, path, title, details, url, date, rating, o_counter, size, duration, video_codec, + audio_codec, format, width, height, framerate, bitrate, studio_id, created_at, updated_at) + VALUES (:oshash, :checksum, :path, :title, :details, :url, :date, :rating, :o_counter, :size, :duration, :video_codec, + :audio_codec, :format, :width, :height, :framerate, :bitrate, :studio_id, :created_at, :updated_at) `, newScene, ) @@ -150,6 +158,24 @@ func (qb *SceneQueryBuilder) Find(id int) (*Scene, error) { return qb.find(id, nil) } +func (qb *SceneQueryBuilder) FindMany(ids []int) ([]*Scene, error) { + var scenes []*Scene + for _, id := range ids { + scene, err := qb.Find(id) + if err != nil { + return nil, err + } + + if scene == nil { + return nil, fmt.Errorf("scene with id %d not found", id) + } + + scenes = append(scenes, scene) + } + + return scenes, nil +} + func (qb *SceneQueryBuilder) find(id int, tx *sqlx.Tx) (*Scene, error) { query := selectAll(sceneTable) + "WHERE id = ? LIMIT 1" args := []interface{}{id} @@ -162,6 +188,12 @@ func (qb *SceneQueryBuilder) FindByChecksum(checksum string) (*Scene, error) { return qb.queryScene(query, args, nil) } +func (qb *SceneQueryBuilder) FindByOSHash(oshash string) (*Scene, error) { + query := "SELECT * FROM scenes WHERE oshash = ? LIMIT 1" + args := []interface{}{oshash} + return qb.queryScene(query, args, nil) +} + func (qb *SceneQueryBuilder) FindByPath(path string) (*Scene, error) { query := selectAll(sceneTable) + "WHERE path = ? LIMIT 1" args := []interface{}{path} @@ -215,6 +247,16 @@ func (qb *SceneQueryBuilder) CountByTagID(tagID int) (int, error) { return runCountQuery(buildCountQuery(countScenesForTagQuery), args) } +// CountMissingChecksum returns the number of scenes missing a checksum value. +func (qb *SceneQueryBuilder) CountMissingChecksum() (int, error) { + return runCountQuery(buildCountQuery(countScenesForMissingChecksumQuery), []interface{}{}) +} + +// CountMissingOSHash returns the number of scenes missing an oshash value. +func (qb *SceneQueryBuilder) CountMissingOSHash() (int, error) { + return runCountQuery(buildCountQuery(countScenesForMissingOSHashQuery), []interface{}{}) +} + func (qb *SceneQueryBuilder) Wall(q *string) ([]*Scene, error) { s := "" if q != nil { @@ -251,7 +293,7 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin ` if q := findFilter.Q; q != nil && *q != "" { - searchColumns := []string{"scenes.title", "scenes.details", "scenes.path", "scenes.checksum", "scene_markers.title"} + searchColumns := []string{"scenes.title", "scenes.details", "scenes.path", "scenes.oshash", "scenes.checksum", "scene_markers.title"} clause, thisArgs := getSearchBinding(searchColumns, *q, false) query.addWhere(clause) query.addArg(thisArgs...) @@ -329,7 +371,7 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin } query.body += " LEFT JOIN tags on tags_join.tag_id = tags.id" - whereClause, havingClause := getMultiCriterionClause("tags", "scenes_tags", "tag_id", tagsFilter) + whereClause, havingClause := getMultiCriterionClause("scenes", "tags", "scenes_tags", "scene_id", "tag_id", tagsFilter) query.addWhere(whereClause) query.addHaving(havingClause) } @@ -340,7 +382,7 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin } query.body += " LEFT JOIN performers ON performers_join.performer_id = performers.id" - whereClause, havingClause := getMultiCriterionClause("performers", "performers_scenes", "performer_id", performersFilter) + whereClause, havingClause := getMultiCriterionClause("scenes", "performers", "performers_scenes", "scene_id", "performer_id", performersFilter) query.addWhere(whereClause) query.addHaving(havingClause) } @@ -350,7 +392,7 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin query.addArg(studioID) } - whereClause, havingClause := getMultiCriterionClause("studio", "", "studio_id", studiosFilter) + whereClause, havingClause := getMultiCriterionClause("scenes", "studio", "", "", "studio_id", studiosFilter) query.addWhere(whereClause) query.addHaving(havingClause) } @@ -361,7 +403,7 @@ func (qb *SceneQueryBuilder) Query(sceneFilter *SceneFilterType, findFilter *Fin } query.body += " LEFT JOIN movies ON movies_join.movie_id = movies.id" - whereClause, havingClause := getMultiCriterionClause("movies", "movies_scenes", "movie_id", moviesFilter) + whereClause, havingClause := getMultiCriterionClause("scenes", "movies", "movies_scenes", "scene_id", "movie_id", moviesFilter) query.addWhere(whereClause) query.addHaving(havingClause) } @@ -414,29 +456,6 @@ func getDurationWhereClause(durationFilter IntCriterionInput) (string, []interfa return clause, args } -// returns where clause and having clause -func getMultiCriterionClause(table string, joinTable string, joinTableField string, criterion *MultiCriterionInput) (string, string) { - whereClause := "" - havingClause := "" - if criterion.Modifier == CriterionModifierIncludes { - // includes any of the provided ids - whereClause = table + ".id IN " + getInBinding(len(criterion.Value)) - } else if criterion.Modifier == CriterionModifierIncludesAll { - // includes all of the provided ids - whereClause = table + ".id IN " + getInBinding(len(criterion.Value)) - havingClause = "count(distinct " + table + ".id) IS " + strconv.Itoa(len(criterion.Value)) - } else if criterion.Modifier == CriterionModifierExcludes { - // excludes all of the provided ids - if joinTable != "" { - whereClause = "not exists (select " + joinTable + ".scene_id from " + joinTable + " where " + joinTable + ".scene_id = scenes.id and " + joinTable + "." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")" - } else { - whereClause = "not exists (select s.id from scenes as s where s.id = scenes.id and s." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")" - } - } - - return whereClause, havingClause -} - func (qb *SceneQueryBuilder) QueryAllByPathRegex(regex string) ([]*Scene, error) { var args []interface{} body := selectDistinctIDs("scenes") + " WHERE scenes.path regexp ?" @@ -549,3 +568,62 @@ func (qb *SceneQueryBuilder) UpdateFormat(id int, format string, tx *sqlx.Tx) er return nil } + +func (qb *SceneQueryBuilder) UpdateOSHash(id int, oshash string, tx *sqlx.Tx) error { + ensureTx(tx) + _, err := tx.Exec( + `UPDATE scenes SET oshash = ? WHERE scenes.id = ? `, + oshash, id, + ) + if err != nil { + return err + } + + return nil +} + +func (qb *SceneQueryBuilder) UpdateChecksum(id int, checksum string, tx *sqlx.Tx) error { + ensureTx(tx) + _, err := tx.Exec( + `UPDATE scenes SET checksum = ? WHERE scenes.id = ? `, + checksum, id, + ) + if err != nil { + return err + } + + return nil +} + +func (qb *SceneQueryBuilder) UpdateSceneCover(sceneID int, cover []byte, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing cover and then create new + if err := qb.DestroySceneCover(sceneID, tx); err != nil { + return err + } + + _, err := tx.Exec( + `INSERT INTO scenes_cover (scene_id, cover) VALUES (?, ?)`, + sceneID, + cover, + ) + + return err +} + +func (qb *SceneQueryBuilder) DestroySceneCover(sceneID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM scenes_cover WHERE scene_id = ?", sceneID) + if err != nil { + return err + } + return err +} + +func (qb *SceneQueryBuilder) GetSceneCover(sceneID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT cover from scenes_cover WHERE scene_id = ?` + return getImage(tx, query, sceneID) +} diff --git a/pkg/models/querybuilder_scene_marker.go b/pkg/models/querybuilder_scene_marker.go index 8cd9f0d69..5edba444c 100644 --- a/pkg/models/querybuilder_scene_marker.go +++ b/pkg/models/querybuilder_scene_marker.go @@ -2,9 +2,11 @@ package models import ( "database/sql" + "fmt" + "strconv" + "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/database" - "strconv" ) const countSceneMarkersForTagQuery = ` @@ -72,6 +74,24 @@ func (qb *SceneMarkerQueryBuilder) Find(id int) (*SceneMarker, error) { return results[0], nil } +func (qb *SceneMarkerQueryBuilder) FindMany(ids []int) ([]*SceneMarker, error) { + var markers []*SceneMarker + for _, id := range ids { + marker, err := qb.Find(id) + if err != nil { + return nil, err + } + + if marker == nil { + return nil, fmt.Errorf("scene marker with id %d not found", id) + } + + markers = append(markers, marker) + } + + return markers, nil +} + func (qb *SceneMarkerQueryBuilder) FindBySceneID(sceneID int, tx *sqlx.Tx) ([]*SceneMarker, error) { query := ` SELECT scene_markers.* FROM scene_markers diff --git a/pkg/models/querybuilder_scene_test.go b/pkg/models/querybuilder_scene_test.go index df6b0e817..c86e7c3e6 100644 --- a/pkg/models/querybuilder_scene_test.go +++ b/pkg/models/querybuilder_scene_test.go @@ -3,13 +3,16 @@ package models_test import ( + "context" "database/sql" "strconv" "testing" "github.com/stretchr/testify/assert" + "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/utils" ) func TestSceneFind(t *testing.T) { @@ -171,6 +174,7 @@ func verifyScenesRating(t *testing.T, ratingCriterion models.IntCriterionInput) } func verifyInt64(t *testing.T, value sql.NullInt64, criterion models.IntCriterionInput) { + t.Helper() assert := assert.New(t) if criterion.Modifier == models.CriterionModifierIsNull { assert.False(value.Valid, "expect is null values to be null") @@ -225,6 +229,7 @@ func verifyScenesOCounter(t *testing.T, oCounterCriterion models.IntCriterionInp } func verifyInt(t *testing.T, value int, criterion models.IntCriterionInput) { + t.Helper() assert := assert.New(t) if criterion.Modifier == models.CriterionModifierEquals { assert.Equal(criterion.Value, value) @@ -676,6 +681,42 @@ func TestSceneQueryStudio(t *testing.T) { assert.Len(t, scenes, 0) } +func TestSceneQueryMovies(t *testing.T) { + sqb := models.NewSceneQueryBuilder() + movieCriterion := models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(movieIDs[movieIdxWithScene]), + }, + Modifier: models.CriterionModifierIncludes, + } + + sceneFilter := models.SceneFilterType{ + Movies: &movieCriterion, + } + + scenes, _ := sqb.Query(&sceneFilter, nil) + + assert.Len(t, scenes, 1) + + // ensure id is correct + assert.Equal(t, sceneIDs[sceneIdxWithMovie], scenes[0].ID) + + movieCriterion = models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(movieIDs[movieIdxWithScene]), + }, + Modifier: models.CriterionModifierExcludes, + } + + q := getSceneStringValue(sceneIdxWithMovie, titleField) + findFilter := models.FindFilterType{ + Q: &q, + } + + scenes, _ = sqb.Query(&sceneFilter, &findFilter) + assert.Len(t, scenes, 0) +} + func TestSceneQuerySorting(t *testing.T) { sort := titleField direction := models.SortDirectionEnumAsc @@ -858,6 +899,104 @@ func TestFindByStudioID(t *testing.T) { assert.Len(t, scenes, 0) } +func TestSceneUpdateSceneCover(t *testing.T) { + qb := models.NewSceneQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestSceneUpdateSceneCover" + scene := models.Scene{ + Path: name, + Checksum: sql.NullString{String: utils.MD5FromString(name), Valid: true}, + } + created, err := qb.Create(scene, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating scene: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateSceneCover(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating scene cover: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // ensure image set + storedImage, err := qb.GetSceneCover(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Equal(t, storedImage, image) + + // set nil image + tx = database.DB.MustBeginTx(ctx, nil) + err = qb.UpdateSceneCover(created.ID, nil, tx) + if err == nil { + t.Fatalf("Expected error setting nil image") + } + + tx.Rollback() +} + +func TestSceneDestroySceneCover(t *testing.T) { + qb := models.NewSceneQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestSceneDestroySceneCover" + scene := models.Scene{ + Path: name, + Checksum: sql.NullString{String: utils.MD5FromString(name), Valid: true}, + } + created, err := qb.Create(scene, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating scene: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateSceneCover(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating scene image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + tx = database.DB.MustBeginTx(ctx, nil) + + err = qb.DestroySceneCover(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying scene cover: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // image should be nil + storedImage, err := qb.GetSceneCover(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Nil(t, storedImage) +} + // TODO Update // TODO IncrementOCounter // TODO DecrementOCounter diff --git a/pkg/models/querybuilder_sql.go b/pkg/models/querybuilder_sql.go index 2944462c2..300884491 100644 --- a/pkg/models/querybuilder_sql.go +++ b/pkg/models/querybuilder_sql.go @@ -31,7 +31,7 @@ func (qb queryBuilder) executeFind() ([]int, int) { func (qb *queryBuilder) addWhere(clauses ...string) { for _, clause := range clauses { if len(clause) > 0 { - qb.whereClauses = append(qb.whereClauses, clauses...) + qb.whereClauses = append(qb.whereClauses, clause) } } } @@ -104,7 +104,7 @@ func getSort(sort string, direction string, tableName string) string { const randomSeedPrefix = "random_" if strings.HasSuffix(sort, "_count") { - var relationTableName = strings.Split(sort, "_")[0] // TODO: pluralize? + var relationTableName = strings.TrimSuffix(sort, "_count") // TODO: pluralize? colName := getColumn(relationTableName, "id") return " ORDER BY COUNT(distinct " + colName + ") " + direction } else if strings.Compare(sort, "filesize") == 0 { @@ -239,6 +239,29 @@ func getIntCriterionWhereClause(column string, input IntCriterionInput) (string, return column + " " + binding, count } +// returns where clause and having clause +func getMultiCriterionClause(primaryTable, foreignTable, joinTable, primaryFK, foreignFK string, criterion *MultiCriterionInput) (string, string) { + whereClause := "" + havingClause := "" + if criterion.Modifier == CriterionModifierIncludes { + // includes any of the provided ids + whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) + } else if criterion.Modifier == CriterionModifierIncludesAll { + // includes all of the provided ids + whereClause = foreignTable + ".id IN " + getInBinding(len(criterion.Value)) + havingClause = "count(distinct " + foreignTable + ".id) IS " + strconv.Itoa(len(criterion.Value)) + } else if criterion.Modifier == CriterionModifierExcludes { + // excludes all of the provided ids + if joinTable != "" { + whereClause = "not exists (select " + joinTable + "." + primaryFK + " from " + joinTable + " where " + joinTable + "." + primaryFK + " = " + primaryTable + ".id and " + joinTable + "." + foreignFK + " in " + getInBinding(len(criterion.Value)) + ")" + } else { + whereClause = "not exists (select s.id from " + primaryTable + " as s where s.id = " + primaryTable + ".id and s." + foreignFK + " in " + getInBinding(len(criterion.Value)) + ")" + } + } + + return whereClause, havingClause +} + func runIdsQuery(query string, args []interface{}) ([]int, error) { var result []struct { Int int `db:"id"` @@ -288,9 +311,12 @@ func executeFindQuery(tableName string, body string, args []interface{}, sortAnd } countQuery := buildCountQuery(body) - countResult, countErr := runCountQuery(countQuery, args) - idsQuery := body + sortAndPagination + + // Perform query and fetch result + logger.Tracef("SQL: %s, args: %v", idsQuery, args) + + countResult, countErr := runCountQuery(countQuery, args) idsResult, idsErr := runIdsQuery(idsQuery, args) if countErr != nil { @@ -395,3 +421,31 @@ func sqlGenKeys(i interface{}, partial bool) string { } return strings.Join(query, ", ") } + +func getImage(tx *sqlx.Tx, query string, args ...interface{}) ([]byte, error) { + var rows *sqlx.Rows + var err error + if tx != nil { + rows, err = tx.Queryx(query, args...) + } else { + rows, err = database.DB.Queryx(query, args...) + } + + if err != nil && err != sql.ErrNoRows { + return nil, err + } + defer rows.Close() + + var ret []byte + if rows.Next() { + if err := rows.Scan(&ret); err != nil { + return nil, err + } + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return ret, nil +} diff --git a/pkg/models/querybuilder_studio.go b/pkg/models/querybuilder_studio.go index 2b65bba2a..67a931df8 100644 --- a/pkg/models/querybuilder_studio.go +++ b/pkg/models/querybuilder_studio.go @@ -16,8 +16,8 @@ func NewStudioQueryBuilder() StudioQueryBuilder { func (qb *StudioQueryBuilder) Create(newStudio Studio, tx *sqlx.Tx) (*Studio, error) { ensureTx(tx) result, err := tx.NamedExec( - `INSERT INTO studios (image, checksum, name, url, created_at, updated_at) - VALUES (:image, :checksum, :name, :url, :created_at, :updated_at) + `INSERT INTO studios (checksum, name, url, parent_id, created_at, updated_at) + VALUES (:checksum, :name, :url, :parent_id, :created_at, :updated_at) `, newStudio, ) @@ -35,20 +35,21 @@ func (qb *StudioQueryBuilder) Create(newStudio Studio, tx *sqlx.Tx) (*Studio, er return &newStudio, nil } -func (qb *StudioQueryBuilder) Update(updatedStudio Studio, tx *sqlx.Tx) (*Studio, error) { +func (qb *StudioQueryBuilder) Update(updatedStudio StudioPartial, tx *sqlx.Tx) (*Studio, error) { ensureTx(tx) _, err := tx.NamedExec( - `UPDATE studios SET `+SQLGenKeys(updatedStudio)+` WHERE studios.id = :id`, + `UPDATE studios SET `+SQLGenKeysPartial(updatedStudio)+` WHERE studios.id = :id`, updatedStudio, ) if err != nil { return nil, err } - if err := tx.Get(&updatedStudio, `SELECT * FROM studios WHERE id = ? LIMIT 1`, updatedStudio.ID); err != nil { + var ret Studio + if err := tx.Get(&ret, `SELECT * FROM studios WHERE id = ? LIMIT 1`, updatedStudio.ID); err != nil { return nil, err } - return &updatedStudio, nil + return &ret, nil } func (qb *StudioQueryBuilder) Destroy(id string, tx *sqlx.Tx) error { @@ -73,6 +74,12 @@ func (qb *StudioQueryBuilder) Find(id int, tx *sqlx.Tx) (*Studio, error) { return qb.queryStudio(query, args, tx) } +func (qb *StudioQueryBuilder) FindChildren(id int, tx *sqlx.Tx) ([]*Studio, error) { + query := "SELECT studios.* FROM studios WHERE studios.parent_id = ?" + args := []interface{}{id} + return qb.queryStudios(query, args, tx) +} + func (qb *StudioQueryBuilder) FindBySceneID(sceneID int) (*Studio, error) { query := "SELECT studios.* FROM studios JOIN scenes ON studios.id = scenes.studio_id WHERE scenes.id = ? LIMIT 1" args := []interface{}{sceneID} @@ -101,7 +108,10 @@ func (qb *StudioQueryBuilder) AllSlim() ([]*Studio, error) { return qb.queryStudios("SELECT studios.id, studios.name FROM studios "+qb.getStudioSort(nil), nil, nil) } -func (qb *StudioQueryBuilder) Query(findFilter *FindFilterType) ([]*Studio, int) { +func (qb *StudioQueryBuilder) Query(studioFilter *StudioFilterType, findFilter *FindFilterType) ([]*Studio, int) { + if studioFilter == nil { + studioFilter = &StudioFilterType{} + } if findFilter == nil { findFilter = &FindFilterType{} } @@ -116,11 +126,37 @@ func (qb *StudioQueryBuilder) Query(findFilter *FindFilterType) ([]*Studio, int) if q := findFilter.Q; q != nil && *q != "" { searchColumns := []string{"studios.name"} + clause, thisArgs := getSearchBinding(searchColumns, *q, false) whereClauses = append(whereClauses, clause) args = append(args, thisArgs...) } + if parentsFilter := studioFilter.Parents; parentsFilter != nil && len(parentsFilter.Value) > 0 { + body += ` + left join studios as parent_studio on parent_studio.id = studios.parent_id + ` + + for _, studioID := range parentsFilter.Value { + args = append(args, studioID) + } + + whereClause, havingClause := getMultiCriterionClause("studios", "parent_studio", "", "", "parent_id", parentsFilter) + whereClauses = appendClause(whereClauses, whereClause) + havingClauses = appendClause(havingClauses, havingClause) + } + + if isMissingFilter := studioFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { + switch *isMissingFilter { + case "image": + body += `left join studios_image on studios_image.studio_id = studios.id + ` + whereClauses = appendClause(whereClauses, "studios_image.studio_id IS NULL") + default: + whereClauses = appendClause(whereClauses, "studios."+*isMissingFilter+" IS NULL") + } + } + sortAndPagination := qb.getStudioSort(findFilter) + getPagination(findFilter) idsResult, countResult := executeFindQuery("studios", body, args, sortAndPagination, whereClauses, havingClauses) @@ -183,3 +219,36 @@ func (qb *StudioQueryBuilder) queryStudios(query string, args []interface{}, tx return studios, nil } + +func (qb *StudioQueryBuilder) UpdateStudioImage(studioID int, image []byte, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing cover and then create new + if err := qb.DestroyStudioImage(studioID, tx); err != nil { + return err + } + + _, err := tx.Exec( + `INSERT INTO studios_image (studio_id, image) VALUES (?, ?)`, + studioID, + image, + ) + + return err +} + +func (qb *StudioQueryBuilder) DestroyStudioImage(studioID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM studios_image WHERE studio_id = ?", studioID) + if err != nil { + return err + } + return err +} + +func (qb *StudioQueryBuilder) GetStudioImage(studioID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT image from studios_image WHERE studio_id = ?` + return getImage(tx, query, studioID) +} diff --git a/pkg/models/querybuilder_studio_test.go b/pkg/models/querybuilder_studio_test.go index 5f7460844..ed192046a 100644 --- a/pkg/models/querybuilder_studio_test.go +++ b/pkg/models/querybuilder_studio_test.go @@ -3,9 +3,13 @@ package models_test import ( + "context" + "database/sql" + "strconv" "strings" "testing" + "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" "github.com/stretchr/testify/assert" ) @@ -39,6 +43,263 @@ func TestStudioFindByName(t *testing.T) { } +func TestStudioQueryParent(t *testing.T) { + sqb := models.NewStudioQueryBuilder() + studioCriterion := models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(studioIDs[studioIdxWithChildStudio]), + }, + Modifier: models.CriterionModifierIncludes, + } + + studioFilter := models.StudioFilterType{ + Parents: &studioCriterion, + } + + studios, _ := sqb.Query(&studioFilter, nil) + + assert.Len(t, studios, 1) + + // ensure id is correct + assert.Equal(t, sceneIDs[studioIdxWithParentStudio], studios[0].ID) + + studioCriterion = models.MultiCriterionInput{ + Value: []string{ + strconv.Itoa(studioIDs[studioIdxWithChildStudio]), + }, + Modifier: models.CriterionModifierExcludes, + } + + q := getStudioStringValue(studioIdxWithParentStudio, titleField) + findFilter := models.FindFilterType{ + Q: &q, + } + + studios, _ = sqb.Query(&studioFilter, &findFilter) + assert.Len(t, studios, 0) +} + +func TestStudioDestroyParent(t *testing.T) { + const parentName = "parent" + const childName = "child" + + // create parent and child studios + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + createdParent, err := createStudio(tx, parentName, nil) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating parent studio: %s", err.Error()) + } + + parentID := int64(createdParent.ID) + createdChild, err := createStudio(tx, childName, &parentID) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating child studio: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + sqb := models.NewStudioQueryBuilder() + + // destroy the parent + tx = database.DB.MustBeginTx(ctx, nil) + + err = sqb.Destroy(strconv.Itoa(createdParent.ID), tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying parent studio: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // destroy the child + tx = database.DB.MustBeginTx(ctx, nil) + + err = sqb.Destroy(strconv.Itoa(createdChild.ID), tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying child studio: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } +} + +func TestStudioFindChildren(t *testing.T) { + sqb := models.NewStudioQueryBuilder() + + studios, err := sqb.FindChildren(studioIDs[studioIdxWithChildStudio], nil) + + if err != nil { + t.Fatalf("error calling FindChildren: %s", err.Error()) + } + + assert.Len(t, studios, 1) + assert.Equal(t, studioIDs[studioIdxWithParentStudio], studios[0].ID) + + studios, err = sqb.FindChildren(0, nil) + + if err != nil { + t.Fatalf("error calling FindChildren: %s", err.Error()) + } + + assert.Len(t, studios, 0) +} + +func TestStudioUpdateClearParent(t *testing.T) { + const parentName = "clearParent_parent" + const childName = "clearParent_child" + + // create parent and child studios + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + createdParent, err := createStudio(tx, parentName, nil) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating parent studio: %s", err.Error()) + } + + parentID := int64(createdParent.ID) + createdChild, err := createStudio(tx, childName, &parentID) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating child studio: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + sqb := models.NewStudioQueryBuilder() + + // clear the parent id from the child + tx = database.DB.MustBeginTx(ctx, nil) + + updatePartial := models.StudioPartial{ + ID: createdChild.ID, + ParentID: &sql.NullInt64{Valid: false}, + } + + updatedStudio, err := sqb.Update(updatePartial, tx) + + if err != nil { + tx.Rollback() + t.Fatalf("Error updated studio: %s", err.Error()) + } + + if updatedStudio.ParentID.Valid { + t.Error("updated studio has parent ID set") + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } +} + +func TestStudioUpdateStudioImage(t *testing.T) { + qb := models.NewStudioQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestStudioUpdateStudioImage" + created, err := createStudio(tx, name, nil) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating studio: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateStudioImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // ensure image set + storedImage, err := qb.GetStudioImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Equal(t, storedImage, image) + + // set nil image + tx = database.DB.MustBeginTx(ctx, nil) + err = qb.UpdateStudioImage(created.ID, nil, tx) + if err == nil { + t.Fatalf("Expected error setting nil image") + } + + tx.Rollback() +} + +func TestStudioDestroyStudioImage(t *testing.T) { + qb := models.NewStudioQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestStudioDestroyStudioImage" + created, err := createStudio(tx, name, nil) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating studio: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateStudioImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + tx = database.DB.MustBeginTx(ctx, nil) + + err = qb.DestroyStudioImage(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // image should be nil + storedImage, err := qb.GetStudioImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Nil(t, storedImage) +} + // TODO Create // TODO Update // TODO Destroy diff --git a/pkg/models/querybuilder_tag.go b/pkg/models/querybuilder_tag.go index 35c64c323..b7a840531 100644 --- a/pkg/models/querybuilder_tag.go +++ b/pkg/models/querybuilder_tag.go @@ -8,6 +8,8 @@ import ( "github.com/stashapp/stash/pkg/database" ) +const tagTable = "tags" + type TagQueryBuilder struct{} func NewTagQueryBuilder() TagQueryBuilder { @@ -146,25 +148,72 @@ func (qb *TagQueryBuilder) AllSlim() ([]*Tag, error) { return qb.queryTags("SELECT tags.id, tags.name FROM tags "+qb.getTagSort(nil), nil, nil) } -func (qb *TagQueryBuilder) Query(findFilter *FindFilterType) ([]*Tag, int) { +func (qb *TagQueryBuilder) Query(tagFilter *TagFilterType, findFilter *FindFilterType) ([]*Tag, int) { + if tagFilter == nil { + tagFilter = &TagFilterType{} + } if findFilter == nil { findFilter = &FindFilterType{} } - var whereClauses []string - var havingClauses []string - var args []interface{} - body := selectDistinctIDs("tags") + query := queryBuilder{ + tableName: tagTable, + } + + query.body = selectDistinctIDs(tagTable) + + /* + query.body += ` + left join tags_image on tags_image.tag_id = tags.id + left join scenes_tags on scenes_tags.tag_id = tags.id + left join scene_markers_tags on scene_markers_tags.tag_id = tags.id + left join scene_markers on scene_markers.primary_tag_id = tags.id OR scene_markers.id = scene_markers_tags.scene_marker_id + left join scenes on scenes_tags.scene_id = scenes.id` + */ + + // the presence of joining on scene_markers.primary_tag_id and scene_markers_tags.tag_id + // appears to confuse sqlite and causes serious performance issues. + // Disabling querying/sorting on marker count for now. + + query.body += ` + left join tags_image on tags_image.tag_id = tags.id + left join scenes_tags on scenes_tags.tag_id = tags.id + left join scenes on scenes_tags.scene_id = scenes.id` if q := findFilter.Q; q != nil && *q != "" { searchColumns := []string{"tags.name"} clause, thisArgs := getSearchBinding(searchColumns, *q, false) - whereClauses = append(whereClauses, clause) - args = append(args, thisArgs...) + query.addWhere(clause) + query.addArg(thisArgs...) } - sortAndPagination := qb.getTagSort(findFilter) + getPagination(findFilter) - idsResult, countResult := executeFindQuery("tags", body, args, sortAndPagination, whereClauses, havingClauses) + if isMissingFilter := tagFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { + switch *isMissingFilter { + case "image": + query.addWhere("tags_image.tag_id IS NULL") + default: + query.addWhere("tags." + *isMissingFilter + " IS NULL") + } + } + + if sceneCount := tagFilter.SceneCount; sceneCount != nil { + clause, count := getIntCriterionWhereClause("count(distinct scenes_tags.scene_id)", *sceneCount) + query.addHaving(clause) + if count == 1 { + query.addArg(sceneCount.Value) + } + } + + // if markerCount := tagFilter.MarkerCount; markerCount != nil { + // clause, count := getIntCriterionWhereClause("count(distinct scene_markers.id)", *markerCount) + // query.addHaving(clause) + // if count == 1 { + // query.addArg(markerCount.Value) + // } + // } + + query.sortAndPagination = qb.getTagSort(findFilter) + getPagination(findFilter) + idsResult, countResult := query.executeFind() var tags []*Tag for _, id := range idsResult { @@ -225,3 +274,36 @@ func (qb *TagQueryBuilder) queryTags(query string, args []interface{}, tx *sqlx. return tags, nil } + +func (qb *TagQueryBuilder) UpdateTagImage(tagID int, image []byte, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing cover and then create new + if err := qb.DestroyTagImage(tagID, tx); err != nil { + return err + } + + _, err := tx.Exec( + `INSERT INTO tags_image (tag_id, image) VALUES (?, ?)`, + tagID, + image, + ) + + return err +} + +func (qb *TagQueryBuilder) DestroyTagImage(tagID int, tx *sqlx.Tx) error { + ensureTx(tx) + + // Delete the existing joins + _, err := tx.Exec("DELETE FROM tags_image WHERE tag_id = ?", tagID) + if err != nil { + return err + } + return err +} + +func (qb *TagQueryBuilder) GetTagImage(tagID int, tx *sqlx.Tx) ([]byte, error) { + query := `SELECT image from tags_image WHERE tag_id = ?` + return getImage(tx, query, tagID) +} diff --git a/pkg/models/querybuilder_tag_test.go b/pkg/models/querybuilder_tag_test.go index 052c89fc3..83357600f 100644 --- a/pkg/models/querybuilder_tag_test.go +++ b/pkg/models/querybuilder_tag_test.go @@ -3,9 +3,12 @@ package models_test import ( + "context" + "database/sql" "strings" "testing" + "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" "github.com/stretchr/testify/assert" ) @@ -106,6 +109,199 @@ func TestTagFindByNames(t *testing.T) { } +func TestTagQueryIsMissingImage(t *testing.T) { + qb := models.NewTagQueryBuilder() + isMissing := "image" + tagFilter := models.TagFilterType{ + IsMissing: &isMissing, + } + + q := getTagStringValue(tagIdxWithImage, "name") + findFilter := models.FindFilterType{ + Q: &q, + } + + tags, _ := qb.Query(&tagFilter, &findFilter) + + assert.Len(t, tags, 0) + + findFilter.Q = nil + tags, _ = qb.Query(&tagFilter, &findFilter) + + // ensure non of the ids equal the one with image + for _, tag := range tags { + assert.NotEqual(t, tagIDs[tagIdxWithImage], tag.ID) + } +} + +func TestTagQuerySceneCount(t *testing.T) { + countCriterion := models.IntCriterionInput{ + Value: 1, + Modifier: models.CriterionModifierEquals, + } + + verifyTagSceneCount(t, countCriterion) + + countCriterion.Modifier = models.CriterionModifierNotEquals + verifyTagSceneCount(t, countCriterion) + + countCriterion.Modifier = models.CriterionModifierLessThan + verifyTagSceneCount(t, countCriterion) + + countCriterion.Value = 0 + countCriterion.Modifier = models.CriterionModifierGreaterThan + verifyTagSceneCount(t, countCriterion) +} + +func verifyTagSceneCount(t *testing.T, sceneCountCriterion models.IntCriterionInput) { + qb := models.NewTagQueryBuilder() + tagFilter := models.TagFilterType{ + SceneCount: &sceneCountCriterion, + } + + tags, _ := qb.Query(&tagFilter, nil) + + for _, tag := range tags { + verifyInt64(t, sql.NullInt64{ + Int64: int64(getTagSceneCount(tag.ID)), + Valid: true, + }, sceneCountCriterion) + } +} + +// disabled due to performance issues + +// func TestTagQueryMarkerCount(t *testing.T) { +// countCriterion := models.IntCriterionInput{ +// Value: 1, +// Modifier: models.CriterionModifierEquals, +// } + +// verifyTagMarkerCount(t, countCriterion) + +// countCriterion.Modifier = models.CriterionModifierNotEquals +// verifyTagMarkerCount(t, countCriterion) + +// countCriterion.Modifier = models.CriterionModifierLessThan +// verifyTagMarkerCount(t, countCriterion) + +// countCriterion.Value = 0 +// countCriterion.Modifier = models.CriterionModifierGreaterThan +// verifyTagMarkerCount(t, countCriterion) +// } + +func verifyTagMarkerCount(t *testing.T, markerCountCriterion models.IntCriterionInput) { + qb := models.NewTagQueryBuilder() + tagFilter := models.TagFilterType{ + MarkerCount: &markerCountCriterion, + } + + tags, _ := qb.Query(&tagFilter, nil) + + for _, tag := range tags { + verifyInt64(t, sql.NullInt64{ + Int64: int64(getTagMarkerCount(tag.ID)), + Valid: true, + }, markerCountCriterion) + } +} + +func TestTagUpdateTagImage(t *testing.T) { + qb := models.NewTagQueryBuilder() + + // create tag to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestTagUpdateTagImage" + tag := models.Tag{ + Name: name, + } + created, err := qb.Create(tag, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating tag: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateTagImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // ensure image set + storedImage, err := qb.GetTagImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Equal(t, storedImage, image) + + // set nil image + tx = database.DB.MustBeginTx(ctx, nil) + err = qb.UpdateTagImage(created.ID, nil, tx) + if err == nil { + t.Fatalf("Expected error setting nil image") + } + + tx.Rollback() +} + +func TestTagDestroyTagImage(t *testing.T) { + qb := models.NewTagQueryBuilder() + + // create performer to test against + ctx := context.TODO() + tx := database.DB.MustBeginTx(ctx, nil) + + const name = "TestTagDestroyTagImage" + tag := models.Tag{ + Name: name, + } + created, err := qb.Create(tag, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error creating tag: %s", err.Error()) + } + + image := []byte("image") + err = qb.UpdateTagImage(created.ID, image, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error updating studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + tx = database.DB.MustBeginTx(ctx, nil) + + err = qb.DestroyTagImage(created.ID, tx) + if err != nil { + tx.Rollback() + t.Fatalf("Error destroying studio image: %s", err.Error()) + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + t.Fatalf("Error committing: %s", err.Error()) + } + + // image should be nil + storedImage, err := qb.GetTagImage(created.ID, nil) + if err != nil { + t.Fatalf("Error getting image: %s", err.Error()) + } + assert.Nil(t, storedImage) +} + // TODO Create // TODO Update // TODO Destroy diff --git a/pkg/models/setup_test.go b/pkg/models/setup_test.go index 0cfb25328..36c53905d 100644 --- a/pkg/models/setup_test.go +++ b/pkg/models/setup_test.go @@ -11,6 +11,7 @@ import ( "os" "strconv" "testing" + "time" "github.com/jmoiron/sqlx" @@ -22,12 +23,12 @@ import ( const totalScenes = 12 const performersNameCase = 3 const performersNameNoCase = 2 -const moviesNameCase = 1 +const moviesNameCase = 2 const moviesNameNoCase = 1 -const totalGalleries = 1 +const totalGalleries = 2 const tagsNameNoCase = 2 -const tagsNameCase = 5 -const studiosNameCase = 1 +const tagsNameCase = 6 +const studiosNameCase = 4 const studiosNameNoCase = 1 var sceneIDs []int @@ -61,9 +62,10 @@ const performerIdx1WithDupName = 3 const performerIdxWithDupName = 4 const movieIdxWithScene = 0 +const movieIdxWithStudio = 1 // movies with dup names start from the end -const movieIdxWithDupName = 1 +const movieIdxWithDupName = 2 const galleryIdxWithScene = 0 @@ -72,15 +74,19 @@ const tagIdx1WithScene = 1 const tagIdx2WithScene = 2 const tagIdxWithPrimaryMarker = 3 const tagIdxWithMarker = 4 +const tagIdxWithImage = 5 // tags with dup names start from the end -const tagIdx1WithDupName = 5 -const tagIdxWithDupName = 6 +const tagIdx1WithDupName = 6 +const tagIdxWithDupName = 7 const studioIdxWithScene = 0 +const studioIdxWithMovie = 1 +const studioIdxWithChildStudio = 2 +const studioIdxWithParentStudio = 3 // studios with dup names start from the end -const studioIdxWithDupName = 1 +const studioIdxWithDupName = 4 const markerIdxWithScene = 0 @@ -158,6 +164,11 @@ func populateDB() error { return err } + if err := addTagImage(tx, tagIdxWithImage); err != nil { + tx.Rollback() + return err + } + if err := createStudios(tx, studiosNameCase, studiosNameNoCase); err != nil { tx.Rollback() return err @@ -196,6 +207,16 @@ func populateDB() error { return err } + if err := linkMovieStudio(tx, movieIdxWithStudio, studioIdxWithMovie); err != nil { + tx.Rollback() + return err + } + + if err := linkStudioParent(tx, studioIdxWithChildStudio, studioIdxWithParentStudio); err != nil { + tx.Rollback() + return err + } + if err := createMarker(tx, sceneIdxWithMarker, tagIdxWithPrimaryMarker, []int{tagIdxWithMarker}); err != nil { tx.Rollback() return err @@ -256,7 +277,7 @@ func createScenes(tx *sqlx.Tx, n int) error { scene := models.Scene{ Path: getSceneStringValue(i, pathField), Title: sql.NullString{String: getSceneStringValue(i, titleField), Valid: true}, - Checksum: getSceneStringValue(i, checksumField), + Checksum: sql.NullString{String: getSceneStringValue(i, checksumField), Valid: true}, Details: sql.NullString{String: getSceneStringValue(i, "Details"), Valid: true}, Rating: getSceneRating(i), OCounter: getSceneOCounter(i), @@ -312,10 +333,9 @@ func createMovies(tx *sqlx.Tx, n int, o int) error { const namePlain = "Name" const nameNoCase = "NaMe" - name := namePlain - for i := 0; i < n+o; i++ { index := i + name := namePlain if i >= n { // i=n movies get dup names if case is not checked @@ -323,16 +343,16 @@ func createMovies(tx *sqlx.Tx, n int, o int) error { } // so count backwards to 0 as needed // movies [ i ] and [ n + o - i - 1 ] should have similar names with only the Name!=NaMe part different + name = getMovieStringValue(index, name) movie := models.Movie{ - Name: sql.NullString{String: getMovieStringValue(index, name), Valid: true}, - FrontImage: []byte(models.DefaultMovieImage), - Checksum: utils.MD5FromString(name), + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), } created, err := mqb.Create(movie, tx) if err != nil { - return fmt.Errorf("Error creating movie %v+: %s", movie, err.Error()) + return fmt.Errorf("Error creating movie [%d] %v+: %s", i, movie, err.Error()) } movieIDs = append(movieIDs, created.ID) @@ -351,6 +371,13 @@ func getPerformerBoolValue(index int) bool { return index == 1 } +func getPerformerBirthdate(index int) string { + const minAge = 18 + birthdate := time.Now() + birthdate = birthdate.AddDate(-minAge-index, -1, -1) + return birthdate.Format("2006-01-02") +} + //createPerformers creates n performers with plain Name and o performers with camel cased NaMe included func createPerformers(tx *sqlx.Tx, n int, o int) error { pqb := models.NewPerformerQueryBuilder() @@ -371,9 +398,11 @@ func createPerformers(tx *sqlx.Tx, n int, o int) error { performer := models.Performer{ Name: sql.NullString{String: getPerformerStringValue(index, name), Valid: true}, Checksum: getPerformerStringValue(i, checksumField), - // just use movie image - Image: []byte(models.DefaultMovieImage), Favorite: sql.NullBool{Bool: getPerformerBoolValue(i), Valid: true}, + Birthdate: models.SQLiteDate{ + String: getPerformerBirthdate(i), + Valid: true, + }, } created, err := pqb.Create(performer, tx) @@ -393,6 +422,22 @@ func getTagStringValue(index int, field string) string { return "tag_" + strconv.FormatInt(int64(index), 10) + "_" + field } +func getTagSceneCount(id int) int { + if id == tagIDs[tagIdx1WithScene] || id == tagIDs[tagIdx2WithScene] || id == tagIDs[tagIdxWithScene] { + return 1 + } + + return 0 +} + +func getTagMarkerCount(id int) int { + if id == tagIDs[tagIdxWithMarker] || id == tagIDs[tagIdxWithPrimaryMarker] { + return 1 + } + + return 0 +} + //createTags creates n tags with plain Name and o tags with camel cased NaMe included func createTags(tx *sqlx.Tx, n int, o int) error { tqb := models.NewTagQueryBuilder() @@ -422,7 +467,6 @@ func createTags(tx *sqlx.Tx, n int, o int) error { tagIDs = append(tagIDs, created.ID) tagNames = append(tagNames, created.Name) - } return nil @@ -432,16 +476,34 @@ func getStudioStringValue(index int, field string) string { return "studio_" + strconv.FormatInt(int64(index), 10) + "_" + field } +func createStudio(tx *sqlx.Tx, name string, parentID *int64) (*models.Studio, error) { + sqb := models.NewStudioQueryBuilder() + studio := models.Studio{ + Name: sql.NullString{String: name, Valid: true}, + Checksum: utils.MD5FromString(name), + } + + if parentID != nil { + studio.ParentID = sql.NullInt64{Int64: *parentID, Valid: true} + } + + created, err := sqb.Create(studio, tx) + + if err != nil { + return nil, fmt.Errorf("Error creating studio %v+: %s", studio, err.Error()) + } + + return created, nil +} + //createStudios creates n studios with plain Name and o studios with camel cased NaMe included func createStudios(tx *sqlx.Tx, n int, o int) error { - sqb := models.NewStudioQueryBuilder() const namePlain = "Name" const nameNoCase = "NaMe" - name := namePlain - for i := 0; i < n+o; i++ { index := i + name := namePlain if i >= n { // i=n studios get dup names if case is not checked @@ -449,16 +511,11 @@ func createStudios(tx *sqlx.Tx, n int, o int) error { } // so count backwards to 0 as needed // studios [ i ] and [ n + o - i - 1 ] should have similar names with only the Name!=NaMe part different - tag := models.Studio{ - Name: sql.NullString{String: getStudioStringValue(index, name), Valid: true}, - Image: []byte(models.DefaultStudioImage), - Checksum: utils.MD5FromString(name), - } - - created, err := sqb.Create(tag, tx) + name = getStudioStringValue(index, name) + created, err := createStudio(tx, name, nil) if err != nil { - return fmt.Errorf("Error creating studio %v+: %s", tag, err.Error()) + return err } studioIDs = append(studioIDs, created.ID) @@ -582,3 +639,33 @@ func linkSceneStudio(tx *sqlx.Tx, sceneIndex, studioIndex int) error { return err } + +func linkMovieStudio(tx *sqlx.Tx, movieIndex, studioIndex int) error { + mqb := models.NewMovieQueryBuilder() + + movie := models.MoviePartial{ + ID: movieIDs[movieIndex], + StudioID: &sql.NullInt64{Int64: int64(studioIDs[studioIndex]), Valid: true}, + } + _, err := mqb.Update(movie, tx) + + return err +} + +func linkStudioParent(tx *sqlx.Tx, parentIndex, childIndex int) error { + sqb := models.NewStudioQueryBuilder() + + studio := models.StudioPartial{ + ID: studioIDs[childIndex], + ParentID: &sql.NullInt64{Int64: int64(studioIDs[parentIndex]), Valid: true}, + } + _, err := sqb.Update(studio, tx) + + return err +} + +func addTagImage(tx *sqlx.Tx, tagIndex int) error { + qb := models.NewTagQueryBuilder() + + return qb.UpdateTagImage(tagIDs[tagIndex], models.DefaultTagImage, tx) +} diff --git a/pkg/plugin/args.go b/pkg/plugin/args.go new file mode 100644 index 000000000..13fc3570e --- /dev/null +++ b/pkg/plugin/args.go @@ -0,0 +1,30 @@ +package plugin + +import ( + "github.com/stashapp/stash/pkg/models" +) + +func findArg(args []*models.PluginArgInput, name string) *models.PluginArgInput { + for _, v := range args { + if v.Key == name { + return v + } + } + + return nil +} + +func applyDefaultArgs(args []*models.PluginArgInput, defaultArgs map[string]string) []*models.PluginArgInput { + for k, v := range defaultArgs { + if arg := findArg(args, k); arg == nil { + args = append(args, &models.PluginArgInput{ + Key: k, + Value: &models.PluginValueInput{ + Str: &v, + }, + }) + } + } + + return args +} diff --git a/pkg/plugin/common/doc.go b/pkg/plugin/common/doc.go new file mode 100644 index 000000000..8aeab7c1f --- /dev/null +++ b/pkg/plugin/common/doc.go @@ -0,0 +1,3 @@ +// Package common encapulates data structures and functions that will be used +// by plugin executables and the plugin subsystem in the stash server. +package common diff --git a/pkg/plugin/common/log/log.go b/pkg/plugin/common/log/log.go new file mode 100644 index 000000000..33bf5f3cb --- /dev/null +++ b/pkg/plugin/common/log/log.go @@ -0,0 +1,203 @@ +// Package log provides a number of logging utility functions for encoding and +// decoding log messages between a stash server and a plugin instance. +// +// Log messages sent from a plugin instance are transmitted via stderr and are +// encoded with a prefix consisting of special character SOH, then the log +// level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, +// warning, error and progress levels respectively), then special character +// STX. +// +// The Trace, Debug, Info, Warning, and Error methods, and their equivalent +// formatted methods are intended for use by plugin instances to transmit log +// messages. The Progress method is also intended for sending progress data. +// +// Conversely, LevelFromName and DetectLogLevel are intended for use by the +// stash server. +package log + +import ( + "fmt" + "math" + "os" + "strings" +) + +// Level represents a logging level for plugin outputs. +type Level struct { + char byte + Name string +} + +// Valid Level values. +var ( + TraceLevel = Level{ + char: 't', + Name: "trace", + } + DebugLevel = Level{ + char: 'd', + Name: "debug", + } + InfoLevel = Level{ + char: 'i', + Name: "info", + } + WarningLevel = Level{ + char: 'w', + Name: "warning", + } + ErrorLevel = Level{ + char: 'e', + Name: "error", + } + ProgressLevel = Level{ + char: 'p', + } + NoneLevel = Level{ + Name: "none", + } +) + +var validLevels = []Level{ + TraceLevel, + DebugLevel, + InfoLevel, + WarningLevel, + ErrorLevel, + ProgressLevel, + NoneLevel, +} + +const startLevelChar byte = 1 +const endLevelChar byte = 2 + +func (l Level) prefix() string { + return string([]byte{ + startLevelChar, + byte(l.char), + endLevelChar, + }) +} + +func (l Level) log(args ...interface{}) { + if l.char == 0 { + return + } + + argsToUse := []interface{}{ + l.prefix(), + } + argsToUse = append(argsToUse, args...) + fmt.Fprintln(os.Stderr, argsToUse...) +} + +func (l Level) logf(format string, args ...interface{}) { + if l.char == 0 { + return + } + + formatToUse := string(l.prefix()) + format + "\n" + fmt.Fprintf(os.Stderr, formatToUse, args...) +} + +// Trace outputs a trace logging message to os.Stderr. Message is encoded with a +// prefix that signifies to the server that it is a trace message. +func Trace(args ...interface{}) { + TraceLevel.log(args...) +} + +// Tracef is the equivalent of Printf outputting as a trace logging message. +func Tracef(format string, args ...interface{}) { + TraceLevel.logf(format, args...) +} + +// Debug outputs a debug logging message to os.Stderr. Message is encoded with a +// prefix that signifies to the server that it is a debug message. +func Debug(args ...interface{}) { + DebugLevel.log(args...) +} + +// Debugf is the equivalent of Printf outputting as a debug logging message. +func Debugf(format string, args ...interface{}) { + DebugLevel.logf(format, args...) +} + +// Info outputs an info logging message to os.Stderr. Message is encoded with a +// prefix that signifies to the server that it is an info message. +func Info(args ...interface{}) { + InfoLevel.log(args...) +} + +// Infof is the equivalent of Printf outputting as an info logging message. +func Infof(format string, args ...interface{}) { + InfoLevel.logf(format, args...) +} + +// Warn outputs a warning logging message to os.Stderr. Message is encoded with a +// prefix that signifies to the server that it is a warning message. +func Warn(args ...interface{}) { + WarningLevel.log(args...) +} + +// Warnf is the equivalent of Printf outputting as a warning logging message. +func Warnf(format string, args ...interface{}) { + WarningLevel.logf(format, args...) +} + +// Error outputs an error logging message to os.Stderr. Message is encoded with a +// prefix that signifies to the server that it is an error message. +func Error(args ...interface{}) { + ErrorLevel.log(args...) +} + +// Errorf is the equivalent of Printf outputting as an error logging message. +func Errorf(format string, args ...interface{}) { + ErrorLevel.logf(format, args...) +} + +// Progress logs the current progress value. The progress value should be +// between 0 and 1.0 inclusively, with 1 representing that the task is +// complete. Values outside of this range will be clamp to be within it. +func Progress(progress float64) { + progress = math.Min(math.Max(0, progress), 1) + ProgressLevel.log(progress) +} + +// LevelFromName returns the Level that matches the provided name or nil if +// the name does not match a valid value. +func LevelFromName(name string) *Level { + for _, l := range validLevels { + if l.Name == name { + return &l + } + } + + return nil +} + +// DetectLogLevel returns the Level and the logging string for a provided line +// of plugin output. It parses the string for logging control characters and +// determines the log level, if present. If not present, the plugin output +// is returned unchanged with a nil Level. +func DetectLogLevel(line string) (*Level, string) { + if len(line) < 4 || line[0] != startLevelChar || line[2] != endLevelChar { + return nil, line + } + + char := line[1] + var level *Level + for _, l := range validLevels { + if l.char == char { + level = &l + break + } + } + + if level == nil { + return nil, line + } + + line = strings.TrimSpace(line[3:]) + + return level, line +} diff --git a/pkg/plugin/common/msg.go b/pkg/plugin/common/msg.go new file mode 100644 index 000000000..39eea860e --- /dev/null +++ b/pkg/plugin/common/msg.go @@ -0,0 +1,99 @@ +package common + +import "net/http" + +// StashServerConnection represents the connection details needed for a +// plugin instance to connect to its parent stash server. +type StashServerConnection struct { + // http or https + Scheme string + + Port int + + // Cookie for authentication purposes + SessionCookie *http.Cookie + + // Dir specifies the directory containing the stash server's configuration + // file. + Dir string + + // PluginDir specifies the directory containing the plugin configuration + // file. + PluginDir string +} + +// PluginArgValue represents a single value parameter for plugin operations. +type PluginArgValue interface{} + +// ArgsMap is a map of argument key to value. +type ArgsMap map[string]PluginArgValue + +// String returns the string field or an empty string if the string field is +// nil +func (m ArgsMap) String(key string) string { + v, found := m[key] + var ret string + if !found { + return ret + } + ret, _ = v.(string) + return ret +} + +// Int returns the int field or 0 if the int field is nil +func (m ArgsMap) Int(key string) int { + v, found := m[key] + var ret int + if !found { + return ret + } + ret, _ = v.(int) + return ret +} + +// Bool returns the boolean field or false if the boolean field is nil +func (m ArgsMap) Bool(key string) bool { + v, found := m[key] + var ret bool + if !found { + return ret + } + ret, _ = v.(bool) + return ret +} + +// Float returns the float field or 0 if the float field is nil +func (m ArgsMap) Float(key string) float64 { + v, found := m[key] + var ret float64 + if !found { + return ret + } + ret, _ = v.(float64) + return ret +} + +// PluginInput is the data structure that is sent to plugin instances when they +// are spawned. +type PluginInput struct { + // Server details to connect to the stash server. + ServerConnection StashServerConnection `json:"server_connection"` + + // Arguments to the plugin operation. + Args ArgsMap `json:"args"` +} + +// PluginOutput is the data structure that is expected to be output by plugin +// processes when execution has concluded. It is expected that this data will +// be encoded as JSON. +type PluginOutput struct { + Error *string `json:"error"` + Output interface{} `json:"output"` +} + +// SetError is a convenience method that sets the Error field based on the +// provided error. +func (o *PluginOutput) SetError(err error) { + errStr := err.Error() + o.Error = &errStr +} diff --git a/pkg/plugin/common/rpc.go b/pkg/plugin/common/rpc.go new file mode 100644 index 000000000..05b37451c --- /dev/null +++ b/pkg/plugin/common/rpc.go @@ -0,0 +1,30 @@ +package common + +import ( + "net/rpc/jsonrpc" + + "github.com/natefinch/pie" +) + +// RPCRunner is the interface that RPC plugins are expected to fulfil. +type RPCRunner interface { + // Perform the operation, using the provided input and populating the + // output object. + Run(input PluginInput, output *PluginOutput) error + + // Stop any running operations, if possible. No input is sent and any + // output is ignored. + Stop(input struct{}, output *bool) error +} + +// ServePlugin is used by plugin instances to serve the plugin via RPC, using +// the provided RPCRunner interface. +func ServePlugin(iface RPCRunner) error { + p := pie.NewProvider() + if err := p.RegisterName("RPCRunner", iface); err != nil { + return err + } + + p.ServeCodec(jsonrpc.NewServerCodec) + return nil +} diff --git a/pkg/plugin/config.go b/pkg/plugin/config.go new file mode 100644 index 000000000..c3bbfe9cd --- /dev/null +++ b/pkg/plugin/config.go @@ -0,0 +1,230 @@ +package plugin + +import ( + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/stashapp/stash/pkg/models" + "gopkg.in/yaml.v2" +) + +// Config describes the configuration for a single plugin. +type Config struct { + id string + + // path to the configuration file + path string + + // The name of the plugin. This will be displayed in the UI. + Name string `yaml:"name"` + + // An optional description of what the plugin does. + Description *string `yaml:"description"` + + // An optional URL for the plugin. + URL *string `yaml:"url"` + + // An optional version string. + Version *string `yaml:"version"` + + // The communication interface used when communicating with the spawned + // plugin process. Defaults to 'raw' if not provided. + Interface interfaceEnum `yaml:"interface"` + + // The command to execute for the operations in this plugin. The first + // element should be the program name, and subsequent elements are passed + // as arguments. + // + // Note: the execution process will search the path for the program, + // then will attempt to find the program in the plugins + // directory. The exe extension is not necessary on Windows platforms. + // The current working directory is set to that of the stash process. + Exec []string `yaml:"exec,flow"` + + // The default log level to output the plugin process's stderr stream. + // Only used if the plugin does not encode its output using log level + // control characters. + // See package common/log for valid values. + // If left unset, defaults to log.ErrorLevel. + PluginErrLogLevel string `yaml:"errLog"` + + // The task configurations for tasks provided by this plugin. + Tasks []*OperationConfig `yaml:"tasks"` +} + +func (c Config) getPluginTasks(includePlugin bool) []*models.PluginTask { + var ret []*models.PluginTask + + for _, o := range c.Tasks { + task := &models.PluginTask{ + Name: o.Name, + Description: &o.Description, + } + + if includePlugin { + task.Plugin = c.toPlugin() + } + ret = append(ret, task) + } + + return ret +} + +func (c Config) getName() string { + if c.Name != "" { + return c.Name + } + + return c.id +} + +func (c Config) toPlugin() *models.Plugin { + return &models.Plugin{ + ID: c.id, + Name: c.getName(), + Description: c.Description, + URL: c.URL, + Version: c.Version, + Tasks: c.getPluginTasks(false), + } +} + +func (c Config) getTask(name string) *OperationConfig { + for _, o := range c.Tasks { + if o.Name == name { + return o + } + } + + return nil +} + +func (c Config) getConfigPath() string { + return filepath.Dir(c.path) +} + +func (c Config) getExecCommand(task *OperationConfig) []string { + ret := c.Exec + + ret = append(ret, task.ExecArgs...) + + if len(ret) > 0 { + _, err := exec.LookPath(ret[0]) + if err != nil { + // change command to use absolute path + pluginPath := filepath.Dir(c.path) + ret[0] = filepath.Join(pluginPath, ret[0]) + } + } + + // replace {pluginDir} in arguments with that of the plugin directory + dir := c.getConfigPath() + for i, arg := range ret { + if i == 0 { + continue + } + + ret[i] = strings.Replace(arg, "{pluginDir}", dir, -1) + } + + return ret +} + +type interfaceEnum string + +// Valid interfaceEnum values +const ( + // InterfaceEnumRPC indicates that the plugin uses the RPCRunner interface + // declared in common/rpc.go. + InterfaceEnumRPC interfaceEnum = "rpc" + + // InterfaceEnumRaw interfaces will have the common.PluginInput encoded as + // json (but may be ignored), and output will be decoded as + // common.PluginOutput. If this decoding fails, then the raw output will be + // treated as the output. + InterfaceEnumRaw interfaceEnum = "raw" +) + +func (i interfaceEnum) Valid() bool { + return i == InterfaceEnumRPC || i == InterfaceEnumRaw +} + +func (i *interfaceEnum) getTaskBuilder() taskBuilder { + if *i == InterfaceEnumRaw { + return &rawTaskBuilder{} + } + + if *i == InterfaceEnumRPC { + return &rpcTaskBuilder{} + } + + // shouldn't happen + return nil +} + +// OperationConfig describes the configuration for a single plugin operation +// provided by a plugin. +type OperationConfig struct { + // Used to identify the operation. Must be unique within a plugin + // configuration. This name is shown in the button for the operation + // in the UI. + Name string `yaml:"name"` + + // A short description of the operation. This description is shown below + // the button in the UI. + Description string `yaml:"description"` + + // A list of arguments that will be appended to the plugin's Exec arguments + // when executing this operation. + ExecArgs []string `yaml:"execArgs"` + + // A map of argument keys to their default values. The default value is + // used if the applicable argument is not provided during the operation + // call. + DefaultArgs map[string]string `yaml:"defaultArgs"` +} + +func loadPluginFromYAML(reader io.Reader) (*Config, error) { + ret := &Config{} + + parser := yaml.NewDecoder(reader) + parser.SetStrict(true) + err := parser.Decode(&ret) + if err != nil { + return nil, err + } + + if ret.Interface == "" { + ret.Interface = InterfaceEnumRaw + } + + if !ret.Interface.Valid() { + return nil, fmt.Errorf("invalid interface type %s", ret.Interface) + } + + return ret, nil +} + +func loadPluginFromYAMLFile(path string) (*Config, error) { + file, err := os.Open(path) + defer file.Close() + if err != nil { + return nil, err + } + + ret, err := loadPluginFromYAML(file) + if err != nil { + return nil, err + } + + // set id to the filename + id := filepath.Base(path) + ret.id = id[:strings.LastIndex(id, ".")] + ret.path = path + + return ret, nil +} diff --git a/pkg/plugin/convert.go b/pkg/plugin/convert.go new file mode 100644 index 000000000..989008d60 --- /dev/null +++ b/pkg/plugin/convert.go @@ -0,0 +1,42 @@ +package plugin + +import ( + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/plugin/common" +) + +func toPluginArgs(args []*models.PluginArgInput) common.ArgsMap { + ret := make(common.ArgsMap) + for _, a := range args { + ret[a.Key] = toPluginArgValue(a.Value) + } + + return ret +} + +func toPluginArgValue(arg *models.PluginValueInput) common.PluginArgValue { + if arg == nil { + return nil + } + + switch { + case arg.Str != nil: + return common.PluginArgValue(*arg.Str) + case arg.I != nil: + return common.PluginArgValue(*arg.I) + case arg.B != nil: + return common.PluginArgValue(*arg.B) + case arg.F != nil: + return common.PluginArgValue(*arg.F) + case arg.O != nil: + return common.PluginArgValue(toPluginArgs(arg.O)) + case arg.A != nil: + var ret []common.PluginArgValue + for _, v := range arg.A { + ret = append(ret, toPluginArgValue(v)) + } + return common.PluginArgValue(ret) + } + + return nil +} diff --git a/pkg/plugin/examples/README.md b/pkg/plugin/examples/README.md new file mode 100644 index 000000000..73f828c96 --- /dev/null +++ b/pkg/plugin/examples/README.md @@ -0,0 +1,9 @@ +# Building + +From the base stash source directory: +``` +go build -tags=plugin_example -o plugin_goraw.exe ./pkg/plugin/examples/goraw/... +go build -tags=plugin_example -o plugin_gorpc.exe ./pkg/plugin/examples/gorpc/... +``` + +Place the resulting binaries together with the yml files in the `plugins` subdirectory of your stash directory. \ No newline at end of file diff --git a/pkg/plugin/examples/common/graphql.go b/pkg/plugin/examples/common/graphql.go new file mode 100644 index 000000000..3ed5ec341 --- /dev/null +++ b/pkg/plugin/examples/common/graphql.go @@ -0,0 +1,231 @@ +// +build plugin_example + +package common + +import ( + "context" + "errors" + "fmt" + + "github.com/shurcooL/graphql" + "github.com/stashapp/stash/pkg/plugin/common/log" +) + +const tagName = "Hawwwwt" + +// graphql inputs and returns +type TagCreate struct { + ID graphql.ID `graphql:"id"` +} + +type TagCreateInput struct { + Name graphql.String `graphql:"name" json:"name"` +} + +type TagDestroyInput struct { + ID graphql.ID `graphql:"id" json:"id"` +} + +type FindScenesResultType struct { + Count graphql.Int + Scenes []Scene +} + +type Tag struct { + ID graphql.ID `graphql:"id"` + Name graphql.String `graphql:"name"` +} + +type Scene struct { + ID graphql.ID + Tags []Tag +} + +func (s Scene) getTagIds() []graphql.ID { + ret := []graphql.ID{} + + for _, t := range s.Tags { + ret = append(ret, t.ID) + } + + return ret +} + +type FindFilterType struct { + PerPage *graphql.Int `graphql:"per_page" json:"per_page"` + Sort *graphql.String `graphql:"sort" json:"sort"` +} + +type SceneUpdate struct { + ID graphql.ID `graphql:"id"` +} + +type SceneUpdateInput struct { + ID graphql.ID `graphql:"id" json:"id"` + TagIds []graphql.ID `graphql:"tag_ids" json:"tag_ids"` +} + +func getTagID(client *graphql.Client, create bool) (*graphql.ID, error) { + log.Info("Checking if tag exists already") + + // see if tag exists already + var q struct { + AllTags []Tag `graphql:"allTags"` + } + + err := client.Query(context.Background(), &q, nil) + if err != nil { + return nil, fmt.Errorf("Error getting tags: %s\n", err.Error()) + } + + for _, t := range q.AllTags { + if t.Name == tagName { + id := t.ID + return &id, nil + } + } + + if !create { + log.Info("Not found and not creating") + return nil, nil + } + + // create the tag + var m struct { + TagCreate TagCreate `graphql:"tagCreate(input: $s)"` + } + + input := TagCreateInput{ + Name: tagName, + } + + vars := map[string]interface{}{ + "s": input, + } + + log.Info("Creating new tag") + + err = client.Mutate(context.Background(), &m, vars) + if err != nil { + return nil, fmt.Errorf("Error mutating scene: %s\n", err.Error()) + } + + return &m.TagCreate.ID, nil +} + +func findRandomScene(client *graphql.Client) (*Scene, error) { + // get a random scene + var q struct { + FindScenes FindScenesResultType `graphql:"findScenes(filter: $c)"` + } + + pp := graphql.Int(1) + sort := graphql.String("random") + filterInput := &FindFilterType{ + PerPage: &pp, + Sort: &sort, + } + + vars := map[string]interface{}{ + "c": filterInput, + } + + log.Info("Finding a random scene") + err := client.Query(context.Background(), &q, vars) + if err != nil { + return nil, fmt.Errorf("Error getting random scene: %s\n", err.Error()) + } + + if q.FindScenes.Count == 0 { + return nil, nil + } + + return &q.FindScenes.Scenes[0], nil +} + +func addTagId(tagIds []graphql.ID, tagId graphql.ID) []graphql.ID { + for _, t := range tagIds { + if t == tagId { + return tagIds + } + } + + tagIds = append(tagIds, tagId) + return tagIds +} + +func AddTag(client *graphql.Client) error { + tagID, err := getTagID(client, true) + + if err != nil { + return err + } + + scene, err := findRandomScene(client) + + if err != nil { + return err + } + + if scene == nil { + return errors.New("no scenes to add tag to") + } + + var m struct { + SceneUpdate SceneUpdate `graphql:"sceneUpdate(input: $s)"` + } + + input := SceneUpdateInput{ + ID: scene.ID, + TagIds: scene.getTagIds(), + } + + input.TagIds = addTagId(input.TagIds, *tagID) + + vars := map[string]interface{}{ + "s": input, + } + + log.Infof("Adding tag to scene %v", scene.ID) + err = client.Mutate(context.Background(), &m, vars) + if err != nil { + return fmt.Errorf("Error mutating scene: %s", err.Error()) + } + + return nil +} + +func RemoveTag(client *graphql.Client) error { + tagID, err := getTagID(client, false) + + if err != nil { + return err + } + + if tagID == nil { + log.Info("Tag does not exist. Nothing to remove") + return nil + } + + // destroy the tag + var m struct { + TagDestroy bool `graphql:"tagDestroy(input: $s)"` + } + + input := TagDestroyInput{ + ID: *tagID, + } + + vars := map[string]interface{}{ + "s": input, + } + + log.Info("Destroying tag") + + err = client.Mutate(context.Background(), &m, vars) + if err != nil { + return fmt.Errorf("Error destroying tag: %s", err.Error()) + } + + return nil +} diff --git a/pkg/plugin/examples/goraw/goraw.yml b/pkg/plugin/examples/goraw/goraw.yml new file mode 100644 index 000000000..de7376a4e --- /dev/null +++ b/pkg/plugin/examples/goraw/goraw.yml @@ -0,0 +1,28 @@ +# example plugin config +name: Hawwwwt Tagger (Raw edition) +description: Ultimate Hawwwwt tagging utility (using raw interface). +version: 1.0 +url: http://www.github.com/stashapp/stash +exec: + - plugin_goraw +interface: raw +tasks: + - name: Add hawwwwt tag to random scene + description: Creates a "Hawwwwt" tag if not present and adds to a random scene. + defaultArgs: + mode: add + - name: Remove hawwwwt tag from system + description: Removes the "Hawwwwt" tag from all scenes and deletes the tag. + defaultArgs: + mode: remove + - name: Indefinite task + description: Sleeps indefinitely - interruptable + # we'll try command-line argument for this one + execArgs: + - indef + - "{pluginDir}" + - name: Long task + description: Sleeps for 100 seconds - interruptable + defaultArgs: + mode: long + \ No newline at end of file diff --git a/pkg/plugin/examples/goraw/main.go b/pkg/plugin/examples/goraw/main.go new file mode 100644 index 000000000..26060f32c --- /dev/null +++ b/pkg/plugin/examples/goraw/main.go @@ -0,0 +1,106 @@ +// +build plugin_example + +package main + +import ( + "encoding/json" + "io/ioutil" + "os" + "time" + + exampleCommon "github.com/stashapp/stash/pkg/plugin/examples/common" + + "github.com/stashapp/stash/pkg/plugin/common" + "github.com/stashapp/stash/pkg/plugin/common/log" + "github.com/stashapp/stash/pkg/plugin/util" +) + +// raw plugins may accept the plugin input from stdin, or they can elect +// to ignore it entirely. In this case it optionally reads from the +// command-line parameters. +func main() { + input := common.PluginInput{} + + if len(os.Args) < 2 { + inData, _ := ioutil.ReadAll(os.Stdin) + log.Debugf("Raw input: %s", string(inData)) + decodeErr := json.Unmarshal(inData, &input) + + if decodeErr != nil { + panic("missing mode argument") + } + } else { + log.Debug("Using command line inputs") + mode := os.Args[1] + log.Debugf("Command line inputs: %v", os.Args[1:]) + input.Args = common.ArgsMap{ + "mode": mode, + } + + // just some hard-coded values + input.ServerConnection = common.StashServerConnection{ + Scheme: "http", + Port: 9999, + } + } + + output := common.PluginOutput{} + Run(input, &output) + + out, _ := json.Marshal(output) + os.Stdout.WriteString(string(out)) +} + +func Run(input common.PluginInput, output *common.PluginOutput) error { + modeArg := input.Args.String("mode") + + var err error + if modeArg == "" || modeArg == "add" { + client := util.NewClient(input.ServerConnection) + err = exampleCommon.AddTag(client) + } else if modeArg == "remove" { + client := util.NewClient(input.ServerConnection) + err = exampleCommon.RemoveTag(client) + } else if modeArg == "long" { + err = doLongTask() + } else if modeArg == "indef" { + err = doIndefiniteTask() + } + + if err != nil { + errStr := err.Error() + *output = common.PluginOutput{ + Error: &errStr, + } + return nil + } + + outputStr := "ok" + *output = common.PluginOutput{ + Output: &outputStr, + } + + return nil +} + +func doLongTask() error { + const total = 100 + upTo := 0 + + log.Info("Doing long task") + for upTo < total { + time.Sleep(time.Second) + + log.Progress(float64(upTo) / float64(total)) + upTo++ + } + + return nil +} + +func doIndefiniteTask() error { + log.Warn("Sleeping indefinitely") + for { + time.Sleep(time.Second) + } +} diff --git a/pkg/plugin/examples/gorpc/gorpc.yml b/pkg/plugin/examples/gorpc/gorpc.yml new file mode 100644 index 000000000..fc9fa74be --- /dev/null +++ b/pkg/plugin/examples/gorpc/gorpc.yml @@ -0,0 +1,26 @@ +# example plugin config +name: Hawwwwt Tagger +description: Ultimate Hawwwwt tagging utility. +version: 1.0 +url: http://www.github.com/stashapp/stash +exec: + - plugin_gorpc +interface: rpc +tasks: + - name: Add hawwwwt tag to random scene + description: Creates a "Hawwwwt" tag if not present and adds to a random scene. + defaultArgs: + mode: add + - name: Remove hawwwwt tag from system + description: Removes the "Hawwwwt" tag from all scenes and deletes the tag. + defaultArgs: + mode: remove + - name: Indefinite task + description: Sleeps indefinitely - interruptable + defaultArgs: + mode: indef + - name: Long task + description: Sleeps for 100 seconds - interruptable + defaultArgs: + mode: long + \ No newline at end of file diff --git a/pkg/plugin/examples/gorpc/main.go b/pkg/plugin/examples/gorpc/main.go new file mode 100644 index 000000000..75e364549 --- /dev/null +++ b/pkg/plugin/examples/gorpc/main.go @@ -0,0 +1,97 @@ +// +build plugin_example + +package main + +import ( + "time" + + exampleCommon "github.com/stashapp/stash/pkg/plugin/examples/common" + + "github.com/stashapp/stash/pkg/plugin/common" + "github.com/stashapp/stash/pkg/plugin/common/log" + "github.com/stashapp/stash/pkg/plugin/util" +) + +func main() { + // serves the plugin, providing an object that satisfies the + // common.RPCRunner interface + err := common.ServePlugin(&api{}) + if err != nil { + panic(err) + } +} + +type api struct { + stopping bool +} + +func (a *api) Stop(input struct{}, output *bool) error { + log.Info("Stopping...") + a.stopping = true + *output = true + return nil +} + +// Run is the main work function of the plugin. It interprets the input and +// acts accordingly. +func (a *api) Run(input common.PluginInput, output *common.PluginOutput) error { + modeArg := input.Args.String("mode") + + var err error + if modeArg == "" || modeArg == "add" { + client := util.NewClient(input.ServerConnection) + err = exampleCommon.AddTag(client) + } else if modeArg == "remove" { + client := util.NewClient(input.ServerConnection) + err = exampleCommon.RemoveTag(client) + } else if modeArg == "long" { + err = a.doLongTask() + } else if modeArg == "indef" { + err = a.doIndefiniteTask() + } + + if err != nil { + errStr := err.Error() + *output = common.PluginOutput{ + Error: &errStr, + } + return nil + } + + outputStr := "ok" + *output = common.PluginOutput{ + Output: &outputStr, + } + + return nil +} + +func (a *api) doLongTask() error { + const total = 100 + upTo := 0 + + log.Info("Doing long task") + for upTo < total { + time.Sleep(time.Second) + if a.stopping { + return nil + } + + log.Progress(float64(upTo) / float64(total)) + upTo++ + } + + return nil +} + +func (a *api) doIndefiniteTask() error { + log.Warn("Sleeping indefinitely") + for { + time.Sleep(time.Second) + if a.stopping { + return nil + } + } + + return nil +} diff --git a/pkg/plugin/log.go b/pkg/plugin/log.go new file mode 100644 index 000000000..96727f688 --- /dev/null +++ b/pkg/plugin/log.go @@ -0,0 +1,79 @@ +package plugin + +import ( + "bufio" + "io" + "strconv" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/plugin/common/log" +) + +func (t *pluginTask) handleStderrLine(line string, defaultLogLevel *log.Level) { + level, l := log.DetectLogLevel(line) + + const pluginPrefix = "[Plugin] " + // if no log level, just output to info + if level == nil { + if defaultLogLevel != nil { + level = defaultLogLevel + } else { + level = &log.InfoLevel + } + } + + switch *level { + case log.TraceLevel: + logger.Trace(pluginPrefix, l) + case log.DebugLevel: + logger.Debug(pluginPrefix, l) + case log.InfoLevel: + logger.Info(pluginPrefix, l) + case log.WarningLevel: + logger.Warn(pluginPrefix, l) + case log.ErrorLevel: + logger.Error(pluginPrefix, l) + case log.ProgressLevel: + progress, err := strconv.ParseFloat(l, 64) + if err != nil { + logger.Errorf("Error parsing progress value '%s': %s", l, err.Error()) + } else { + // only pass progress through if channel present + if t.progress != nil { + // don't block on this + select { + case t.progress <- progress: + default: + } + } + } + } +} + +func (t *pluginTask) handlePluginOutput(pluginOutputReader io.ReadCloser, defaultLogLevel *log.Level) { + // pipe plugin stderr to our logging + scanner := bufio.NewScanner(pluginOutputReader) + for scanner.Scan() { + str := scanner.Text() + if str != "" { + t.handleStderrLine(str, defaultLogLevel) + } + } + + str := scanner.Text() + if str != "" { + t.handleStderrLine(str, defaultLogLevel) + } + + pluginOutputReader.Close() +} + +func (t *pluginTask) handlePluginStderr(pluginOutputReader io.ReadCloser) { + logLevel := log.LevelFromName(t.plugin.PluginErrLogLevel) + if logLevel == nil { + // default log level to error + logLevel = &log.ErrorLevel + } + + t.handlePluginOutput(pluginOutputReader, logLevel) +} diff --git a/pkg/plugin/plugins.go b/pkg/plugin/plugins.go new file mode 100644 index 000000000..77819de28 --- /dev/null +++ b/pkg/plugin/plugins.go @@ -0,0 +1,140 @@ +// Package plugin implements functions and types for maintaining and running +// stash plugins. +// +// Stash plugins are configured using yml files in the configured plugins +// directory. These yml files must follow the Config structure format. +// +// The main entry into the plugin sub-system is via the Cache type. +package plugin + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/plugin/common" +) + +// Cache stores plugin details. +type Cache struct { + path string + plugins []Config +} + +// NewCache returns a new Cache loading plugin configurations +// from the provided plugin path. It returns an new instance and an error +// if the plugin directory could not be loaded. +// +// Plugins configurations are loaded from yml files in the provided plugin +// directory and any subdirectories. +func NewCache(pluginPath string) (*Cache, error) { + plugins, err := loadPlugins(pluginPath) + if err != nil { + return nil, err + } + + return &Cache{ + path: pluginPath, + plugins: plugins, + }, nil +} + +// ReloadPlugins clears the plugin cache and reloads from the plugin path. +// In the event of an error during loading, the cache will be left empty. +func (c *Cache) ReloadPlugins() error { + c.plugins = nil + plugins, err := loadPlugins(c.path) + if err != nil { + return err + } + + c.plugins = plugins + return nil +} + +func loadPlugins(path string) ([]Config, error) { + plugins := make([]Config, 0) + + logger.Debugf("Reading plugin configs from %s", path) + pluginFiles := []string{} + err := filepath.Walk(path, func(fp string, f os.FileInfo, err error) error { + if filepath.Ext(fp) == ".yml" { + pluginFiles = append(pluginFiles, fp) + } + return nil + }) + + if err != nil { + + return nil, err + } + + for _, file := range pluginFiles { + plugin, err := loadPluginFromYAMLFile(file) + if err != nil { + logger.Errorf("Error loading plugin %s: %s", file, err.Error()) + } else { + plugins = append(plugins, *plugin) + } + } + + return plugins, nil +} + +// ListPlugins returns plugin details for all of the loaded plugins. +func (c Cache) ListPlugins() []*models.Plugin { + var ret []*models.Plugin + for _, s := range c.plugins { + ret = append(ret, s.toPlugin()) + } + + return ret +} + +// ListPluginTasks returns all runnable plugin tasks in all loaded plugins. +func (c Cache) ListPluginTasks() []*models.PluginTask { + var ret []*models.PluginTask + for _, s := range c.plugins { + ret = append(ret, s.getPluginTasks(true)...) + } + + return ret +} + +// CreateTask runs the plugin operation for the pluginID and operation +// name provided. Returns an error if the plugin or the operation could not be +// resolved. +func (c Cache) CreateTask(pluginID string, operationName string, serverConnection common.StashServerConnection, args []*models.PluginArgInput, progress chan float64) (Task, error) { + // find the plugin and operation + plugin := c.getPlugin(pluginID) + + if plugin == nil { + return nil, fmt.Errorf("no plugin with ID %s", pluginID) + } + + operation := plugin.getTask(operationName) + if operation == nil { + return nil, fmt.Errorf("no task with name %s in plugin %s", operationName, plugin.getName()) + } + + task := pluginTask{ + plugin: plugin, + operation: operation, + serverConnection: serverConnection, + args: args, + progress: progress, + } + return task.createTask(), nil +} + +func (c Cache) getPlugin(pluginID string) *Config { + for _, s := range c.plugins { + if s.id == pluginID { + return &s + } + } + + return nil +} diff --git a/pkg/plugin/raw.go b/pkg/plugin/raw.go new file mode 100644 index 000000000..0169b9bb6 --- /dev/null +++ b/pkg/plugin/raw.go @@ -0,0 +1,122 @@ +package plugin + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os/exec" + "sync" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/plugin/common" +) + +type rawTaskBuilder struct{} + +func (*rawTaskBuilder) build(task pluginTask) Task { + return &rawPluginTask{ + pluginTask: task, + } +} + +type rawPluginTask struct { + pluginTask + + started bool + waitGroup sync.WaitGroup + cmd *exec.Cmd + done chan bool +} + +func (t *rawPluginTask) Start() error { + if t.started { + return errors.New("task already started") + } + + command := t.plugin.getExecCommand(t.operation) + if len(command) == 0 { + return fmt.Errorf("empty exec value in operation %s", t.operation.Name) + } + + cmd := exec.Command(command[0], command[1:]...) + + stdin, err := cmd.StdinPipe() + if err != nil { + return fmt.Errorf("error getting plugin process stdin: %s", err.Error()) + } + + go func() { + defer stdin.Close() + + input := t.buildPluginInput() + inBytes, _ := json.Marshal(input) + io.WriteString(stdin, string(inBytes)) + }() + + stderr, err := cmd.StderrPipe() + if err != nil { + logger.Error("Plugin stderr not available: " + err.Error()) + } + + stdout, err := cmd.StdoutPipe() + if nil != err { + logger.Error("Plugin stdout not available: " + err.Error()) + } + + t.waitGroup.Add(1) + t.done = make(chan bool, 1) + if err = cmd.Start(); err != nil { + return fmt.Errorf("Error running plugin: %s", err.Error()) + } + + go t.handlePluginStderr(stderr) + t.cmd = cmd + + // send the stdout to the plugin output + go func() { + defer t.waitGroup.Done() + defer close(t.done) + stdoutData, _ := ioutil.ReadAll(stdout) + stdoutString := string(stdoutData) + + output := t.getOutput(stdoutString) + + err := cmd.Wait() + if err != nil && output.Error == nil { + errStr := err.Error() + output.Error = &errStr + } + + t.result = &output + }() + + t.started = true + return nil +} + +func (t *rawPluginTask) getOutput(output string) common.PluginOutput { + // try to parse the output as a PluginOutput json. If it fails just + // get the raw output + ret := common.PluginOutput{} + decodeErr := json.Unmarshal([]byte(output), &ret) + + if decodeErr != nil { + ret.Output = &output + } + + return ret +} + +func (t *rawPluginTask) Wait() { + t.waitGroup.Wait() +} + +func (t *rawPluginTask) Stop() error { + if t.cmd == nil { + return nil + } + + return t.cmd.Process.Kill() +} diff --git a/pkg/plugin/rpc.go b/pkg/plugin/rpc.go new file mode 100644 index 000000000..f12b17cd8 --- /dev/null +++ b/pkg/plugin/rpc.go @@ -0,0 +1,103 @@ +package plugin + +import ( + "errors" + "fmt" + "io" + "net/rpc" + "net/rpc/jsonrpc" + "sync" + + "github.com/natefinch/pie" + "github.com/stashapp/stash/pkg/plugin/common" +) + +type rpcTaskBuilder struct{} + +func (*rpcTaskBuilder) build(task pluginTask) Task { + return &rpcPluginTask{ + pluginTask: task, + } +} + +type rpcPluginClient struct { + Client *rpc.Client +} + +func (p rpcPluginClient) Run(input common.PluginInput, output *common.PluginOutput) error { + return p.Client.Call("RPCRunner.Run", input, output) +} + +func (p rpcPluginClient) RunAsync(input common.PluginInput, output *common.PluginOutput, done chan *rpc.Call) *rpc.Call { + return p.Client.Go("RPCRunner.Run", input, output, done) +} + +func (p rpcPluginClient) Stop() error { + var resp interface{} + return p.Client.Call("RPCRunner.Stop", nil, &resp) +} + +type rpcPluginTask struct { + pluginTask + + started bool + client *rpc.Client + waitGroup sync.WaitGroup + done chan *rpc.Call +} + +func (t *rpcPluginTask) Start() error { + if t.started { + return errors.New("task already started") + } + + command := t.plugin.getExecCommand(t.operation) + if len(command) == 0 { + return fmt.Errorf("empty exec value in operation %s", t.operation.Name) + } + + pluginErrReader, pluginErrWriter := io.Pipe() + + var err error + t.client, err = pie.StartProviderCodec(jsonrpc.NewClientCodec, pluginErrWriter, command[0], command[1:]...) + if err != nil { + return err + } + + go t.handlePluginStderr(pluginErrReader) + + iface := rpcPluginClient{ + Client: t.client, + } + + input := t.buildPluginInput() + + t.done = make(chan *rpc.Call, 1) + result := common.PluginOutput{} + t.waitGroup.Add(1) + iface.RunAsync(input, &result, t.done) + go t.waitToFinish(&result) + + t.started = true + return nil +} + +func (t *rpcPluginTask) waitToFinish(result *common.PluginOutput) { + defer t.client.Close() + defer t.waitGroup.Done() + <-t.done + + t.result = result +} + +func (t *rpcPluginTask) Wait() { + t.waitGroup.Wait() +} + +func (t *rpcPluginTask) Stop() error { + iface := rpcPluginClient{ + Client: t.client, + } + + return iface.Stop() +} diff --git a/pkg/plugin/task.go b/pkg/plugin/task.go new file mode 100644 index 000000000..e5635a93d --- /dev/null +++ b/pkg/plugin/task.go @@ -0,0 +1,56 @@ +package plugin + +import ( + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/plugin/common" +) + +// Task is the interface that handles management of a single plugin task. +type Task interface { + // Start starts the plugin task. Returns an error if task could not be + // started or the task has already been started. + Start() error + + // Stop instructs a running plugin task to stop and returns immediately. + // Use Wait to subsequently wait for the task to stop. + Stop() error + + // Wait blocks until the plugin task is complete. Returns immediately if + // task has not been started. + Wait() + + // GetResult returns the output of the plugin task. Returns nil if the task + // has not completed. + GetResult() *common.PluginOutput +} + +type taskBuilder interface { + build(task pluginTask) Task +} + +type pluginTask struct { + plugin *Config + operation *OperationConfig + serverConnection common.StashServerConnection + args []*models.PluginArgInput + + progress chan float64 + result *common.PluginOutput +} + +func (t *pluginTask) GetResult() *common.PluginOutput { + return t.result +} + +func (t *pluginTask) createTask() Task { + return t.plugin.Interface.getTaskBuilder().build(*t) +} + +func (t *pluginTask) buildPluginInput() common.PluginInput { + args := applyDefaultArgs(t.args, t.operation.DefaultArgs) + t.serverConnection.PluginDir = t.plugin.getConfigPath() + return common.PluginInput{ + ServerConnection: t.serverConnection, + Args: toPluginArgs(args), + } +} diff --git a/pkg/plugin/util/client.go b/pkg/plugin/util/client.go new file mode 100644 index 000000000..a51dca5aa --- /dev/null +++ b/pkg/plugin/util/client.go @@ -0,0 +1,39 @@ +// Package util implements utility and convenience methods for plugins. It is +// not intended for the main stash code to access. +package util + +import ( + "net/http" + "net/http/cookiejar" + "net/url" + "strconv" + + "github.com/shurcooL/graphql" + + "github.com/stashapp/stash/pkg/plugin/common" +) + +// NewClient creates a graphql Client connecting to the stash server using +// the provided server connection details. +// Always connects to the graphql endpoint of the localhost. +func NewClient(provider common.StashServerConnection) *graphql.Client { + portStr := strconv.Itoa(provider.Port) + + u, _ := url.Parse("http://localhost:" + portStr + "/graphql") + u.Scheme = provider.Scheme + + cookieJar, _ := cookiejar.New(nil) + + cookie := provider.SessionCookie + if cookie != nil { + cookieJar.SetCookies(u, []*http.Cookie{ + cookie, + }) + } + + httpClient := &http.Client{ + Jar: cookieJar, + } + + return graphql.NewClient(u.String(), httpClient) +} diff --git a/pkg/scraper/action.go b/pkg/scraper/action.go new file mode 100644 index 000000000..8156fb6ce --- /dev/null +++ b/pkg/scraper/action.go @@ -0,0 +1,59 @@ +package scraper + +import "github.com/stashapp/stash/pkg/models" + +type scraperAction string + +const ( + scraperActionScript scraperAction = "script" + scraperActionStash scraperAction = "stash" + scraperActionXPath scraperAction = "scrapeXPath" + scraperActionJson scraperAction = "scrapeJson" +) + +var allScraperAction = []scraperAction{ + scraperActionScript, + scraperActionStash, + scraperActionXPath, + scraperActionJson, +} + +func (e scraperAction) IsValid() bool { + switch e { + case scraperActionScript, scraperActionStash, scraperActionXPath, scraperActionJson: + return true + } + return false +} + +type scrapeOptions struct { + scraper scraperTypeConfig + config config + globalConfig GlobalConfig +} + +type scraper interface { + scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) + scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) + scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) + + scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) + scrapeSceneByURL(url string) (*models.ScrapedScene, error) + + scrapeMovieByURL(url string) (*models.ScrapedMovie, error) +} + +func getScraper(scraper scraperTypeConfig, config config, globalConfig GlobalConfig) scraper { + switch scraper.Action { + case scraperActionScript: + return newScriptScraper(scraper, config, globalConfig) + case scraperActionStash: + return newStashScraper(scraper, config, globalConfig) + case scraperActionXPath: + return newXpathScraper(scraper, config, globalConfig) + case scraperActionJson: + return newJsonScraper(scraper, config, globalConfig) + } + + panic("unknown scraper action: " + scraper.Action) +} diff --git a/pkg/scraper/config.go b/pkg/scraper/config.go index 07e916d16..fad5e04e4 100644 --- a/pkg/scraper/config.go +++ b/pkg/scraper/config.go @@ -1,6 +1,8 @@ package scraper import ( + "errors" + "fmt" "io" "os" "path/filepath" @@ -11,32 +13,95 @@ import ( "github.com/stashapp/stash/pkg/models" ) +type config struct { + ID string + path string + + // The name of the scraper. This is displayed in the UI. + Name string `yaml:"name"` + + // Configuration for querying performers by name + PerformerByName *scraperTypeConfig `yaml:"performerByName"` + + // Configuration for querying performers by a Performer fragment + PerformerByFragment *scraperTypeConfig `yaml:"performerByFragment"` + + // Configuration for querying a performer by a URL + PerformerByURL []*scrapeByURLConfig `yaml:"performerByURL"` + + // Configuration for querying scenes by a Scene fragment + SceneByFragment *scraperTypeConfig `yaml:"sceneByFragment"` + + // Configuration for querying a scene by a URL + SceneByURL []*scrapeByURLConfig `yaml:"sceneByURL"` + + // Configuration for querying a movie by a URL + MovieByURL []*scrapeByURLConfig `yaml:"movieByURL"` + + // Scraper debugging options + DebugOptions *scraperDebugOptions `yaml:"debug"` + + // Stash server configuration + StashServer *stashServer `yaml:"stashServer"` + + // Xpath scraping configurations + XPathScrapers mappedScrapers `yaml:"xPathScrapers"` + + // Json scraping configurations + JsonScrapers mappedScrapers `yaml:"jsonScrapers"` + + // Scraping driver options + DriverOptions *scraperDriverOptions `yaml:"driver"` +} + +func (c config) validate() error { + if strings.TrimSpace(c.Name) == "" { + return errors.New("name must not be empty") + } + + if c.PerformerByName != nil { + if err := c.PerformerByName.validate(); err != nil { + return err + } + } + + if c.PerformerByFragment != nil { + if err := c.PerformerByFragment.validate(); err != nil { + return err + } + } + + if c.SceneByFragment != nil { + if err := c.SceneByFragment.validate(); err != nil { + return err + } + } + + for _, s := range c.PerformerByURL { + if err := s.validate(); err != nil { + return err + } + } + + for _, s := range c.SceneByURL { + if err := s.validate(); err != nil { + return err + } + } + + for _, s := range c.MovieByURL { + if err := s.validate(); err != nil { + return err + } + } + + return nil +} + type stashServer struct { URL string `yaml:"url"` } -type scraperAction string - -const ( - scraperActionScript scraperAction = "script" - scraperActionStash scraperAction = "stash" - scraperActionXPath scraperAction = "scrapeXPath" -) - -var allScraperAction = []scraperAction{ - scraperActionScript, - scraperActionStash, - scraperActionXPath, -} - -func (e scraperAction) IsValid() bool { - switch e { - case scraperActionScript, scraperActionStash, scraperActionXPath: - return true - } - return false -} - type scraperTypeConfig struct { Action scraperAction `yaml:"action"` Script []string `yaml:"script,flow"` @@ -44,40 +109,18 @@ type scraperTypeConfig struct { // for xpath name scraper only QueryURL string `yaml:"queryURL"` - - scraperConfig *scraperConfig } -type scrapePerformerNamesFunc func(c scraperTypeConfig, name string) ([]*models.ScrapedPerformer, error) - -type performerByNameConfig struct { - scraperTypeConfig `yaml:",inline"` - performScrape scrapePerformerNamesFunc -} - -func (c *performerByNameConfig) resolveFn() { - if c.Action == scraperActionScript { - c.performScrape = scrapePerformerNamesScript - } else if c.Action == scraperActionStash { - c.performScrape = scrapePerformerNamesStash - } else if c.Action == scraperActionXPath { - c.performScrape = scrapePerformerNamesXPath +func (c scraperTypeConfig) validate() error { + if !c.Action.IsValid() { + return fmt.Errorf("%s is not a valid scraper action", c.Action) } -} -type scrapePerformerFragmentFunc func(c scraperTypeConfig, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) - -type performerByFragmentConfig struct { - scraperTypeConfig `yaml:",inline"` - performScrape scrapePerformerFragmentFunc -} - -func (c *performerByFragmentConfig) resolveFn() { - if c.Action == scraperActionScript { - c.performScrape = scrapePerformerFragmentScript - } else if c.Action == scraperActionStash { - c.performScrape = scrapePerformerFragmentStash + if c.Action == scraperActionScript && len(c.Script) == 0 { + return errors.New("script is mandatory for script scraper action") } + + return nil } type scrapeByURLConfig struct { @@ -85,6 +128,14 @@ type scrapeByURLConfig struct { URL []string `yaml:"url,flow"` } +func (c scrapeByURLConfig) validate() error { + if len(c.URL) == 0 { + return errors.New("url is mandatory for scrape by url scrapers") + } + + return c.scraperTypeConfig.validate() +} + func (c scrapeByURLConfig) matchesURL(url string) bool { for _, thisURL := range c.URL { if strings.Contains(url, thisURL) { @@ -95,71 +146,17 @@ func (c scrapeByURLConfig) matchesURL(url string) bool { return false } -type scrapePerformerByURLFunc func(c scraperTypeConfig, url string) (*models.ScrapedPerformer, error) - -type scrapePerformerByURLConfig struct { - scrapeByURLConfig `yaml:",inline"` - performScrape scrapePerformerByURLFunc -} - -func (c *scrapePerformerByURLConfig) resolveFn() { - if c.Action == scraperActionScript { - c.performScrape = scrapePerformerURLScript - } else if c.Action == scraperActionXPath { - c.performScrape = scrapePerformerURLXpath - } -} - -type scrapeSceneFragmentFunc func(c scraperTypeConfig, scene models.SceneUpdateInput) (*models.ScrapedScene, error) - -type sceneByFragmentConfig struct { - scraperTypeConfig `yaml:",inline"` - performScrape scrapeSceneFragmentFunc -} - -func (c *sceneByFragmentConfig) resolveFn() { - if c.Action == scraperActionScript { - c.performScrape = scrapeSceneFragmentScript - } else if c.Action == scraperActionStash { - c.performScrape = scrapeSceneFragmentStash - } -} - -type scrapeSceneByURLFunc func(c scraperTypeConfig, url string) (*models.ScrapedScene, error) - -type scrapeSceneByURLConfig struct { - scrapeByURLConfig `yaml:",inline"` - performScrape scrapeSceneByURLFunc -} - -func (c *scrapeSceneByURLConfig) resolveFn() { - if c.Action == scraperActionScript { - c.performScrape = scrapeSceneURLScript - } else if c.Action == scraperActionXPath { - c.performScrape = scrapeSceneURLXPath - } -} - type scraperDebugOptions struct { PrintHTML bool `yaml:"printHTML"` } -type scraperConfig struct { - ID string - Name string `yaml:"name"` - PerformerByName *performerByNameConfig `yaml:"performerByName"` - PerformerByFragment *performerByFragmentConfig `yaml:"performerByFragment"` - PerformerByURL []*scrapePerformerByURLConfig `yaml:"performerByURL"` - SceneByFragment *sceneByFragmentConfig `yaml:"sceneByFragment"` - SceneByURL []*scrapeSceneByURLConfig `yaml:"sceneByURL"` - - DebugOptions *scraperDebugOptions `yaml:"debug"` - StashServer *stashServer `yaml:"stashServer"` - XPathScrapers xpathScrapers `yaml:"xPathScrapers"` +type scraperDriverOptions struct { + UseCDP bool `yaml:"useCDP"` + Sleep int `yaml:"sleep"` } -func loadScraperFromYAML(id string, reader io.Reader) (*scraperConfig, error) { - ret := &scraperConfig{} +func loadScraperFromYAML(id string, reader io.Reader) (*config, error) { + ret := &config{} parser := yaml.NewDecoder(reader) parser.SetStrict(true) @@ -170,13 +167,14 @@ func loadScraperFromYAML(id string, reader io.Reader) (*scraperConfig, error) { ret.ID = id - // set the scraper interface - ret.initialiseConfigs() + if err := ret.validate(); err != nil { + return nil, err + } return ret, nil } -func loadScraperFromYAMLFile(path string) (*scraperConfig, error) { +func loadScraperFromYAMLFile(path string) (*config, error) { file, err := os.Open(path) defer file.Close() if err != nil { @@ -187,34 +185,17 @@ func loadScraperFromYAMLFile(path string) (*scraperConfig, error) { id := filepath.Base(path) id = id[:strings.LastIndex(id, ".")] - return loadScraperFromYAML(id, file) + ret, err := loadScraperFromYAML(id, file) + if err != nil { + return nil, err + } + + ret.path = path + + return ret, nil } -func (c *scraperConfig) initialiseConfigs() { - if c.PerformerByName != nil { - c.PerformerByName.resolveFn() - c.PerformerByName.scraperConfig = c - } - if c.PerformerByFragment != nil { - c.PerformerByFragment.resolveFn() - c.PerformerByFragment.scraperConfig = c - } - for _, s := range c.PerformerByURL { - s.resolveFn() - s.scraperConfig = c - } - - if c.SceneByFragment != nil { - c.SceneByFragment.resolveFn() - c.SceneByFragment.scraperConfig = c - } - for _, s := range c.SceneByURL { - s.resolveFn() - s.scraperConfig = c - } -} - -func (c scraperConfig) toScraper() *models.Scraper { +func (c config) toScraper() *models.Scraper { ret := models.Scraper{ ID: c.ID, Name: c.Name, @@ -253,14 +234,26 @@ func (c scraperConfig) toScraper() *models.Scraper { ret.Scene = &scene } + movie := models.ScraperSpec{} + if len(c.MovieByURL) > 0 { + movie.SupportedScrapes = append(movie.SupportedScrapes, models.ScrapeTypeURL) + for _, v := range c.MovieByURL { + movie.Urls = append(movie.Urls, v.URL...) + } + } + + if len(movie.SupportedScrapes) > 0 { + ret.Movie = &movie + } + return &ret } -func (c scraperConfig) supportsPerformers() bool { +func (c config) supportsPerformers() bool { return c.PerformerByName != nil || c.PerformerByFragment != nil || len(c.PerformerByURL) > 0 } -func (c scraperConfig) matchesPerformerURL(url string) bool { +func (c config) matchesPerformerURL(url string) bool { for _, scraper := range c.PerformerByURL { if scraper.matchesURL(url) { return true @@ -270,31 +263,34 @@ func (c scraperConfig) matchesPerformerURL(url string) bool { return false } -func (c scraperConfig) ScrapePerformerNames(name string) ([]*models.ScrapedPerformer, error) { - if c.PerformerByName != nil && c.PerformerByName.performScrape != nil { - return c.PerformerByName.performScrape(c.PerformerByName.scraperTypeConfig, name) +func (c config) ScrapePerformerNames(name string, globalConfig GlobalConfig) ([]*models.ScrapedPerformer, error) { + if c.PerformerByName != nil { + s := getScraper(*c.PerformerByName, c, globalConfig) + return s.scrapePerformersByName(name) } return nil, nil } -func (c scraperConfig) ScrapePerformer(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { - if c.PerformerByFragment != nil && c.PerformerByFragment.performScrape != nil { - return c.PerformerByFragment.performScrape(c.PerformerByFragment.scraperTypeConfig, scrapedPerformer) +func (c config) ScrapePerformer(scrapedPerformer models.ScrapedPerformerInput, globalConfig GlobalConfig) (*models.ScrapedPerformer, error) { + if c.PerformerByFragment != nil { + s := getScraper(*c.PerformerByFragment, c, globalConfig) + return s.scrapePerformerByFragment(scrapedPerformer) } // try to match against URL if present if scrapedPerformer.URL != nil && *scrapedPerformer.URL != "" { - return c.ScrapePerformerURL(*scrapedPerformer.URL) + return c.ScrapePerformerURL(*scrapedPerformer.URL, globalConfig) } return nil, nil } -func (c scraperConfig) ScrapePerformerURL(url string) (*models.ScrapedPerformer, error) { +func (c config) ScrapePerformerURL(url string, globalConfig GlobalConfig) (*models.ScrapedPerformer, error) { for _, scraper := range c.PerformerByURL { - if scraper.matchesURL(url) && scraper.performScrape != nil { - ret, err := scraper.performScrape(scraper.scraperTypeConfig, url) + if scraper.matchesURL(url) { + s := getScraper(scraper.scraperTypeConfig, c, globalConfig) + ret, err := s.scrapePerformerByURL(url) if err != nil { return nil, err } @@ -308,11 +304,11 @@ func (c scraperConfig) ScrapePerformerURL(url string) (*models.ScrapedPerformer, return nil, nil } -func (c scraperConfig) supportsScenes() bool { +func (c config) supportsScenes() bool { return c.SceneByFragment != nil || len(c.SceneByURL) > 0 } -func (c scraperConfig) matchesSceneURL(url string) bool { +func (c config) matchesSceneURL(url string) bool { for _, scraper := range c.SceneByURL { if scraper.matchesURL(url) { return true @@ -322,18 +318,52 @@ func (c scraperConfig) matchesSceneURL(url string) bool { return false } -func (c scraperConfig) ScrapeScene(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { - if c.SceneByFragment != nil && c.SceneByFragment.performScrape != nil { - return c.SceneByFragment.performScrape(c.SceneByFragment.scraperTypeConfig, scene) +func (c config) supportsMovies() bool { + return len(c.MovieByURL) > 0 +} + +func (c config) matchesMovieURL(url string) bool { + for _, scraper := range c.MovieByURL { + if scraper.matchesURL(url) { + return true + } + } + + return false +} + +func (c config) ScrapeScene(scene models.SceneUpdateInput, globalConfig GlobalConfig) (*models.ScrapedScene, error) { + if c.SceneByFragment != nil { + s := getScraper(*c.SceneByFragment, c, globalConfig) + return s.scrapeSceneByFragment(scene) } return nil, nil } -func (c scraperConfig) ScrapeSceneURL(url string) (*models.ScrapedScene, error) { +func (c config) ScrapeSceneURL(url string, globalConfig GlobalConfig) (*models.ScrapedScene, error) { for _, scraper := range c.SceneByURL { - if scraper.matchesURL(url) && scraper.performScrape != nil { - ret, err := scraper.performScrape(scraper.scraperTypeConfig, url) + if scraper.matchesURL(url) { + s := getScraper(scraper.scraperTypeConfig, c, globalConfig) + ret, err := s.scrapeSceneByURL(url) + if err != nil { + return nil, err + } + + if ret != nil { + return ret, nil + } + } + } + + return nil, nil +} + +func (c config) ScrapeMovieURL(url string, globalConfig GlobalConfig) (*models.ScrapedMovie, error) { + for _, scraper := range c.MovieByURL { + if scraper.matchesURL(url) { + s := getScraper(scraper.scraperTypeConfig, c, globalConfig) + ret, err := s.scrapeMovieByURL(url) if err != nil { return nil, err } diff --git a/pkg/scraper/freeones.go b/pkg/scraper/freeones.go index 3bfc8bcc6..8b7277ba2 100644 --- a/pkg/scraper/freeones.go +++ b/pkg/scraper/freeones.go @@ -6,9 +6,10 @@ import ( "github.com/stashapp/stash/pkg/logger" ) -const freeonesScraperID = "builtin_freeones" +// FreeonesScraperID is the scraper ID for the built-in Freeones scraper +const FreeonesScraperID = "builtin_freeones" -// 537: stolen from: https://github.com/stashapp/CommunityScrapers/blob/master/scrapers/NewFreeones.yml +// 537: stolen from: https://github.com/stashapp/CommunityScrapers/blob/master/scrapers/FreeonesCommunity.yml const freeonesScraperConfig = ` name: Freeones performerByName: @@ -41,8 +42,8 @@ xPathScrapers: replace: - regex: ^ with: https://www.freeones.xxx - Twitter: //div[p[text()='Follow On']]//div//a[@class='d-flex align-items-center justify-content-center mr-2 social-icons color-twitter']/@href - Instagram: //div[p[text()='Follow On']]//div//a[@class='d-flex align-items-center justify-content-center mr-2 social-icons color-telegram']/@href + Twitter: //div[p[text()='Follow On']]//div//a[@class='d-flex align-items-center justify-content-center m-2 social-icons color-twitter']/@href + Instagram: //div[p[text()='Follow On']]//div//a[@class='d-flex align-items-center justify-content-center m-2 social-icons color-telegram']/@href Birthdate: selector: //div[p[text()='Personal Information']]//div//p/a/span[contains(text(),'Born On')] replace: @@ -63,14 +64,15 @@ xPathScrapers: - regex: Latin with: "hispanic" Country: //div[p[text()='Personal Information']]//div//p//a[@data-test="link-country"] - EyeColor: //div[p[text()='Eye Color']]//div//p//a//span + EyeColor: //span[@data-test="link_span_eye_color"] Height: - selector: //div[p[text()='Height']]//div//p//a//span + selector: //span[@data-test="link_span_height"] replace: - regex: \D+[\s\S]+ with: "" Measurements: - selector: //div[p[text()='Measurements']]//div[@class='p-3']//p + selector: //span[@data-test="p-measurements"]//a/span + concat: " - " replace: - regex: Unknown with: @@ -90,16 +92,22 @@ xPathScrapers: - regex: -\w+-\w+-\w+-\w+-\w+$ with: "" Aliases: //div[p[text()='Aliases']]//div//p[@class='mb-0 text-center'] - Tattoos: //div[p[text()='Tattoos']]//div//p[@class='mb-0 text-center'] - Piercings: //div[p[text()='Piercings']]//div//p[@class='mb-0 text-center'] + Tattoos: //span[@data-test="p_has_tattoos"]|//span[@cdata-test="p_has_tattoos"] + Piercings: //span[@data-test="p_has_piercings"] Image: - selector: //div[@class='profile-image-large']//a/img/@src + selector: //div[@class='profile-image-container']//a/img/@src + Gender: + selector: //meta[@name="language"]/@name + replace: + - regex: language + with: "Female" +# Last updated June 15, 2020 ` -func GetFreeonesScraper() scraperConfig { +func getFreeonesScraper() config { yml := freeonesScraperConfig - scraper, err := loadScraperFromYAML(freeonesScraperID, strings.NewReader(yml)) + scraper, err := loadScraperFromYAML(FreeonesScraperID, strings.NewReader(yml)) if err != nil { logger.Fatalf("Error loading builtin freeones scraper: %s", err.Error()) } diff --git a/pkg/scraper/image.go b/pkg/scraper/image.go index 4cdd691c1..5ab845444 100644 --- a/pkg/scraper/image.go +++ b/pkg/scraper/image.go @@ -6,7 +6,6 @@ import ( "strings" "time" - "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/utils" ) @@ -15,13 +14,13 @@ import ( // configurable at some point. const imageGetTimeout = time.Second * 30 -func setPerformerImage(p *models.ScrapedPerformer) error { +func setPerformerImage(p *models.ScrapedPerformer, globalConfig GlobalConfig) error { if p == nil || p.Image == nil || !strings.HasPrefix(*p.Image, "http") { // nothing to do return nil } - img, err := getImage(*p.Image) + img, err := getImage(*p.Image, globalConfig) if err != nil { return err } @@ -31,14 +30,14 @@ func setPerformerImage(p *models.ScrapedPerformer) error { return nil } -func setSceneImage(s *models.ScrapedScene) error { +func setSceneImage(s *models.ScrapedScene, globalConfig GlobalConfig) error { // don't try to get the image if it doesn't appear to be a URL if s == nil || s.Image == nil || !strings.HasPrefix(*s.Image, "http") { // nothing to do return nil } - img, err := getImage(*s.Image) + img, err := getImage(*s.Image, globalConfig) if err != nil { return err } @@ -48,7 +47,41 @@ func setSceneImage(s *models.ScrapedScene) error { return nil } -func getImage(url string) (*string, error) { +func setMovieFrontImage(m *models.ScrapedMovie, globalConfig GlobalConfig) error { + // don't try to get the image if it doesn't appear to be a URL + if m == nil || m.FrontImage == nil || !strings.HasPrefix(*m.FrontImage, "http") { + // nothing to do + return nil + } + + img, err := getImage(*m.FrontImage, globalConfig) + if err != nil { + return err + } + + m.FrontImage = img + + return nil +} + +func setMovieBackImage(m *models.ScrapedMovie, globalConfig GlobalConfig) error { + // don't try to get the image if it doesn't appear to be a URL + if m == nil || m.BackImage == nil || !strings.HasPrefix(*m.BackImage, "http") { + // nothing to do + return nil + } + + img, err := getImage(*m.BackImage, globalConfig) + if err != nil { + return err + } + + m.BackImage = img + + return nil +} + +func getImage(url string, globalConfig GlobalConfig) (*string, error) { client := &http.Client{ Timeout: imageGetTimeout, } @@ -58,13 +91,20 @@ func getImage(url string) (*string, error) { return nil, err } - userAgent := config.GetScraperUserAgent() + userAgent := globalConfig.UserAgent if userAgent != "" { req.Header.Set("User-Agent", userAgent) } // assume is a URL for now + + // set the host of the URL as the referer + if req.URL.Scheme != "" { + req.Header.Set("Referer", req.URL.Scheme+"://"+req.Host) + } + resp, err := client.Do(req) + if err != nil { return nil, err } @@ -86,10 +126,10 @@ func getImage(url string) (*string, error) { return &img, nil } -func getStashPerformerImage(stashURL string, performerID string) (*string, error) { - return getImage(stashURL + "/performer/" + performerID + "/image") +func getStashPerformerImage(stashURL string, performerID string, globalConfig GlobalConfig) (*string, error) { + return getImage(stashURL+"/performer/"+performerID+"/image", globalConfig) } -func getStashSceneImage(stashURL string, sceneID string) (*string, error) { - return getImage(stashURL + "/scene/" + sceneID + "/screenshot") +func getStashSceneImage(stashURL string, sceneID string, globalConfig GlobalConfig) (*string, error) { + return getImage(stashURL+"/scene/"+sceneID+"/screenshot", globalConfig) } diff --git a/pkg/scraper/json.go b/pkg/scraper/json.go new file mode 100644 index 000000000..7fb7522a3 --- /dev/null +++ b/pkg/scraper/json.go @@ -0,0 +1,201 @@ +package scraper + +import ( + "errors" + "io/ioutil" + "net/url" + "strings" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "github.com/tidwall/gjson" +) + +type jsonScraper struct { + scraper scraperTypeConfig + config config + globalConfig GlobalConfig +} + +func newJsonScraper(scraper scraperTypeConfig, config config, globalConfig GlobalConfig) *jsonScraper { + return &jsonScraper{ + scraper: scraper, + config: config, + globalConfig: globalConfig, + } +} + +func (s *jsonScraper) getJsonScraper() *mappedScraper { + return s.config.JsonScrapers[s.scraper.Scraper] +} + +func (s *jsonScraper) scrapeURL(url string) (string, *mappedScraper, error) { + scraper := s.getJsonScraper() + + if scraper == nil { + return "", nil, errors.New("json scraper with name " + s.scraper.Scraper + " not found in config") + } + + doc, err := s.loadURL(url) + + if err != nil { + return "", nil, err + } + + return doc, scraper, nil +} + +func (s *jsonScraper) loadURL(url string) (string, error) { + r, err := loadURL(url, s.config, s.globalConfig) + if err != nil { + return "", err + } + + doc, err := ioutil.ReadAll(r) + if err != nil { + return "", err + } + + docStr := string(doc) + if !gjson.Valid(docStr) { + return "", errors.New("not valid json") + } + + if err == nil && s.config.DebugOptions != nil && s.config.DebugOptions.PrintHTML { + logger.Infof("loadURL (%s) response: \n%s", url, docStr) + } + + return docStr, err +} + +func (s *jsonScraper) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) { + doc, scraper, err := s.scrapeURL(url) + if err != nil { + return nil, err + } + + q := s.getJsonQuery(doc) + return scraper.scrapePerformer(q) +} + +func (s *jsonScraper) scrapeSceneByURL(url string) (*models.ScrapedScene, error) { + doc, scraper, err := s.scrapeURL(url) + if err != nil { + return nil, err + } + + q := s.getJsonQuery(doc) + return scraper.scrapeScene(q) +} + +func (s *jsonScraper) scrapeMovieByURL(url string) (*models.ScrapedMovie, error) { + doc, scraper, err := s.scrapeURL(url) + if err != nil { + return nil, err + } + + q := s.getJsonQuery(doc) + return scraper.scrapeMovie(q) +} + +func (s *jsonScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) { + scraper := s.getJsonScraper() + + if scraper == nil { + return nil, errors.New("json scraper with name " + s.scraper.Scraper + " not found in config") + } + + const placeholder = "{}" + + // replace the placeholder string with the URL-escaped name + escapedName := url.QueryEscape(name) + + url := s.scraper.QueryURL + url = strings.Replace(url, placeholder, escapedName, -1) + + doc, err := s.loadURL(url) + + if err != nil { + return nil, err + } + + q := s.getJsonQuery(doc) + return scraper.scrapePerformers(q) +} + +func (s *jsonScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { + return nil, errors.New("scrapePerformerByFragment not supported for json scraper") +} + +func (s *jsonScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { + storedScene, err := sceneFromUpdateFragment(scene) + if err != nil { + return nil, err + } + + if storedScene == nil { + return nil, errors.New("no scene found") + } + + // construct the URL + url := constructSceneURL(s.scraper.QueryURL, storedScene) + + scraper := s.getJsonScraper() + + if scraper == nil { + return nil, errors.New("json scraper with name " + s.scraper.Scraper + " not found in config") + } + + doc, err := s.loadURL(url) + + if err != nil { + return nil, err + } + + q := s.getJsonQuery(doc) + return scraper.scrapeScene(q) +} + +func (s *jsonScraper) getJsonQuery(doc string) *jsonQuery { + return &jsonQuery{ + doc: doc, + scraper: s, + } +} + +type jsonQuery struct { + doc string + scraper *jsonScraper +} + +func (q *jsonQuery) runQuery(selector string) []string { + value := gjson.Get(q.doc, selector) + + if !value.Exists() { + logger.Warnf("Could not find json path '%s' in json object", selector) + return nil + } + + var ret []string + if value.IsArray() { + value.ForEach(func(k, v gjson.Result) bool { + ret = append(ret, v.String()) + return true + }) + } else { + ret = append(ret, value.String()) + } + + return ret +} + +func (q *jsonQuery) subScrape(value string) mappedQuery { + doc, err := q.scraper.loadURL(value) + + if err != nil { + logger.Warnf("Error getting URL '%s' for sub-scraper: %s", value, err.Error()) + return nil + } + + return q.scraper.getJsonQuery(doc) +} diff --git a/pkg/scraper/json_test.go b/pkg/scraper/json_test.go new file mode 100644 index 000000000..6145cc88b --- /dev/null +++ b/pkg/scraper/json_test.go @@ -0,0 +1,93 @@ +package scraper + +import ( + "testing" + + "gopkg.in/yaml.v2" +) + +func TestJsonPerformerScraper(t *testing.T) { + const yamlStr = `name: Test +jsonScrapers: + performerScraper: + common: + $extras: data.extras + performer: + Name: data.name + Gender: $extras.gender + Birthdate: $extras.birthday + Ethnicity: $extras.ethnicity + Height: $extras.height + Measurements: $extras.measurements + Tattoos: $extras.tattoos + Piercings: $extras.piercings + Aliases: data.aliases + Image: data.image +` + + const json = ` +{ + "data": { + "id": "2cd4146b-637d-49b1-8ff9-19d4a06947bb", + "name": "Mia Malkova", + "bio": "Some girls are so damn hot that they can get you bent out of shape, and you will not even be mad at them for doing so. Well, tawny blonde Mia Malkova can bend her body into any shape she pleases, and that’s sure to satisfy all of the horny cocks and wet pussies out there. This girl has acrobatic and contortionist abilities that could even twist a pretzel into a new knot, which can be very helpful in the ... arrow_drop_down Some girls are so damn hot that they can get you bent out of shape, and you will not even be mad at them for doing so. Well, tawny blonde Mia Malkova can bend her body into any shape she pleases, and that’s sure to satisfy all of the horny cocks and wet pussies out there. This girl has acrobatic and contortionist abilities that could even twist a pretzel into a new knot, which can be very helpful in the VR Porn movies – trust us. Ankles behind her neck and feet over her back so she can kiss her toes, turned, twisted and gyrating, she can fuck any which way she wants (and that ass!), will surely make you fall in love with this hot Virtual Reality Porn slut, as she is one of the finest of them all. Talking about perfection, maybe it’s all the acrobatic work that keeps it in such gorgeous shape? Who cares really, because you just want to take a big bite out of it and never let go. But it’s not all about the body. Mia’s also got a great smile, which might not sound kinky, but believe us, it is a smile that will heat up your innards and drop your pants. Is it her golden skin, her innocent pink lips or that heart-shaped face? There is just too much good stuff going on with Mia Malkova, which is maybe why these past few years have heaped awards upon awards on this Southern California native. Mia came to VR Bangers for her first VR Porn video, so you know she’s only going for top-notch scenes with top-game performers, men, and women. Better hit up that yoga studio if you ever dream of being able to bang a flexible and talented chick like lady Malkova. arrow_drop_up", + "extras": { + "gender": "Female", + "birthday": "1992-07-01", + "birthday_timestamp": 709948800, + "birthplace": "Palm Springs, California, United States", + "active": 1, + "astrology": "Cancer (Jun 21 - Jul 22)", + "ethnicity": "Caucasian", + "nationality": "United States", + "hair_colour": "Blonde", + "weight": "126 lbs (or 57 kg)", + "height": "5'6\" (or 167 cm)", + "measurements": "34-26-36", + "cupsize": "34C (75C)", + "tattoos": "None", + "piercings": "Navel", + "first_seen": null + }, + "aliases": [ + "Mia Bliss", + "Madison Clover", + "Madison Swan", + "Mia Mountain", + "Mia M.", + "Mia Malvoka", + "Mia Molkova", + "Mia Thomas" + ], + "image": "https:\/\/thumb.metadataapi.net\/unsafe\/1000x1500\/smart\/filters:sharpen():upscale()\/https%3A%2F%2Fcdn.metadataapi.net%2Fperformer%2F49%2F05%2F30%2Fade2255dc065032a89ebb23f0e038fa%2Fposter%2Fmia-malkova.jpg%3Fid1582610531" + } +} +` + + c := &config{} + err := yaml.Unmarshal([]byte(yamlStr), &c) + + if err != nil { + t.Fatalf("Error loading yaml: %s", err.Error()) + } + + // perform scrape using json string + performerScraper := c.JsonScrapers["performerScraper"] + + q := &jsonQuery{ + doc: json, + } + + scrapedPerformer, err := performerScraper.scrapePerformer(q) + if err != nil { + t.Fatalf("Error scraping performer: %s", err.Error()) + } + + verifyField(t, "Mia Malkova", scrapedPerformer.Name, "Name") + verifyField(t, "Female", scrapedPerformer.Gender, "Gender") + verifyField(t, "1992-07-01", scrapedPerformer.Birthdate, "Birthdate") + verifyField(t, "Caucasian", scrapedPerformer.Ethnicity, "Ethnicity") + verifyField(t, "5'6\" (or 167 cm)", scrapedPerformer.Height, "Height") + verifyField(t, "None", scrapedPerformer.Tattoos, "Tattoos") + verifyField(t, "Navel", scrapedPerformer.Piercings, "Piercings") +} diff --git a/pkg/scraper/mapped.go b/pkg/scraper/mapped.go new file mode 100644 index 000000000..540fcd9a5 --- /dev/null +++ b/pkg/scraper/mapped.go @@ -0,0 +1,718 @@ +package scraper + +import ( + "errors" + "fmt" + "math" + "reflect" + "regexp" + "strconv" + "strings" + "time" + + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "gopkg.in/yaml.v2" +) + +type mappedQuery interface { + runQuery(selector string) []string + subScrape(value string) mappedQuery +} + +type commonMappedConfig map[string]string + +type mappedConfig map[string]mappedScraperAttrConfig + +func (s mappedConfig) applyCommon(c commonMappedConfig, src string) string { + if c == nil { + return src + } + + ret := src + for commonKey, commonVal := range c { + if strings.Contains(ret, commonKey) { + ret = strings.Replace(ret, commonKey, commonVal, -1) + } + } + + return ret +} + +func (s mappedConfig) process(q mappedQuery, common commonMappedConfig) mappedResults { + var ret mappedResults + + for k, attrConfig := range s { + + if attrConfig.Fixed != "" { + // TODO - not sure if this needs to set _all_ indexes for the key + const i = 0 + ret = ret.setKey(i, k, attrConfig.Fixed) + } else { + selector := attrConfig.Selector + selector = s.applyCommon(common, selector) + + found := q.runQuery(selector) + + if len(found) > 0 { + result := s.postProcess(q, attrConfig, found) + for i, text := range result { + ret = ret.setKey(i, k, text) + } + } + } + } + + return ret +} + +func (s mappedConfig) postProcess(q mappedQuery, attrConfig mappedScraperAttrConfig, found []string) []string { + // check if we're concatenating the results into a single result + var ret []string + if attrConfig.hasConcat() { + result := attrConfig.concatenateResults(found) + result = attrConfig.postProcess(result, q) + if attrConfig.hasSplit() { + return attrConfig.splitString(result) + } + + ret = []string{result} + } else { + for _, text := range found { + text = attrConfig.postProcess(text, q) + if attrConfig.hasSplit() { + return attrConfig.splitString(text) + } + + ret = append(ret, text) + } + } + + return ret +} + +type mappedSceneScraperConfig struct { + mappedConfig + + Tags mappedConfig `yaml:"Tags"` + Performers mappedConfig `yaml:"Performers"` + Studio mappedConfig `yaml:"Studio"` + Movies mappedConfig `yaml:"Movies"` +} +type _mappedSceneScraperConfig mappedSceneScraperConfig + +const ( + mappedScraperConfigSceneTags = "Tags" + mappedScraperConfigScenePerformers = "Performers" + mappedScraperConfigSceneStudio = "Studio" + mappedScraperConfigSceneMovies = "Movies" +) + +func (s *mappedSceneScraperConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + // HACK - unmarshal to map first, then remove known scene sub-fields, then + // remarshal to yaml and pass that down to the base map + parentMap := make(map[string]interface{}) + if err := unmarshal(parentMap); err != nil { + return err + } + + // move the known sub-fields to a separate map + thisMap := make(map[string]interface{}) + + thisMap[mappedScraperConfigSceneTags] = parentMap[mappedScraperConfigSceneTags] + thisMap[mappedScraperConfigScenePerformers] = parentMap[mappedScraperConfigScenePerformers] + thisMap[mappedScraperConfigSceneStudio] = parentMap[mappedScraperConfigSceneStudio] + thisMap[mappedScraperConfigSceneMovies] = parentMap[mappedScraperConfigSceneMovies] + + delete(parentMap, mappedScraperConfigSceneTags) + delete(parentMap, mappedScraperConfigScenePerformers) + delete(parentMap, mappedScraperConfigSceneStudio) + delete(parentMap, mappedScraperConfigSceneMovies) + + // re-unmarshal the sub-fields + yml, err := yaml.Marshal(thisMap) + if err != nil { + return err + } + + // needs to be a different type to prevent infinite recursion + c := _mappedSceneScraperConfig{} + if err := yaml.Unmarshal(yml, &c); err != nil { + return err + } + + *s = mappedSceneScraperConfig(c) + + yml, err = yaml.Marshal(parentMap) + if err != nil { + return err + } + + if err := yaml.Unmarshal(yml, &s.mappedConfig); err != nil { + return err + } + + return nil +} + +type mappedPerformerScraperConfig struct { + mappedConfig +} + +func (s *mappedPerformerScraperConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + return unmarshal(&s.mappedConfig) +} + +type mappedMovieScraperConfig struct { + mappedConfig + + Studio mappedConfig `yaml:"Studio"` +} +type _mappedMovieScraperConfig mappedMovieScraperConfig + +const ( + mappedScraperConfigMovieStudio = "Studio" +) + +func (s *mappedMovieScraperConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + // HACK - unmarshal to map first, then remove known movie sub-fields, then + // remarshal to yaml and pass that down to the base map + parentMap := make(map[string]interface{}) + if err := unmarshal(parentMap); err != nil { + return err + } + + // move the known sub-fields to a separate map + thisMap := make(map[string]interface{}) + + thisMap[mappedScraperConfigMovieStudio] = parentMap[mappedScraperConfigMovieStudio] + + delete(parentMap, mappedScraperConfigMovieStudio) + + // re-unmarshal the sub-fields + yml, err := yaml.Marshal(thisMap) + if err != nil { + return err + } + + // needs to be a different type to prevent infinite recursion + c := _mappedMovieScraperConfig{} + if err := yaml.Unmarshal(yml, &c); err != nil { + return err + } + + *s = mappedMovieScraperConfig(c) + + yml, err = yaml.Marshal(parentMap) + if err != nil { + return err + } + + if err := yaml.Unmarshal(yml, &s.mappedConfig); err != nil { + return err + } + + return nil +} + +type mappedRegexConfig struct { + Regex string `yaml:"regex"` + With string `yaml:"with"` +} + +type mappedRegexConfigs []mappedRegexConfig + +func (c mappedRegexConfig) apply(value string) string { + if c.Regex != "" { + re, err := regexp.Compile(c.Regex) + if err != nil { + logger.Warnf("Error compiling regex '%s': %s", c.Regex, err.Error()) + return value + } + + ret := re.ReplaceAllString(value, c.With) + + // trim leading and trailing whitespace + // this is done to maintain backwards compatibility with existing + // scrapers + ret = strings.TrimSpace(ret) + + logger.Debugf(`Replace: '%s' with '%s'`, c.Regex, c.With) + logger.Debugf("Before: %s", value) + logger.Debugf("After: %s", ret) + return ret + } + + return value +} + +func (c mappedRegexConfigs) apply(value string) string { + // apply regex in order + for _, config := range c { + value = config.apply(value) + } + + return value +} + +type postProcessAction interface { + Apply(value string, q mappedQuery) string +} + +type postProcessParseDate string + +func (p *postProcessParseDate) Apply(value string, q mappedQuery) string { + parseDate := string(*p) + + if parseDate == "" { + return value + } + + // try to parse the date using the pattern + // if it fails, then just fall back to the original value + parsedValue, err := time.Parse(parseDate, value) + if err != nil { + logger.Warnf("Error parsing date string '%s' using format '%s': %s", value, parseDate, err.Error()) + return value + } + + // convert it into our date format + const internalDateFormat = "2006-01-02" + return parsedValue.Format(internalDateFormat) +} + +type postProcessReplace mappedRegexConfigs + +func (c *postProcessReplace) Apply(value string, q mappedQuery) string { + replace := mappedRegexConfigs(*c) + return replace.apply(value) +} + +type postProcessSubScraper mappedScraperAttrConfig + +func (p *postProcessSubScraper) Apply(value string, q mappedQuery) string { + subScrapeConfig := mappedScraperAttrConfig(*p) + + logger.Debugf("Sub-scraping for: %s", value) + ss := q.subScrape(value) + + if ss != nil { + found := ss.runQuery(subScrapeConfig.Selector) + + if len(found) > 0 { + // check if we're concatenating the results into a single result + var result string + if subScrapeConfig.hasConcat() { + result = subScrapeConfig.concatenateResults(found) + } else { + result = found[0] + } + + result = subScrapeConfig.postProcess(result, ss) + return result + } + } + + return "" +} + +type postProcessMap map[string]string + +func (p *postProcessMap) Apply(value string, q mappedQuery) string { + // return the mapped value if present + m := *p + mapped, ok := m[value] + + if ok { + return mapped + } + + return value +} + +type postProcessFeetToCm bool + +func (p *postProcessFeetToCm) Apply(value string, q mappedQuery) string { + const foot_in_cm = 30.48 + const inch_in_cm = 2.54 + + reg := regexp.MustCompile("[0-9]+") + filtered := reg.FindAllString(value, -1) + + var feet float64 + var inches float64 + if len(filtered) > 0 { + feet, _ = strconv.ParseFloat(filtered[0], 64) + } + if len(filtered) > 1 { + inches, _ = strconv.ParseFloat(filtered[1], 64) + } + + var centimeters = feet*foot_in_cm + inches*inch_in_cm + + // Return rounded integer string + return strconv.Itoa(int(math.Round(centimeters))) +} + +type mappedPostProcessAction struct { + ParseDate string `yaml:"parseDate"` + Replace mappedRegexConfigs `yaml:"replace"` + SubScraper *mappedScraperAttrConfig `yaml:"subScraper"` + Map map[string]string `yaml:"map"` + FeetToCm bool `yaml:"feetToCm"` +} + +func (a mappedPostProcessAction) ToPostProcessAction() (postProcessAction, error) { + var found string + var ret postProcessAction + + if a.ParseDate != "" { + found = "parseDate" + action := postProcessParseDate(a.ParseDate) + ret = &action + } + if len(a.Replace) > 0 { + if found != "" { + return nil, fmt.Errorf("post-process actions must have a single field, found %s and %s", found, "replace") + } + found = "replace" + action := postProcessReplace(a.Replace) + ret = &action + } + if a.SubScraper != nil { + if found != "" { + return nil, fmt.Errorf("post-process actions must have a single field, found %s and %s", found, "subScraper") + } + found = "subScraper" + action := postProcessSubScraper(*a.SubScraper) + ret = &action + } + if a.Map != nil { + if found != "" { + return nil, fmt.Errorf("post-process actions must have a single field, found %s and %s", found, "map") + } + found = "map" + action := postProcessMap(a.Map) + ret = &action + } + if a.FeetToCm { + if found != "" { + return nil, fmt.Errorf("post-process actions must have a single field, found %s and %s", found, "feetToCm") + } + found = "feetToCm" + action := postProcessFeetToCm(a.FeetToCm) + ret = &action + } + + if ret == nil { + return nil, errors.New("invalid post-process action") + } + + return ret, nil +} + +type mappedScraperAttrConfig struct { + Selector string `yaml:"selector"` + Fixed string `yaml:"fixed"` + PostProcess []mappedPostProcessAction `yaml:"postProcess"` + Concat string `yaml:"concat"` + Split string `yaml:"split"` + + postProcessActions []postProcessAction + + // deprecated: use PostProcess instead + ParseDate string `yaml:"parseDate"` + Replace mappedRegexConfigs `yaml:"replace"` + SubScraper *mappedScraperAttrConfig `yaml:"subScraper"` +} + +type _mappedScraperAttrConfig mappedScraperAttrConfig + +func (c *mappedScraperAttrConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + // try unmarshalling into a string first + if err := unmarshal(&c.Selector); err != nil { + // if it's a type error then we try to unmarshall to the full object + if _, ok := err.(*yaml.TypeError); !ok { + return err + } + + // unmarshall to full object + // need it as a separate object + t := _mappedScraperAttrConfig{} + if err = unmarshal(&t); err != nil { + return err + } + + *c = mappedScraperAttrConfig(t) + } + + return c.convertPostProcessActions() +} + +func (c *mappedScraperAttrConfig) convertPostProcessActions() error { + // ensure we don't have the old deprecated fields and the new post process field + if len(c.PostProcess) > 0 { + if c.ParseDate != "" || len(c.Replace) > 0 || c.SubScraper != nil { + return errors.New("cannot include postProcess and (parseDate, replace, subScraper) deprecated fields") + } + + // convert xpathPostProcessAction actions to postProcessActions + for _, a := range c.PostProcess { + action, err := a.ToPostProcessAction() + if err != nil { + return err + } + c.postProcessActions = append(c.postProcessActions, action) + } + + c.PostProcess = nil + } else { + // convert old deprecated fields if present + // in same order as they used to be executed + if len(c.Replace) > 0 { + action := postProcessReplace(c.Replace) + c.postProcessActions = append(c.postProcessActions, &action) + c.Replace = nil + } + + if c.SubScraper != nil { + action := postProcessSubScraper(*c.SubScraper) + c.postProcessActions = append(c.postProcessActions, &action) + c.SubScraper = nil + } + + if c.ParseDate != "" { + action := postProcessParseDate(c.ParseDate) + c.postProcessActions = append(c.postProcessActions, &action) + c.ParseDate = "" + } + } + + return nil +} + +func (c mappedScraperAttrConfig) hasConcat() bool { + return c.Concat != "" +} + +func (c mappedScraperAttrConfig) hasSplit() bool { + return c.Split != "" +} + +func (c mappedScraperAttrConfig) concatenateResults(nodes []string) string { + separator := c.Concat + result := []string{} + + for _, text := range nodes { + result = append(result, text) + } + + return strings.Join(result, separator) +} + +func (c mappedScraperAttrConfig) splitString(value string) []string { + separator := c.Split + var res []string + + if separator == "" { + return []string{value} + } + + for _, str := range strings.Split(value, separator) { + if str != "" { + res = append(res, str) + } + } + + return res +} + +func (c mappedScraperAttrConfig) postProcess(value string, q mappedQuery) string { + for _, action := range c.postProcessActions { + value = action.Apply(value, q) + } + + return value +} + +type mappedScrapers map[string]*mappedScraper + +type mappedScraper struct { + Common commonMappedConfig `yaml:"common"` + Scene *mappedSceneScraperConfig `yaml:"scene"` + Performer *mappedPerformerScraperConfig `yaml:"performer"` + Movie *mappedMovieScraperConfig `yaml:"movie"` +} + +type mappedResult map[string]string +type mappedResults []mappedResult + +func (r mappedResult) apply(dest interface{}) { + destVal := reflect.ValueOf(dest) + + // dest should be a pointer + destVal = destVal.Elem() + + for key, value := range r { + field := destVal.FieldByName(key) + + if field.IsValid() { + var reflectValue reflect.Value + if field.Kind() == reflect.Ptr { + // need to copy the value, otherwise everything is set to the + // same pointer + localValue := value + reflectValue = reflect.ValueOf(&localValue) + } else { + reflectValue = reflect.ValueOf(value) + } + + field.Set(reflectValue) + } else { + logger.Errorf("Field %s does not exist in %T", key, dest) + } + } +} + +func (r mappedResults) setKey(index int, key string, value string) mappedResults { + if index >= len(r) { + r = append(r, make(mappedResult)) + } + + logger.Debugf(`[%d][%s] = %s`, index, key, value) + r[index][key] = value + return r +} + +func (s mappedScraper) scrapePerformer(q mappedQuery) (*models.ScrapedPerformer, error) { + var ret models.ScrapedPerformer + + performerMap := s.Performer + if performerMap == nil { + return nil, nil + } + + results := performerMap.process(q, s.Common) + if len(results) > 0 { + results[0].apply(&ret) + } + + return &ret, nil +} + +func (s mappedScraper) scrapePerformers(q mappedQuery) ([]*models.ScrapedPerformer, error) { + var ret []*models.ScrapedPerformer + + performerMap := s.Performer + if performerMap == nil { + return nil, nil + } + + results := performerMap.process(q, s.Common) + for _, r := range results { + var p models.ScrapedPerformer + r.apply(&p) + ret = append(ret, &p) + } + + return ret, nil +} + +func (s mappedScraper) scrapeScene(q mappedQuery) (*models.ScrapedScene, error) { + var ret models.ScrapedScene + + sceneScraperConfig := s.Scene + sceneMap := sceneScraperConfig.mappedConfig + if sceneMap == nil { + return nil, nil + } + + scenePerformersMap := sceneScraperConfig.Performers + sceneTagsMap := sceneScraperConfig.Tags + sceneStudioMap := sceneScraperConfig.Studio + sceneMoviesMap := sceneScraperConfig.Movies + + logger.Debug(`Processing scene:`) + results := sceneMap.process(q, s.Common) + if len(results) > 0 { + results[0].apply(&ret) + + // now apply the performers and tags + if scenePerformersMap != nil { + logger.Debug(`Processing scene performers:`) + performerResults := scenePerformersMap.process(q, s.Common) + + for _, p := range performerResults { + performer := &models.ScrapedScenePerformer{} + p.apply(performer) + ret.Performers = append(ret.Performers, performer) + } + } + + if sceneTagsMap != nil { + logger.Debug(`Processing scene tags:`) + tagResults := sceneTagsMap.process(q, s.Common) + + for _, p := range tagResults { + tag := &models.ScrapedSceneTag{} + p.apply(tag) + ret.Tags = append(ret.Tags, tag) + } + } + + if sceneStudioMap != nil { + logger.Debug(`Processing scene studio:`) + studioResults := sceneStudioMap.process(q, s.Common) + + if len(studioResults) > 0 { + studio := &models.ScrapedSceneStudio{} + studioResults[0].apply(studio) + ret.Studio = studio + } + } + + if sceneMoviesMap != nil { + logger.Debug(`Processing scene movies:`) + movieResults := sceneMoviesMap.process(q, s.Common) + + for _, p := range movieResults { + movie := &models.ScrapedSceneMovie{} + p.apply(movie) + ret.Movies = append(ret.Movies, movie) + } + + } + } + + return &ret, nil +} + +func (s mappedScraper) scrapeMovie(q mappedQuery) (*models.ScrapedMovie, error) { + var ret models.ScrapedMovie + + movieScraperConfig := s.Movie + movieMap := movieScraperConfig.mappedConfig + if movieMap == nil { + return nil, nil + } + + movieStudioMap := movieScraperConfig.Studio + + results := movieMap.process(q, s.Common) + if len(results) > 0 { + results[0].apply(&ret) + + if movieStudioMap != nil { + logger.Debug(`Processing movie studio:`) + studioResults := movieStudioMap.process(q, s.Common) + + if len(studioResults) > 0 { + studio := &models.ScrapedMovieStudio{} + studioResults[0].apply(studio) + ret.Studio = studio + } + } + } + + return &ret, nil +} diff --git a/pkg/scraper/mapped_test.go b/pkg/scraper/mapped_test.go new file mode 100644 index 000000000..193847ca4 --- /dev/null +++ b/pkg/scraper/mapped_test.go @@ -0,0 +1,60 @@ +package scraper + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" +) + +func TestInvalidPostProcessAction(t *testing.T) { + yamlStr := `name: Test +performerByURL: + - action: scrapeXPath + scraper: performerScraper +xPathScrapers: + performerScraper: + performer: + Name: + selector: //div/a/@href + postProcess: + - parseDate: Jan 2, 2006 + - anything +` + + c := &config{} + err := yaml.Unmarshal([]byte(yamlStr), &c) + + if err == nil { + t.Error("expected error unmarshalling with invalid post-process action") + return + } +} + +type feetToCMTest struct { + in string + out string +} + +var feetToCMTests = []feetToCMTest{ + {"", "0"}, + {"a", "0"}, + {"6", "183"}, + {"6 feet", "183"}, + {"6ft0", "183"}, + {"6ft2", "188"}, + {"6'2\"", "188"}, + {"6.2", "188"}, + {"6ft2.99", "188"}, + {"text6other2", "188"}, +} + +func TestFeetToCM(t *testing.T) { + pp := postProcessFeetToCm(true) + + q := &xpathQuery{} + + for _, test := range feetToCMTests { + assert.Equal(t, test.out, pp.Apply(test.in, q)) + } +} diff --git a/pkg/scraper/scrapers.go b/pkg/scraper/scrapers.go index c352dfd20..64788bccc 100644 --- a/pkg/scraper/scrapers.go +++ b/pkg/scraper/scrapers.go @@ -5,21 +5,56 @@ import ( "os" "path/filepath" "strconv" + "strings" "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" ) -var scrapers []scraperConfig +// GlobalConfig contains the global scraper options. +type GlobalConfig struct { + // User Agent used when scraping using http. + UserAgent string -func loadScrapers() ([]scraperConfig, error) { - if scrapers != nil { - return scrapers, nil + // Path (file or remote address) to a Chrome CDP instance. + CDPPath string + Path string +} + +func (c GlobalConfig) isCDPPathHTTP() bool { + return strings.HasPrefix(c.CDPPath, "http://") || strings.HasPrefix(c.CDPPath, "https://") +} + +func (c GlobalConfig) isCDPPathWS() bool { + return strings.HasPrefix(c.CDPPath, "ws://") +} + +// Cache stores scraper details. +type Cache struct { + scrapers []config + globalConfig GlobalConfig +} + +// NewCache returns a new Cache loading scraper configurations from the +// scraper path provided in the global config object. It returns a new +// instance and an error if the scraper directory could not be loaded. +// +// Scraper configurations are loaded from yml files in the provided scrapers +// directory and any subdirectories. +func NewCache(globalConfig GlobalConfig) (*Cache, error) { + scrapers, err := loadScrapers(globalConfig.Path) + if err != nil { + return nil, err } - path := config.GetScrapersPath() - scrapers = make([]scraperConfig, 0) + return &Cache{ + globalConfig: globalConfig, + scrapers: scrapers, + }, nil +} + +func loadScrapers(path string) ([]config, error) { + scrapers := make([]config, 0) logger.Debugf("Reading scraper configs from %s", path) scraperFiles := []string{} @@ -36,7 +71,7 @@ func loadScrapers() ([]scraperConfig, error) { } // add built-in freeones scraper - scrapers = append(scrapers, GetFreeonesScraper()) + scrapers = append(scrapers, getFreeonesScraper()) for _, file := range scraperFiles { scraper, err := loadScraperFromYAMLFile(file) @@ -50,49 +85,69 @@ func loadScrapers() ([]scraperConfig, error) { return scrapers, nil } -func ListPerformerScrapers() ([]*models.Scraper, error) { - // read scraper config files from the directory and cache - scrapers, err := loadScrapers() - +// ReloadScrapers clears the scraper cache and reloads from the scraper path. +// In the event of an error during loading, the cache will be left empty. +func (c *Cache) ReloadScrapers() error { + c.scrapers = nil + scrapers, err := loadScrapers(c.globalConfig.Path) if err != nil { - return nil, err + return err } + c.scrapers = scrapers + return nil +} + +// UpdateConfig updates the global config for the cache. If the scraper path +// has changed, ReloadScrapers will need to be called separately. +func (c *Cache) UpdateConfig(globalConfig GlobalConfig) { + c.globalConfig = globalConfig +} + +// ListPerformerScrapers returns a list of scrapers that are capable of +// scraping performers. +func (c Cache) ListPerformerScrapers() []*models.Scraper { var ret []*models.Scraper - for _, s := range scrapers { + for _, s := range c.scrapers { // filter on type if s.supportsPerformers() { ret = append(ret, s.toScraper()) } } - return ret, nil + return ret } -func ListSceneScrapers() ([]*models.Scraper, error) { - // read scraper config files from the directory and cache - scrapers, err := loadScrapers() - - if err != nil { - return nil, err - } - +// ListSceneScrapers returns a list of scrapers that are capable of +// scraping scenes. +func (c Cache) ListSceneScrapers() []*models.Scraper { var ret []*models.Scraper - for _, s := range scrapers { + for _, s := range c.scrapers { // filter on type if s.supportsScenes() { ret = append(ret, s.toScraper()) } } - return ret, nil + return ret } -func findScraper(scraperID string) *scraperConfig { - // read scraper config files from the directory and cache - loadScrapers() +// ListMovieScrapers returns a list of scrapers that are capable of +// scraping scenes. +func (c Cache) ListMovieScrapers() []*models.Scraper { + var ret []*models.Scraper + for _, s := range c.scrapers { + // filter on type + if s.supportsMovies() { + ret = append(ret, s.toScraper()) + } + } - for _, s := range scrapers { + return ret +} + +func (c Cache) findScraper(scraperID string) *config { + for _, s := range c.scrapers { if s.ID == scraperID { return &s } @@ -101,27 +156,32 @@ func findScraper(scraperID string) *scraperConfig { return nil } -func ScrapePerformerList(scraperID string, query string) ([]*models.ScrapedPerformer, error) { +// ScrapePerformerList uses the scraper with the provided ID to query for +// performers using the provided query string. It returns a list of +// scraped performer data. +func (c Cache) ScrapePerformerList(scraperID string, query string) ([]*models.ScrapedPerformer, error) { // find scraper with the provided id - s := findScraper(scraperID) + s := c.findScraper(scraperID) if s != nil { - return s.ScrapePerformerNames(query) + return s.ScrapePerformerNames(query, c.globalConfig) } return nil, errors.New("Scraper with ID " + scraperID + " not found") } -func ScrapePerformer(scraperID string, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { +// ScrapePerformer uses the scraper with the provided ID to scrape a +// performer using the provided performer fragment. +func (c Cache) ScrapePerformer(scraperID string, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { // find scraper with the provided id - s := findScraper(scraperID) + s := c.findScraper(scraperID) if s != nil { - ret, err := s.ScrapePerformer(scrapedPerformer) + ret, err := s.ScrapePerformer(scrapedPerformer, c.globalConfig) if err != nil { return nil, err } // post-process - set the image if applicable - if err := setPerformerImage(ret); err != nil { + if err := setPerformerImage(ret, c.globalConfig); err != nil { logger.Warnf("Could not set image using URL %s: %s", *ret.Image, err.Error()) } @@ -131,16 +191,19 @@ func ScrapePerformer(scraperID string, scrapedPerformer models.ScrapedPerformerI return nil, errors.New("Scraper with ID " + scraperID + " not found") } -func ScrapePerformerURL(url string) (*models.ScrapedPerformer, error) { - for _, s := range scrapers { +// ScrapePerformerURL uses the first scraper it finds that matches the URL +// provided to scrape a performer. If no scrapers are found that matches +// the URL, then nil is returned. +func (c Cache) ScrapePerformerURL(url string) (*models.ScrapedPerformer, error) { + for _, s := range c.scrapers { if s.matchesPerformerURL(url) { - ret, err := s.ScrapePerformerURL(url) + ret, err := s.ScrapePerformerURL(url, c.globalConfig) if err != nil { return nil, err } // post-process - set the image if applicable - if err := setPerformerImage(ret); err != nil { + if err := setPerformerImage(ret, c.globalConfig); err != nil { logger.Warnf("Could not set image using URL %s: %s", *ret.Image, err.Error()) } @@ -188,6 +251,7 @@ func matchStudio(s *models.ScrapedSceneStudio) error { s.ID = &id return nil } + func matchMovie(m *models.ScrapedSceneMovie) error { qb := models.NewMovieQueryBuilder() @@ -226,7 +290,7 @@ func matchTag(s *models.ScrapedSceneTag) error { return nil } -func postScrapeScene(ret *models.ScrapedScene) error { +func (c Cache) postScrapeScene(ret *models.ScrapedScene) error { for _, p := range ret.Performers { err := matchPerformer(p) if err != nil { @@ -256,25 +320,26 @@ func postScrapeScene(ret *models.ScrapedScene) error { } // post-process - set the image if applicable - if err := setSceneImage(ret); err != nil { + if err := setSceneImage(ret, c.globalConfig); err != nil { logger.Warnf("Could not set image using URL %s: %s", *ret.Image, err.Error()) } return nil } -func ScrapeScene(scraperID string, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +// ScrapeScene uses the scraper with the provided ID to scrape a scene. +func (c Cache) ScrapeScene(scraperID string, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { // find scraper with the provided id - s := findScraper(scraperID) + s := c.findScraper(scraperID) if s != nil { - ret, err := s.ScrapeScene(scene) + ret, err := s.ScrapeScene(scene, c.globalConfig) if err != nil { return nil, err } if ret != nil { - err = postScrapeScene(ret) + err = c.postScrapeScene(ret) if err != nil { return nil, err } @@ -286,16 +351,19 @@ func ScrapeScene(scraperID string, scene models.SceneUpdateInput) (*models.Scrap return nil, errors.New("Scraper with ID " + scraperID + " not found") } -func ScrapeSceneURL(url string) (*models.ScrapedScene, error) { - for _, s := range scrapers { +// ScrapeSceneURL uses the first scraper it finds that matches the URL +// provided to scrape a scene. If no scrapers are found that matches +// the URL, then nil is returned. +func (c Cache) ScrapeSceneURL(url string) (*models.ScrapedScene, error) { + for _, s := range c.scrapers { if s.matchesSceneURL(url) { - ret, err := s.ScrapeSceneURL(url) + ret, err := s.ScrapeSceneURL(url, c.globalConfig) if err != nil { return nil, err } - err = postScrapeScene(ret) + err = c.postScrapeScene(ret) if err != nil { return nil, err } @@ -306,3 +374,55 @@ func ScrapeSceneURL(url string) (*models.ScrapedScene, error) { return nil, nil } + +func matchMovieStudio(s *models.ScrapedMovieStudio) error { + qb := models.NewStudioQueryBuilder() + + studio, err := qb.FindByName(s.Name, nil, true) + + if err != nil { + return err + } + + if studio == nil { + // ignore - cannot match + return nil + } + + id := strconv.Itoa(studio.ID) + s.ID = &id + return nil +} + +// ScrapeMovieURL uses the first scraper it finds that matches the URL +// provided to scrape a movie. If no scrapers are found that matches +// the URL, then nil is returned. +func (c Cache) ScrapeMovieURL(url string) (*models.ScrapedMovie, error) { + for _, s := range c.scrapers { + if s.matchesMovieURL(url) { + ret, err := s.ScrapeMovieURL(url, c.globalConfig) + if err != nil { + return nil, err + } + + if ret.Studio != nil { + err := matchMovieStudio(ret.Studio) + if err != nil { + return nil, err + } + } + + // post-process - set the image if applicable + if err := setMovieFrontImage(ret, c.globalConfig); err != nil { + logger.Warnf("Could not set front image using URL %s: %s", *ret.FrontImage, err.Error()) + } + if err := setMovieBackImage(ret, c.globalConfig); err != nil { + logger.Warnf("Could not set back image using URL %s: %s", *ret.BackImage, err.Error()) + } + + return ret, nil + } + } + + return nil, nil +} diff --git a/pkg/scraper/script.go b/pkg/scraper/script.go index 7dd6c93e7..91a754f7c 100644 --- a/pkg/scraper/script.go +++ b/pkg/scraper/script.go @@ -6,16 +6,32 @@ import ( "io" "io/ioutil" "os/exec" + "path/filepath" "strings" "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" ) -func runScraperScript(command []string, inString string, out interface{}) error { +type scriptScraper struct { + scraper scraperTypeConfig + config config + globalConfig GlobalConfig +} + +func newScriptScraper(scraper scraperTypeConfig, config config, globalConfig GlobalConfig) *scriptScraper { + return &scriptScraper{ + scraper: scraper, + config: config, + globalConfig: globalConfig, + } +} + +func (s *scriptScraper) runScraperScript(inString string, out interface{}) error { + command := s.scraper.Script + cmd := exec.Command(command[0], command[1:]...) - cmd.Dir = config.GetScrapersPath() + cmd.Dir = filepath.Dir(s.config.path) stdin, err := cmd.StdinPipe() if err != nil { @@ -65,12 +81,12 @@ func runScraperScript(command []string, inString string, out interface{}) error return nil } -func scrapePerformerNamesScript(c scraperTypeConfig, name string) ([]*models.ScrapedPerformer, error) { +func (s *scriptScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) { inString := `{"name": "` + name + `"}` var performers []models.ScrapedPerformer - err := runScraperScript(c.Script, inString, &performers) + err := s.runScraperScript(inString, &performers) // convert to pointers var ret []*models.ScrapedPerformer @@ -83,7 +99,7 @@ func scrapePerformerNamesScript(c scraperTypeConfig, name string) ([]*models.Scr return ret, err } -func scrapePerformerFragmentScript(c scraperTypeConfig, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { +func (s *scriptScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { inString, err := json.Marshal(scrapedPerformer) if err != nil { @@ -92,22 +108,22 @@ func scrapePerformerFragmentScript(c scraperTypeConfig, scrapedPerformer models. var ret models.ScrapedPerformer - err = runScraperScript(c.Script, string(inString), &ret) + err = s.runScraperScript(string(inString), &ret) return &ret, err } -func scrapePerformerURLScript(c scraperTypeConfig, url string) (*models.ScrapedPerformer, error) { +func (s *scriptScraper) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) { inString := `{"url": "` + url + `"}` var ret models.ScrapedPerformer - err := runScraperScript(c.Script, string(inString), &ret) + err := s.runScraperScript(string(inString), &ret) return &ret, err } -func scrapeSceneFragmentScript(c scraperTypeConfig, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +func (s *scriptScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { inString, err := json.Marshal(scene) if err != nil { @@ -116,17 +132,27 @@ func scrapeSceneFragmentScript(c scraperTypeConfig, scene models.SceneUpdateInpu var ret models.ScrapedScene - err = runScraperScript(c.Script, string(inString), &ret) + err = s.runScraperScript(string(inString), &ret) return &ret, err } -func scrapeSceneURLScript(c scraperTypeConfig, url string) (*models.ScrapedScene, error) { +func (s *scriptScraper) scrapeSceneByURL(url string) (*models.ScrapedScene, error) { inString := `{"url": "` + url + `"}` var ret models.ScrapedScene - err := runScraperScript(c.Script, string(inString), &ret) + err := s.runScraperScript(string(inString), &ret) + + return &ret, err +} + +func (s *scriptScraper) scrapeMovieByURL(url string) (*models.ScrapedMovie, error) { + inString := `{"url": "` + url + `"}` + + var ret models.ScrapedMovie + + err := s.runScraperScript(string(inString), &ret) return &ret, err } diff --git a/pkg/scraper/stash.go b/pkg/scraper/stash.go index 92aed73d1..d14122760 100644 --- a/pkg/scraper/stash.go +++ b/pkg/scraper/stash.go @@ -2,6 +2,7 @@ package scraper import ( "context" + "errors" "strconv" "github.com/jinzhu/copier" @@ -10,8 +11,22 @@ import ( "github.com/stashapp/stash/pkg/models" ) -func getStashClient(c scraperTypeConfig) *graphql.Client { - url := c.scraperConfig.StashServer.URL +type stashScraper struct { + scraper scraperTypeConfig + config config + globalConfig GlobalConfig +} + +func newStashScraper(scraper scraperTypeConfig, config config, globalConfig GlobalConfig) *stashScraper { + return &stashScraper{ + scraper: scraper, + config: config, + globalConfig: globalConfig, + } +} + +func (s *stashScraper) getStashClient() *graphql.Client { + url := s.config.StashServer.URL return graphql.NewClient(url+"/graphql", nil) } @@ -33,8 +48,8 @@ type stashFindPerformerNamesResultType struct { Performers []*stashFindPerformerNamePerformer `graphql:"performers"` } -func scrapePerformerNamesStash(c scraperTypeConfig, name string) ([]*models.ScrapedPerformer, error) { - client := getStashClient(c) +func (s *stashScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) { + client := s.getStashClient() var q struct { FindPerformers stashFindPerformerNamesResultType `graphql:"findPerformers(filter: $f)"` @@ -64,8 +79,8 @@ func scrapePerformerNamesStash(c scraperTypeConfig, name string) ([]*models.Scra return ret, nil } -func scrapePerformerFragmentStash(c scraperTypeConfig, scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { - client := getStashClient(c) +func (s *stashScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { + client := s.getStashClient() var q struct { FindPerformer *models.ScrapedPerformerStash `graphql:"findPerformer(id: $f)"` @@ -91,7 +106,7 @@ func scrapePerformerFragmentStash(c scraperTypeConfig, scrapedPerformer models.S } // get the performer image directly - ret.Image, err = getStashPerformerImage(c.scraperConfig.StashServer.URL, performerID) + ret.Image, err = getStashPerformerImage(s.config.StashServer.URL, performerID, s.globalConfig) if err != nil { return nil, err } @@ -99,7 +114,7 @@ func scrapePerformerFragmentStash(c scraperTypeConfig, scrapedPerformer models.S return &ret, nil } -func scrapeSceneFragmentStash(c scraperTypeConfig, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +func (s *stashScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { // query by MD5 // assumes that the scene exists in the database qb := models.NewSceneQueryBuilder() @@ -115,15 +130,24 @@ func scrapeSceneFragmentStash(c scraperTypeConfig, scene models.SceneUpdateInput } var q struct { - FindScene *models.ScrapedSceneStash `graphql:"findScene(checksum: $c)"` + FindScene *models.ScrapedSceneStash `graphql:"findSceneByHash(input: $c)"` + } + + type SceneHashInput struct { + Checksum *string `graphql:"checksum" json:"checksum"` + Oshash *string `graphql:"oshash" json:"oshash"` + } + + input := SceneHashInput{ + Checksum: &storedScene.Checksum.String, + Oshash: &storedScene.OSHash.String, } - checksum := graphql.String(storedScene.Checksum) vars := map[string]interface{}{ - "c": &checksum, + "c": &input, } - client := getStashClient(c) + client := s.getStashClient() err = client.Query(context.Background(), &q, vars) if err != nil { return nil, err @@ -152,10 +176,33 @@ func scrapeSceneFragmentStash(c scraperTypeConfig, scene models.SceneUpdateInput } // get the performer image directly - ret.Image, err = getStashSceneImage(c.scraperConfig.StashServer.URL, q.FindScene.ID) + ret.Image, err = getStashSceneImage(s.config.StashServer.URL, q.FindScene.ID, s.globalConfig) if err != nil { return nil, err } return &ret, nil } + +func (s *stashScraper) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) { + return nil, errors.New("scrapePerformerByURL not supported for stash scraper") +} + +func (s *stashScraper) scrapeSceneByURL(url string) (*models.ScrapedScene, error) { + return nil, errors.New("scrapeSceneByURL not supported for stash scraper") +} + +func (s *stashScraper) scrapeMovieByURL(url string) (*models.ScrapedMovie, error) { + return nil, errors.New("scrapeMovieByURL not supported for stash scraper") +} + +func sceneFromUpdateFragment(scene models.SceneUpdateInput) (*models.Scene, error) { + qb := models.NewSceneQueryBuilder() + id, err := strconv.Atoi(scene.ID) + if err != nil { + return nil, err + } + + // TODO - should we modify it with the input? + return qb.Find(id) +} diff --git a/pkg/scraper/url.go b/pkg/scraper/url.go new file mode 100644 index 000000000..5b99f6a25 --- /dev/null +++ b/pkg/scraper/url.go @@ -0,0 +1,199 @@ +package scraper + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/cookiejar" + "os" + "path/filepath" + "strings" + "time" + + "github.com/chromedp/cdproto/dom" + "github.com/chromedp/cdproto/network" + "github.com/chromedp/chromedp" + jsoniter "github.com/json-iterator/go" + "github.com/stashapp/stash/pkg/logger" + "github.com/stashapp/stash/pkg/models" + "golang.org/x/net/html/charset" + "golang.org/x/net/publicsuffix" +) + +// Timeout for the scrape http request. Includes transfer time. May want to make this +// configurable at some point. +const scrapeGetTimeout = time.Second * 30 + +func constructSceneURL(url string, scene *models.Scene) string { + // support checksum, title and filename + ret := strings.Replace(url, "{checksum}", scene.Checksum.String, -1) + ret = strings.Replace(url, "{oshash}", scene.OSHash.String, -1) + ret = strings.Replace(ret, "{filename}", filepath.Base(scene.Path), -1) + ret = strings.Replace(ret, "{title}", scene.Title.String, -1) + + return ret +} + +func loadURL(url string, scraperConfig config, globalConfig GlobalConfig) (io.Reader, error) { + driverOptions := scraperConfig.DriverOptions + if driverOptions != nil && driverOptions.UseCDP { + // get the page using chrome dp + return urlFromCDP(url, *driverOptions, globalConfig) + } + + // get the page using http.Client + options := cookiejar.Options{ + PublicSuffixList: publicsuffix.List, + } + jar, er := cookiejar.New(&options) + if er != nil { + return nil, er + } + + client := &http.Client{ + Timeout: scrapeGetTimeout, + // defaultCheckRedirect code with max changed from 10 to 20 + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) >= 20 { + return errors.New("stopped after 20 redirects") + } + return nil + }, + Jar: jar, + } + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + userAgent := globalConfig.UserAgent + if userAgent != "" { + req.Header.Set("User-Agent", userAgent) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + bodyReader := bytes.NewReader(body) + + return charset.NewReader(bodyReader, resp.Header.Get("Content-Type")) +} + +// func urlFromCDP uses chrome cdp and DOM to load and process the url +// if remote is set as true in the scraperConfig it will try to use localhost:9222 +// else it will look for google-chrome in path +func urlFromCDP(url string, driverOptions scraperDriverOptions, globalConfig GlobalConfig) (io.Reader, error) { + const defaultSleep = 2 + + if !driverOptions.UseCDP { + return nil, fmt.Errorf("Url shouldn't be feetched through CDP") + } + + sleep := defaultSleep + + if driverOptions.Sleep != 0 { + sleep = driverOptions.Sleep + } + + sleepDuration := time.Duration(sleep) * time.Second + act := context.Background() + + // if scraperCDPPath is a remote address, then allocate accordingly + if globalConfig.CDPPath != "" { + var cancelAct context.CancelFunc + + if globalConfig.isCDPPathHTTP() || globalConfig.isCDPPathWS() { + remote := globalConfig.CDPPath + + // if CDPPath is http(s) then we need to get the websocket URL + if globalConfig.isCDPPathHTTP() { + var err error + remote, err = getRemoteCDPWSAddress(remote) + if err != nil { + return nil, err + } + } + + act, cancelAct = chromedp.NewRemoteAllocator(context.Background(), remote) + } else { + // user a temporary user directory for chrome + dir, err := ioutil.TempDir("", "stash-chromedp") + if err != nil { + return nil, err + } + defer os.RemoveAll(dir) + + opts := append(chromedp.DefaultExecAllocatorOptions[:], + chromedp.UserDataDir(dir), + chromedp.ExecPath(globalConfig.CDPPath), + ) + act, cancelAct = chromedp.NewExecAllocator(act, opts...) + } + + defer cancelAct() + } + + ctx, cancel := chromedp.NewContext(act) + defer cancel() + + var res string + err := chromedp.Run(ctx, + network.Enable(), + chromedp.Navigate(url), + chromedp.Sleep(sleepDuration), + chromedp.ActionFunc(func(ctx context.Context) error { + node, err := dom.GetDocument().Do(ctx) + if err != nil { + return err + } + res, err = dom.GetOuterHTML().WithNodeID(node.NodeID).Do(ctx) + return err + }), + ) + if err != nil { + return nil, err + } + + return strings.NewReader(res), nil +} + +// getRemoteCDPWSAddress returns the complete remote address that is required to access the cdp instance +func getRemoteCDPWSAddress(address string) (string, error) { + resp, err := http.Get(address) + if err != nil { + return "", err + } + + var result map[string]interface{} + var json = jsoniter.ConfigCompatibleWithStandardLibrary + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return "", err + } + remote := result["webSocketDebuggerUrl"].(string) + logger.Debugf("Remote cdp instance found %s", remote) + return remote, err +} + +func cdpNetwork(enable bool) chromedp.Action { + return chromedp.ActionFunc(func(ctx context.Context) error { + if enable { + network.Enable().Do(ctx) + } else { + network.Disable().Do(ctx) + } + return nil + }) +} diff --git a/pkg/scraper/xpath.go b/pkg/scraper/xpath.go index 8c2670870..6219b10e8 100644 --- a/pkg/scraper/xpath.go +++ b/pkg/scraper/xpath.go @@ -3,555 +3,149 @@ package scraper import ( "bytes" "errors" - "net/http" "net/url" - "reflect" "regexp" "strings" - "time" "github.com/antchfx/htmlquery" + "golang.org/x/net/html" - "golang.org/x/net/html/charset" "github.com/stashapp/stash/pkg/logger" - "github.com/stashapp/stash/pkg/manager/config" "github.com/stashapp/stash/pkg/models" ) -// Timeout for the scrape http request. Includes transfer time. May want to make this -// configurable at some point. -const scrapeGetTimeout = time.Second * 30 - -type commonXPathConfig map[string]string - -func (c commonXPathConfig) applyCommon(src string) string { - ret := src - for commonKey, commonVal := range c { - if strings.Contains(ret, commonKey) { - ret = strings.Replace(ret, commonKey, commonVal, -1) - } - } - - return ret -} - -type xpathScraperConfig map[string]interface{} - -func createXPathScraperConfig(src map[interface{}]interface{}) xpathScraperConfig { - ret := make(xpathScraperConfig) - - if src != nil { - for k, v := range src { - keyStr, isStr := k.(string) - if isStr { - ret[keyStr] = v - } - } - } - - return ret -} - -type xpathRegexConfig map[interface{}]interface{} -type xpathRegexConfigs []xpathRegexConfig - -func (c xpathRegexConfig) apply(value string) string { - regex := "" - with := "" - - if regexI, _ := c["regex"]; regexI != nil { - regex, _ = regexI.(string) - } - if withI, _ := c["with"]; withI != nil { - with, _ = withI.(string) - } - - if regex != "" { - re, err := regexp.Compile(regex) - if err != nil { - logger.Warnf("Error compiling regex '%s': %s", regex, err.Error()) - return value - } - - ret := re.ReplaceAllString(value, with) - - logger.Debugf(`Replace: '%s' with '%s'`, regex, with) - logger.Debugf("Before: %s", value) - logger.Debugf("After: %s", ret) - return ret - } - - return value -} - -func (c xpathRegexConfigs) apply(value string) string { - // apply regex in order - for _, config := range c { - value = config.apply(value) - } - - // remove whitespace again - value = commonPostProcess(value) - - return value -} - -type xpathScraperAttrConfig map[interface{}]interface{} - -func (c xpathScraperAttrConfig) getString(key string) string { - ret, _ := c[key] - - if ret == nil { - return "" - } - - asStr, _ := ret.(string) - return asStr -} - -func (c xpathScraperAttrConfig) getSelector() string { - const selectorKey = "selector" - return c.getString(selectorKey) -} - -func (c xpathScraperAttrConfig) getConcat() string { - const concatKey = "concat" - return c.getString(concatKey) -} - -func (c xpathScraperAttrConfig) hasConcat() bool { - return c.getConcat() != "" -} - -func (c xpathScraperAttrConfig) getParseDate() string { - const parseDateKey = "parseDate" - return c.getString(parseDateKey) -} - -func (c xpathScraperAttrConfig) getReplace() xpathRegexConfigs { - const replaceKey = "replace" - val, _ := c[replaceKey] - - var ret xpathRegexConfigs - if val == nil { - return ret - } - - asSlice, _ := val.([]interface{}) - - for _, v := range asSlice { - asMap, _ := v.(map[interface{}]interface{}) - ret = append(ret, xpathRegexConfig(asMap)) - } - - return ret -} - -func (c xpathScraperAttrConfig) getSubScraper() xpathScraperAttrConfig { - const subScraperKey = "subScraper" - val, _ := c[subScraperKey] - - if val == nil { - return nil - } - - asMap, _ := val.(map[interface{}]interface{}) - if asMap != nil { - return xpathScraperAttrConfig(asMap) - } - - return nil -} - -func (c xpathScraperAttrConfig) concatenateResults(nodes []*html.Node) string { - separator := c.getConcat() - result := []string{} - - for _, elem := range nodes { - text := NodeText(elem) - text = commonPostProcess(text) - - result = append(result, text) - } - - return strings.Join(result, separator) -} - -func (c xpathScraperAttrConfig) parseDate(value string) string { - parseDate := c.getParseDate() - - if parseDate == "" { - return value - } - - // try to parse the date using the pattern - // if it fails, then just fall back to the original value - parsedValue, err := time.Parse(parseDate, value) - if err != nil { - logger.Warnf("Error parsing date string '%s' using format '%s': %s", value, parseDate, err.Error()) - return value - } - - // convert it into our date format - const internalDateFormat = "2006-01-02" - return parsedValue.Format(internalDateFormat) -} - -func (c xpathScraperAttrConfig) replaceRegex(value string) string { - replace := c.getReplace() - return replace.apply(value) -} - -func (c xpathScraperAttrConfig) applySubScraper(value string) string { - subScraper := c.getSubScraper() - - if subScraper == nil { - return value - } - - logger.Debugf("Sub-scraping for: %s", value) - doc, err := loadURL(value, nil) - - if err != nil { - logger.Warnf("Error getting URL '%s' for sub-scraper: %s", value, err.Error()) - return "" - } - - found := runXPathQuery(doc, subScraper.getSelector(), nil) - - if len(found) > 0 { - // check if we're concatenating the results into a single result - var result string - if subScraper.hasConcat() { - result = subScraper.concatenateResults(found) - } else { - result = NodeText(found[0]) - result = commonPostProcess(result) - } - - result = subScraper.postProcess(result) - return result - } - - return "" -} - -func (c xpathScraperAttrConfig) postProcess(value string) string { - // perform regex replacements first - value = c.replaceRegex(value) - value = c.parseDate(value) - value = c.applySubScraper(value) - - return value -} - -func commonPostProcess(value string) string { - value = strings.TrimSpace(value) - - // remove multiple whitespace and end lines - re := regexp.MustCompile("\n") - value = re.ReplaceAllString(value, "") - re = regexp.MustCompile(" +") - value = re.ReplaceAllString(value, " ") - - return value -} - -func runXPathQuery(doc *html.Node, xpath string, common commonXPathConfig) []*html.Node { - // apply common - if common != nil { - xpath = common.applyCommon(xpath) - } - - found, err := htmlquery.QueryAll(doc, xpath) - if err != nil { - logger.Warnf("Error parsing xpath expression '%s': %s", xpath, err.Error()) - return nil - } - - return found -} - -func (s xpathScraperConfig) process(doc *html.Node, common commonXPathConfig) xPathResults { - var ret xPathResults - - for k, value := range s { - switch v := value.(type) { - case string: - found := runXPathQuery(doc, v, common) - - if len(found) > 0 { - for i, elem := range found { - text := NodeText(elem) - text = commonPostProcess(text) - - ret = ret.setKey(i, k, text) - } - } - case map[interface{}]interface{}: - attrConfig := xpathScraperAttrConfig(v) - - found := runXPathQuery(doc, attrConfig.getSelector(), common) - - if len(found) > 0 { - // check if we're concatenating the results into a single result - if attrConfig.hasConcat() { - result := attrConfig.concatenateResults(found) - result = attrConfig.postProcess(result) - const i = 0 - ret = ret.setKey(i, k, result) - } else { - for i, elem := range found { - text := NodeText(elem) - text = commonPostProcess(text) - text = attrConfig.postProcess(text) - - ret = ret.setKey(i, k, text) - } - } - } - } - } - - return ret -} - -type xpathScrapers map[string]*xpathScraper - type xpathScraper struct { - Common commonXPathConfig `yaml:"common"` - Scene xpathScraperConfig `yaml:"scene"` - Performer xpathScraperConfig `yaml:"performer"` + scraper scraperTypeConfig + config config + globalConfig GlobalConfig } -const ( - XPathScraperConfigSceneTags = "Tags" - XPathScraperConfigScenePerformers = "Performers" - XPathScraperConfigSceneStudio = "Studio" - XPathScraperConfigSceneMovies = "Movies" -) - -func (s xpathScraper) GetSceneSimple() xpathScraperConfig { - // exclude the complex sub-configs - ret := make(xpathScraperConfig) - mapped := s.Scene - - if mapped != nil { - for k, v := range mapped { - if k != XPathScraperConfigSceneTags && k != XPathScraperConfigScenePerformers && k != XPathScraperConfigSceneStudio && k != XPathScraperConfigSceneMovies { - ret[k] = v - } - } - } - - return ret -} - -func (s xpathScraper) getSceneSubMap(key string) xpathScraperConfig { - var ret map[interface{}]interface{} - mapped := s.Scene - - if mapped != nil { - v, ok := mapped[key] - if ok { - ret, _ = v.(map[interface{}]interface{}) - } - } - - if ret != nil { - return createXPathScraperConfig(ret) - } - - return nil -} - -func (s xpathScraper) GetScenePerformers() xpathScraperConfig { - return s.getSceneSubMap(XPathScraperConfigScenePerformers) -} - -func (s xpathScraper) GetSceneTags() xpathScraperConfig { - return s.getSceneSubMap(XPathScraperConfigSceneTags) -} - -func (s xpathScraper) GetSceneStudio() xpathScraperConfig { - return s.getSceneSubMap(XPathScraperConfigSceneStudio) -} - -func (s xpathScraper) GetSceneMovies() xpathScraperConfig { - return s.getSceneSubMap(XPathScraperConfigSceneMovies) -} - -func (s xpathScraper) scrapePerformer(doc *html.Node) (*models.ScrapedPerformer, error) { - var ret models.ScrapedPerformer - - performerMap := s.Performer - if performerMap == nil { - return nil, nil - } - - results := performerMap.process(doc, s.Common) - if len(results) > 0 { - results[0].apply(&ret) - } - - return &ret, nil -} - -func (s xpathScraper) scrapePerformers(doc *html.Node) ([]*models.ScrapedPerformer, error) { - var ret []*models.ScrapedPerformer - - performerMap := s.Performer - if performerMap == nil { - return nil, nil - } - - results := performerMap.process(doc, s.Common) - for _, r := range results { - var p models.ScrapedPerformer - r.apply(&p) - ret = append(ret, &p) - } - - return ret, nil -} - -func (s xpathScraper) scrapeScene(doc *html.Node) (*models.ScrapedScene, error) { - var ret models.ScrapedScene - - sceneMap := s.GetSceneSimple() - if sceneMap == nil { - return nil, nil - } - - scenePerformersMap := s.GetScenePerformers() - sceneTagsMap := s.GetSceneTags() - sceneStudioMap := s.GetSceneStudio() - sceneMoviesMap := s.GetSceneMovies() - - logger.Debug(`Processing scene:`) - results := sceneMap.process(doc, s.Common) - if len(results) > 0 { - results[0].apply(&ret) - - // now apply the performers and tags - if scenePerformersMap != nil { - logger.Debug(`Processing scene performers:`) - performerResults := scenePerformersMap.process(doc, s.Common) - - for _, p := range performerResults { - performer := &models.ScrapedScenePerformer{} - p.apply(performer) - ret.Performers = append(ret.Performers, performer) - } - } - - if sceneTagsMap != nil { - logger.Debug(`Processing scene tags:`) - tagResults := sceneTagsMap.process(doc, s.Common) - - for _, p := range tagResults { - tag := &models.ScrapedSceneTag{} - p.apply(tag) - ret.Tags = append(ret.Tags, tag) - } - } - - if sceneStudioMap != nil { - logger.Debug(`Processing scene studio:`) - studioResults := sceneStudioMap.process(doc, s.Common) - - if len(studioResults) > 0 { - studio := &models.ScrapedSceneStudio{} - studioResults[0].apply(studio) - ret.Studio = studio - } - } - - if sceneMoviesMap != nil { - logger.Debug(`Processing scene movies:`) - movieResults := sceneMoviesMap.process(doc, s.Common) - - for _, p := range movieResults { - movie := &models.ScrapedSceneMovie{} - p.apply(movie) - ret.Movies = append(ret.Movies, movie) - } - - } - } - - return &ret, nil -} - -type xPathResult map[string]string -type xPathResults []xPathResult - -func (r xPathResult) apply(dest interface{}) { - destVal := reflect.ValueOf(dest) - - // dest should be a pointer - destVal = destVal.Elem() - - for key, value := range r { - field := destVal.FieldByName(key) - - if field.IsValid() { - var reflectValue reflect.Value - if field.Kind() == reflect.Ptr { - // need to copy the value, otherwise everything is set to the - // same pointer - localValue := value - reflectValue = reflect.ValueOf(&localValue) - } else { - reflectValue = reflect.ValueOf(value) - } - - field.Set(reflectValue) - } else { - logger.Errorf("Field %s does not exist in %T", key, dest) - } +func newXpathScraper(scraper scraperTypeConfig, config config, globalConfig GlobalConfig) *xpathScraper { + return &xpathScraper{ + scraper: scraper, + config: config, + globalConfig: globalConfig, } } -func (r xPathResults) setKey(index int, key string, value string) xPathResults { - if index >= len(r) { - r = append(r, make(xPathResult)) - } - - logger.Debugf(`[%d][%s] = %s`, index, key, value) - r[index][key] = value - return r +func (s *xpathScraper) getXpathScraper() *mappedScraper { + return s.config.XPathScrapers[s.scraper.Scraper] } -func loadURL(url string, c *scraperConfig) (*html.Node, error) { - client := &http.Client{ - Timeout: scrapeGetTimeout, +func (s *xpathScraper) scrapeURL(url string) (*html.Node, *mappedScraper, error) { + scraper := s.getXpathScraper() + + if scraper == nil { + return nil, nil, errors.New("xpath scraper with name " + s.scraper.Scraper + " not found in config") } - req, err := http.NewRequest("GET", url, nil) + + doc, err := s.loadURL(url) + + if err != nil { + return nil, nil, err + } + + return doc, scraper, nil +} + +func (s *xpathScraper) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) { + doc, scraper, err := s.scrapeURL(url) if err != nil { return nil, err } - userAgent := config.GetScraperUserAgent() - if userAgent != "" { - req.Header.Set("User-Agent", userAgent) - } + q := s.getXPathQuery(doc) + return scraper.scrapePerformer(q) +} - resp, err := client.Do(req) +func (s *xpathScraper) scrapeSceneByURL(url string) (*models.ScrapedScene, error) { + doc, scraper, err := s.scrapeURL(url) if err != nil { return nil, err } - defer resp.Body.Close() - r, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type")) + q := s.getXPathQuery(doc) + return scraper.scrapeScene(q) +} + +func (s *xpathScraper) scrapeMovieByURL(url string) (*models.ScrapedMovie, error) { + doc, scraper, err := s.scrapeURL(url) + if err != nil { + return nil, err + } + + q := s.getXPathQuery(doc) + return scraper.scrapeMovie(q) +} + +func (s *xpathScraper) scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) { + scraper := s.getXpathScraper() + + if scraper == nil { + return nil, errors.New("xpath scraper with name " + s.scraper.Scraper + " not found in config") + } + + const placeholder = "{}" + + // replace the placeholder string with the URL-escaped name + escapedName := url.QueryEscape(name) + + url := s.scraper.QueryURL + url = strings.Replace(url, placeholder, escapedName, -1) + + doc, err := s.loadURL(url) + + if err != nil { + return nil, err + } + + q := s.getXPathQuery(doc) + return scraper.scrapePerformers(q) +} + +func (s *xpathScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { + return nil, errors.New("scrapePerformerByFragment not supported for xpath scraper") +} + +func (s *xpathScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { + storedScene, err := sceneFromUpdateFragment(scene) + if err != nil { + return nil, err + } + + if storedScene == nil { + return nil, errors.New("no scene found") + } + + // construct the URL + url := constructSceneURL(s.scraper.QueryURL, storedScene) + + scraper := s.getXpathScraper() + + if scraper == nil { + return nil, errors.New("xpath scraper with name " + s.scraper.Scraper + " not found in config") + } + + doc, err := s.loadURL(url) + + if err != nil { + return nil, err + } + + q := s.getXPathQuery(doc) + return scraper.scrapeScene(q) +} + +func (s *xpathScraper) loadURL(url string) (*html.Node, error) { + r, err := loadURL(url, s.config, s.globalConfig) if err != nil { return nil, err } ret, err := html.Parse(r) - if err == nil && c != nil && c.DebugOptions != nil && c.DebugOptions.PrintHTML { + if err == nil && s.config.DebugOptions != nil && s.config.DebugOptions.PrintHTML { var b bytes.Buffer html.Render(&b, ret) logger.Infof("loadURL (%s) response: \n%s", url, b.String()) @@ -560,65 +154,66 @@ func loadURL(url string, c *scraperConfig) (*html.Node, error) { return ret, err } -func scrapePerformerURLXpath(c scraperTypeConfig, url string) (*models.ScrapedPerformer, error) { - scraper := c.scraperConfig.XPathScrapers[c.Scraper] - - if scraper == nil { - return nil, errors.New("xpath scraper with name " + c.Scraper + " not found in config") +func (s *xpathScraper) getXPathQuery(doc *html.Node) *xpathQuery { + return &xpathQuery{ + doc: doc, + scraper: s, } - - doc, err := loadURL(url, c.scraperConfig) - - if err != nil { - return nil, err - } - - return scraper.scrapePerformer(doc) } -func scrapeSceneURLXPath(c scraperTypeConfig, url string) (*models.ScrapedScene, error) { - scraper := c.scraperConfig.XPathScrapers[c.Scraper] - - if scraper == nil { - return nil, errors.New("xpath scraper with name " + c.Scraper + " not found in config") - } - - doc, err := loadURL(url, c.scraperConfig) - - if err != nil { - return nil, err - } - - return scraper.scrapeScene(doc) +type xpathQuery struct { + doc *html.Node + scraper *xpathScraper } -func scrapePerformerNamesXPath(c scraperTypeConfig, name string) ([]*models.ScrapedPerformer, error) { - scraper := c.scraperConfig.XPathScrapers[c.Scraper] - - if scraper == nil { - return nil, errors.New("xpath scraper with name " + c.Scraper + " not found in config") - } - - const placeholder = "{}" - - // replace the placeholder string with the URL-escaped name - escapedName := url.QueryEscape(name) - - u := c.QueryURL - u = strings.Replace(u, placeholder, escapedName, -1) - - doc, err := loadURL(u, c.scraperConfig) - +func (q *xpathQuery) runQuery(selector string) []string { + found, err := htmlquery.QueryAll(q.doc, selector) if err != nil { - return nil, err + logger.Warnf("Error parsing xpath expression '%s': %s", selector, err.Error()) + return nil } - return scraper.scrapePerformers(doc) + var ret []string + for _, n := range found { + // don't add empty strings + nodeText := q.nodeText(n) + if nodeText != "" { + ret = append(ret, q.nodeText(n)) + } + } + + return ret } -func NodeText(n *html.Node) string { +func (q *xpathQuery) nodeText(n *html.Node) string { + var ret string if n != nil && n.Type == html.CommentNode { - return htmlquery.OutputHTML(n, true) + ret = htmlquery.OutputHTML(n, true) + } else { + ret = htmlquery.InnerText(n) } - return htmlquery.InnerText(n) + + // trim all leading and trailing whitespace + ret = strings.TrimSpace(ret) + + // remove multiple whitespace + re := regexp.MustCompile(" +") + ret = re.ReplaceAllString(ret, " ") + + // TODO - make this optional + re = regexp.MustCompile("\n") + ret = re.ReplaceAllString(ret, "") + + return ret +} + +func (q *xpathQuery) subScrape(value string) mappedQuery { + doc, err := q.scraper.loadURL(value) + + if err != nil { + logger.Warnf("Error getting URL '%s' for sub-scraper: %s", value, err.Error()) + return nil + } + + return q.scraper.getXPathQuery(doc) } diff --git a/pkg/scraper/xpath_test.go b/pkg/scraper/xpath_test.go index 8fad5f513..bd81b121c 100644 --- a/pkg/scraper/xpath_test.go +++ b/pkg/scraper/xpath_test.go @@ -1,11 +1,15 @@ package scraper import ( + "fmt" + "net/http" + "net/http/httptest" "strings" "testing" "github.com/antchfx/htmlquery" "github.com/stashapp/stash/pkg/models" + "github.com/stretchr/testify/assert" "gopkg.in/yaml.v2" ) @@ -93,28 +97,7 @@ const htmlDoc1 = ` Height: -   + 5ft7 @@ -155,7 +138,7 @@ const htmlDoc1 = ` Piercings: - None  + ; @@ -183,49 +166,84 @@ func makeCommonXPath(attr string) string { return `//table[@id="biographyTable"]//tr/td[@class="paramname"]//b[text() = '` + attr + `']/ancestor::tr/td[@class="paramvalue"]` } -func makeReplaceRegex(regex string, with string) map[interface{}]interface{} { - ret := make(map[interface{}]interface{}) +func makeSimpleAttrConfig(str string) mappedScraperAttrConfig { + return mappedScraperAttrConfig{ + Selector: str, + } +} + +func makeReplaceRegex(regex string, with string) mappedRegexConfig { + ret := mappedRegexConfig{ + Regex: regex, + With: with, + } - ret["regex"] = regex - ret["with"] = with return ret } -func makeXPathConfig() xpathScraperConfig { - config := make(xpathScraperConfig) +func makeXPathConfig() mappedPerformerScraperConfig { + config := mappedPerformerScraperConfig{ + mappedConfig: make(mappedConfig), + } - config["Name"] = makeCommonXPath("Babe Name:") + `/a` - config["Ethnicity"] = makeCommonXPath("Ethnicity:") - config["Country"] = makeCommonXPath("Country of Origin:") - config["Aliases"] = makeCommonXPath("Aliases:") - config["EyeColor"] = makeCommonXPath("Eye Color:") - config["Measurements"] = makeCommonXPath("Measurements:") - config["FakeTits"] = makeCommonXPath("Fake boobs:") - config["Height"] = makeCommonXPath("Height:") - config["Tattoos"] = makeCommonXPath("Tattoos:") - config["Piercings"] = makeCommonXPath("Piercings:") + config.mappedConfig["Name"] = makeSimpleAttrConfig(makeCommonXPath("Babe Name:") + `/a`) + config.mappedConfig["Ethnicity"] = makeSimpleAttrConfig(makeCommonXPath("Ethnicity:")) + config.mappedConfig["Aliases"] = makeSimpleAttrConfig(makeCommonXPath("Aliases:")) + config.mappedConfig["EyeColor"] = makeSimpleAttrConfig(makeCommonXPath("Eye Color:")) + config.mappedConfig["Measurements"] = makeSimpleAttrConfig(makeCommonXPath("Measurements:")) + config.mappedConfig["FakeTits"] = makeSimpleAttrConfig(makeCommonXPath("Fake boobs:")) + config.mappedConfig["Tattoos"] = makeSimpleAttrConfig(makeCommonXPath("Tattoos:")) + config.mappedConfig["Piercings"] = makeSimpleAttrConfig(makeCommonXPath("Piercings:") + "/comment()") // special handling for birthdate - birthdateAttrConfig := make(map[interface{}]interface{}) - birthdateAttrConfig["selector"] = makeCommonXPath("Date of Birth:") + birthdateAttrConfig := makeSimpleAttrConfig(makeCommonXPath("Date of Birth:")) - var birthdateReplace []interface{} - birthdateReplace = append(birthdateReplace, makeReplaceRegex(` \(.* years old\)`, "")) + var birthdateReplace mappedRegexConfigs + // make this leave the trailing space to test existing scrapers that do so + birthdateReplace = append(birthdateReplace, makeReplaceRegex(`\(.* years old\)`, "")) - birthdateAttrConfig["replace"] = birthdateReplace - birthdateAttrConfig["parseDate"] = "January 2, 2006" // "July 1, 1992 (27 years old) " - config["Birthdate"] = birthdateAttrConfig + birthdateReplaceAction := postProcessReplace(birthdateReplace) + birthdateParseDate := postProcessParseDate("January 2, 2006") // "July 1, 1992 (27 years old) " + birthdateAttrConfig.postProcessActions = []postProcessAction{ + &birthdateReplaceAction, + &birthdateParseDate, + } + config.mappedConfig["Birthdate"] = birthdateAttrConfig // special handling for career length - careerLengthAttrConfig := make(map[interface{}]interface{}) // no colon in attribute header - careerLengthAttrConfig["selector"] = makeCommonXPath("Career Start And End") + careerLengthAttrConfig := makeSimpleAttrConfig(makeCommonXPath("Career Start And End")) - var careerLengthReplace []interface{} + var careerLengthReplace mappedRegexConfigs careerLengthReplace = append(careerLengthReplace, makeReplaceRegex(`\s+\(.*\)`, "")) - careerLengthAttrConfig["replace"] = careerLengthReplace + careerLengthReplaceAction := postProcessReplace(careerLengthReplace) + careerLengthAttrConfig.postProcessActions = []postProcessAction{ + &careerLengthReplaceAction, + } - config["CareerLength"] = careerLengthAttrConfig + config.mappedConfig["CareerLength"] = careerLengthAttrConfig + + // use map post-process action for gender + genderConfig := makeSimpleAttrConfig(makeCommonXPath("Profession:")) + genderMapAction := make(postProcessMap) + genderMapAction["Porn Star"] = "Female" + genderConfig.postProcessActions = []postProcessAction{ + &genderMapAction, + } + + config.mappedConfig["Gender"] = genderConfig + + // use fixed for height + config.mappedConfig["Country"] = mappedScraperAttrConfig{ + Fixed: "United States", + } + + heightConfig := makeSimpleAttrConfig(makeCommonXPath("Height:")) + heightConvAction := postProcessFeetToCm(true) + heightConfig.postProcessActions = []postProcessAction{ + &heightConvAction, + } + config.mappedConfig["Height"] = heightConfig return config } @@ -253,11 +271,15 @@ func TestScrapePerformerXPath(t *testing.T) { xpathConfig := makeXPathConfig() - scraper := xpathScraper{ - Performer: xpathConfig, + scraper := mappedScraper{ + Performer: &xpathConfig, } - performer, err := scraper.scrapePerformer(doc) + q := &xpathQuery{ + doc: doc, + } + + performer, err := scraper.scrapePerformer(q) if err != nil { t.Errorf("Error scraping performer: %s", err.Error()) @@ -273,9 +295,13 @@ func TestScrapePerformerXPath(t *testing.T) { const measurements = "34C-26-36" const fakeTits = "No" const careerLength = "2012 - 2019" - const tattoosPiercings = "None" + const tattoos = "None" + const piercings = "" + const gender = "Female" + const height = "170" verifyField(t, performerName, performer.Name, "Name") + verifyField(t, gender, performer.Gender, "Gender") verifyField(t, ethnicity, performer.Ethnicity, "Ethnicity") verifyField(t, country, performer.Country, "Country") @@ -288,8 +314,9 @@ func TestScrapePerformerXPath(t *testing.T) { verifyField(t, careerLength, performer.CareerLength, "CareerLength") - verifyField(t, tattoosPiercings, performer.Tattoos, "Tattoos") - verifyField(t, tattoosPiercings, performer.Piercings, "Piercings") + verifyField(t, tattoos, performer.Tattoos, "Tattoos") + verifyField(t, piercings, performer.Piercings, "Piercings") + verifyField(t, height, performer.Height, "Height") } func TestConcatXPath(t *testing.T) { @@ -313,18 +340,25 @@ func TestConcatXPath(t *testing.T) { return } - xpathConfig := make(xpathScraperConfig) - nameAttrConfig := make(map[interface{}]interface{}) - nameAttrConfig["selector"] = "//div" - nameAttrConfig["concat"] = separator + xpathConfig := make(mappedConfig) + nameAttrConfig := mappedScraperAttrConfig{ + Selector: "//div", + Concat: separator, + } xpathConfig["Name"] = nameAttrConfig - xpathConfig["EyeColor"] = "//span" + xpathConfig["EyeColor"] = makeSimpleAttrConfig("//span") - scraper := xpathScraper{ - Performer: xpathConfig, + scraper := mappedScraper{ + Performer: &mappedPerformerScraperConfig{ + mappedConfig: xpathConfig, + }, } - performer, err := scraper.scrapePerformer(doc) + q := &xpathQuery{ + doc: doc, + } + + performer, err := scraper.scrapePerformer(q) if err != nil { t.Errorf("Error scraping performer: %s", err.Error()) @@ -342,55 +376,24 @@ const sceneHTML = ` Test Video - Pornhub.com - - - -
- - -
- +
-
- +
+

Test Video

@@ -402,45 +405,27 @@ const sceneHTML = ` -
-
- Production:  - professional -
-
-
Tags:  @@ -510,121 +487,6 @@ const sceneHTML = `
- -
- Added on: 2 months ago -
- -
- Featured on: 1 month ago -
-
-
- -
-
Jump to your favorite action
- -
- -
- -
-
- - - - -
-
- - -
@@ -637,34 +499,45 @@ const sceneHTML = ` ` -func makeSceneXPathConfig() xpathScraper { - common := make(commonXPathConfig) +func makeSceneXPathConfig() mappedScraper { + common := make(commonMappedConfig) common["$performerElem"] = `//div[@class="pornstarsWrapper"]/a[@data-mxptype="Pornstar"]` common["$studioElem"] = `//div[@data-type="channel"]/a` - config := make(xpathScraperConfig) + config := mappedSceneScraperConfig{ + mappedConfig: make(mappedConfig), + } - config["Title"] = `//meta[@property="og:title"]/@content` + config.mappedConfig["Title"] = makeSimpleAttrConfig(`//meta[@property="og:title"]/@content`) // this needs post-processing - config["Date"] = `//script[@type="application/ld+json"]` + config.mappedConfig["Date"] = makeSimpleAttrConfig(`//script[@type="application/ld+json"]`) - tagConfig := make(map[interface{}]interface{}) - tagConfig["Name"] = `//div[@class="categoriesWrapper"]//a[not(@class="add-btn-small ")]` - config["Tags"] = tagConfig + tagConfig := make(mappedConfig) + tagConfig["Name"] = makeSimpleAttrConfig(`//div[@class="categoriesWrapper"]//a[not(@class="add-btn-small ")]`) + config.Tags = tagConfig - performerConfig := make(map[interface{}]interface{}) - performerConfig["Name"] = `$performerElem/@data-mxptext` - performerConfig["URL"] = `$performerElem/@href` - config["Performers"] = performerConfig + performerConfig := make(mappedConfig) + performerConfig["Name"] = makeSimpleAttrConfig(`$performerElem/@data-mxptext`) + performerConfig["URL"] = makeSimpleAttrConfig(`$performerElem/@href`) + config.Performers = performerConfig - studioConfig := make(map[interface{}]interface{}) - studioConfig["Name"] = `$studioElem` - studioConfig["URL"] = `$studioElem/@href` - config["Studio"] = studioConfig + studioConfig := make(mappedConfig) + studioConfig["Name"] = makeSimpleAttrConfig(`$studioElem`) + studioConfig["URL"] = makeSimpleAttrConfig(`$studioElem/@href`) + config.Studio = studioConfig - scraper := xpathScraper{ - Scene: config, + const sep = " " + moviesNameConfig := mappedScraperAttrConfig{ + Selector: `//i[@class="isMe tooltipTrig"]/@data-title`, + Split: sep, + } + moviesConfig := make(mappedConfig) + moviesConfig["Name"] = moviesNameConfig + config.Movies = moviesConfig + + scraper := mappedScraper{ + Scene: &config, Common: common, } @@ -692,6 +565,27 @@ func verifyTags(t *testing.T, expectedTagNames []string, actualTags []*models.Sc } } +func verifyMovies(t *testing.T, expectedMovieNames []string, actualMovies []*models.ScrapedSceneMovie) { + t.Helper() + + i := 0 + for i < len(expectedMovieNames) || i < len(actualMovies) { + expectedMovie := "" + actualMovie := "" + if i < len(expectedMovieNames) { + expectedMovie = expectedMovieNames[i] + } + if i < len(actualMovies) { + actualMovie = actualMovies[i].Name + } + + if expectedMovie != actualMovie { + t.Errorf("Expected movie %s, got %s", expectedMovie, actualMovie) + } + i++ + } +} + func verifyPerformers(t *testing.T, expectedNames []string, expectedURLs []string, actualPerformers []*models.ScrapedScenePerformer) { t.Helper() @@ -735,7 +629,10 @@ func TestApplySceneXPathConfig(t *testing.T) { scraper := makeSceneXPathConfig() - scene, err := scraper.scrapeScene(doc) + q := &xpathQuery{ + doc: doc, + } + scene, err := scraper.scrapeScene(q) if err != nil { t.Errorf("Error scraping scene: %s", err.Error()) @@ -761,6 +658,15 @@ func TestApplySceneXPathConfig(t *testing.T) { } verifyTags(t, expectedTags, scene.Tags) + // verify movies + expectedMovies := []string{ + "Video", + "of", + "verified", + "member", + } + verifyMovies(t, expectedMovies, scene.Movies) + expectedPerformerNames := []string{ "Alex D", "Mia Malkova", @@ -793,21 +699,49 @@ xPathScrapers: performerScraper: performer: name: //h1[@itemprop="name"] + sceneScraper: + scene: + Title: + selector: //title + postProcess: + - parseDate: January 2, 2006 + Tags: + Name: //tags + Movies: + Name: //movies + Performers: + Name: //performers + Studio: + Name: //studio ` - config := &scraperConfig{} - err := yaml.Unmarshal([]byte(yamlStr), &config) + c := &config{} + err := yaml.Unmarshal([]byte(yamlStr), &c) if err != nil { t.Errorf("Error loading yaml: %s", err.Error()) return } + + // ensure fields are filled in correctly + sceneScraper := c.XPathScrapers["sceneScraper"] + sceneConfig := sceneScraper.Scene + + assert.Equal(t, "//title", sceneConfig.mappedConfig["Title"].Selector) + assert.Equal(t, "//tags", sceneConfig.Tags["Name"].Selector) + assert.Equal(t, "//movies", sceneConfig.Movies["Name"].Selector) + assert.Equal(t, "//performers", sceneConfig.Performers["Name"].Selector) + assert.Equal(t, "//studio", sceneConfig.Studio["Name"].Selector) + + postProcess := sceneConfig.mappedConfig["Title"].postProcessActions + parseDate := postProcess[0].(*postProcessParseDate) + assert.Equal(t, "January 2, 2006", string(*parseDate)) } func TestLoadInvalidXPath(t *testing.T) { - config := make(xpathScraperConfig) + config := make(mappedConfig) - config["Name"] = `//a[id=']/span` + config["Name"] = makeSimpleAttrConfig(`//a[id=']/span`) reader := strings.NewReader(htmlDoc1) doc, err := htmlquery.Parse(reader) @@ -817,6 +751,68 @@ func TestLoadInvalidXPath(t *testing.T) { return } - common := make(commonXPathConfig) - config.process(doc, common) + q := &xpathQuery{ + doc: doc, + } + + config.process(q, nil) +} + +func TestSubScrape(t *testing.T) { + retHTML := ` +
+ A link +
+ ` + + ssHTML := ` + The name + ` + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/getName" { + fmt.Fprint(w, ssHTML) + } else { + fmt.Fprint(w, retHTML) + } + })) + defer ts.Close() + + yamlStr := `name: Test +performerByURL: + - action: scrapeXPath + url: + - ` + ts.URL + ` + scraper: performerScraper +xPathScrapers: + performerScraper: + performer: + Name: + selector: //div/a/@href + postProcess: + - replace: + - regex: ^ + with: ` + ts.URL + ` + - subScraper: + selector: //span +` + + c := &config{} + err := yaml.Unmarshal([]byte(yamlStr), &c) + + if err != nil { + t.Errorf("Error loading yaml: %s", err.Error()) + return + } + + globalConfig := GlobalConfig{} + + performer, err := c.ScrapePerformerURL(ts.URL, globalConfig) + + if err != nil { + t.Errorf("Error scraping performer: %s", err.Error()) + return + } + + verifyField(t, "The name", performer.Name, "Name") } diff --git a/pkg/utils/byterange.go b/pkg/utils/byterange.go new file mode 100644 index 000000000..2220b8dc4 --- /dev/null +++ b/pkg/utils/byterange.go @@ -0,0 +1,59 @@ +package utils + +import ( + "strconv" + "strings" +) + +type ByteRange struct { + Start int64 + End *int64 + RawString string +} + +func CreateByteRange(s string) ByteRange { + // strip bytes= + r := strings.TrimPrefix(s, "bytes=") + e := strings.Split(r, "-") + + ret := ByteRange{ + RawString: s, + } + if len(e) > 0 { + ret.Start, _ = strconv.ParseInt(e[0], 10, 64) + } + if len(e) > 1 && e[1] != "" { + end, _ := strconv.ParseInt(e[1], 10, 64) + ret.End = &end + } + + return ret +} + +func (r ByteRange) getBytesToRead() int64 { + if r.End == nil { + return 0 + } + + return *r.End - r.Start + 1 +} + +func (r ByteRange) ToHeaderValue(fileLength int64) string { + if r.End == nil { + return "" + } + end := *r.End + return "bytes " + strconv.FormatInt(r.Start, 10) + "-" + strconv.FormatInt(end, 10) + "/" + strconv.FormatInt(fileLength, 10) +} + +func (r ByteRange) Apply(bytes []byte) []byte { + if r.End == nil { + return bytes[r.Start:] + } + + end := *r.End + 1 + if int(end) > len(bytes) { + end = int64(len(bytes)) + } + return bytes[r.Start:end] +} diff --git a/pkg/utils/crypto.go b/pkg/utils/crypto.go index ab765b6a0..06e11c9f9 100644 --- a/pkg/utils/crypto.go +++ b/pkg/utils/crypto.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "crypto/rand" "fmt" + "hash/fnv" "io" "os" ) @@ -38,3 +39,9 @@ func GenerateRandomKey(l int) string { rand.Read(b) return fmt.Sprintf("%x", b) } + +func IntFromString(str string) uint64 { + h := fnv.New64a() + h.Write([]byte(str)) + return h.Sum64() +} diff --git a/pkg/utils/file.go b/pkg/utils/file.go index b80e888c9..f4d947408 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -3,13 +3,17 @@ package utils import ( "archive/zip" "fmt" - "github.com/h2non/filetype" - "github.com/h2non/filetype/types" + "io" "io/ioutil" "math" + "net/http" "os" "os/user" "path/filepath" + + "github.com/h2non/filetype" + "github.com/h2non/filetype/types" + "github.com/stashapp/stash/pkg/logger" ) // FileType uses the filetype package to determine the given file path's type @@ -129,6 +133,43 @@ func GetHomeDirectory() string { return currentUser.HomeDir } +func SafeMove(src, dst string) error { + err := os.Rename(src, dst) + + if err != nil { + logger.Errorf("[Util] unable to rename: \"%s\" due to %s. Falling back to copying.", src, err.Error()) + + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + err = out.Close() + if err != nil { + return err + } + + err = os.Remove(src) + if err != nil { + return err + } + } + + return nil +} + // IsZipFileUnmcompressed returns true if zip file in path is using 0 compression level func IsZipFileUncompressed(path string) (bool, error) { r, err := zip.OpenReader(path) @@ -219,3 +260,11 @@ func GetParent(path string) *string { return &parentPath } } + +// ServeFileNoCache serves the provided file, ensuring that the response +// contains headers to prevent caching. +func ServeFileNoCache(w http.ResponseWriter, r *http.Request, filepath string) { + w.Header().Add("Cache-Control", "no-cache") + + http.ServeFile(w, r, filepath) +} diff --git a/pkg/utils/image.go b/pkg/utils/image.go index 8a07db441..7c550a0be 100644 --- a/pkg/utils/image.go +++ b/pkg/utils/image.go @@ -1,9 +1,12 @@ package utils import ( + "crypto/md5" "encoding/base64" "fmt" + "net/http" "regexp" + "strings" ) // ProcessBase64Image transforms a base64 encoded string from a form post and returns the MD5 hash of the data and the @@ -45,3 +48,24 @@ func GetBase64StringFromData(data []byte) string { //} //return result } + +func ServeImage(image []byte, w http.ResponseWriter, r *http.Request) error { + etag := fmt.Sprintf("%x", md5.Sum(image)) + + if match := r.Header.Get("If-None-Match"); match != "" { + if strings.Contains(match, etag) { + w.WriteHeader(http.StatusNotModified) + return nil + } + } + + contentType := http.DetectContentType(image) + if contentType == "text/xml; charset=utf-8" || contentType == "text/plain; charset=utf-8" { + contentType = "image/svg+xml" + } + + w.Header().Set("Content-Type", contentType) + w.Header().Add("Etag", etag) + _, err := w.Write(image) + return err +} diff --git a/pkg/utils/oshash.go b/pkg/utils/oshash.go new file mode 100644 index 000000000..1ddbe4de2 --- /dev/null +++ b/pkg/utils/oshash.go @@ -0,0 +1,82 @@ +package utils + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" +) + +// OSHashFromFilePath calculates the hash using the same algorithm that +// OpenSubtitles.org uses. +// +// Calculation is as follows: +// size + 64 bit checksum of the first and last 64k bytes of the file. +func OSHashFromFilePath(filePath string) (string, error) { + f, err := os.Open(filePath) + if err != nil { + return "", err + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return "", err + } + + fileSize := int64(fi.Size()) + + if fileSize == 0 { + return "", nil + } + + const chunkSize = 64 * 1024 + fileChunkSize := int64(chunkSize) + if fileSize < fileChunkSize { + fileChunkSize = fileSize + } + + head := make([]byte, fileChunkSize) + tail := make([]byte, fileChunkSize) + + // read the head of the file into the start of the buffer + _, err = f.Read(head) + if err != nil { + return "", err + } + + // seek to the end of the file - the chunk size + _, err = f.Seek(-fileChunkSize, 2) + if err != nil { + return "", err + } + + // read the tail of the file + _, err = f.Read(tail) + if err != nil { + return "", err + } + + // put the head and tail together + buf := append(head, tail...) + + // convert bytes into uint64 + ints := make([]uint64, len(buf)/8) + reader := bytes.NewReader(buf) + err = binary.Read(reader, binary.LittleEndian, &ints) + if err != nil { + return "", err + } + + // sum the integers + var sum uint64 + for _, v := range ints { + sum += v + } + + // add the filesize + sum += uint64(fileSize) + + // output as hex + return fmt.Sprintf("%016x", sum), nil +} diff --git a/pkg/utils/string_collections.go b/pkg/utils/string_collections.go index 5702e9375..aff2d136a 100644 --- a/pkg/utils/string_collections.go +++ b/pkg/utils/string_collections.go @@ -1,5 +1,7 @@ package utils +import "strconv" + // https://gobyexample.com/collection-functions func StrIndex(vs []string, t string) int { @@ -32,3 +34,15 @@ func StrMap(vs []string, f func(string) string) []string { } return vsm } + +// StringSliceToIntSlice converts a slice of strings to a slice of ints. If any +// values cannot be parsed, then they are inserted into the returned slice as +// 0. +func StringSliceToIntSlice(ss []string) []int { + ret := make([]int, len(ss)) + for i, v := range ss { + ret[i], _ = strconv.Atoi(v) + } + + return ret +} diff --git a/scripts/cross-compile.sh b/scripts/cross-compile.sh index 4601b8c85..5b9066ecb 100755 --- a/scripts/cross-compile.sh +++ b/scripts/cross-compile.sh @@ -1,15 +1,17 @@ #!/bin/sh -DATE=`go run -mod=vendor scripts/getDate.go` +BUILD_DATE=`go run -mod=vendor scripts/getDate.go` GITHASH=`git rev-parse --short HEAD` STASH_VERSION=`git describe --tags --exclude latest_develop` -VERSION_FLAGS="-X 'github.com/stashapp/stash/pkg/api.version=$STASH_VERSION' -X 'github.com/stashapp/stash/pkg/api.buildstamp=$DATE' -X 'github.com/stashapp/stash/pkg/api.githash=$GITHASH'" -SETUP="export GO111MODULE=on; export CGO_ENABLED=1; packr2;" -WINDOWS="echo '=== Building Windows binary ==='; GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -o dist/stash-win.exe -ldflags \"-extldflags '-static' $VERSION_FLAGS\" -tags extended -v -mod=vendor;" -DARWIN="echo '=== Building OSX binary ==='; GOOS=darwin GOARCH=amd64 CC=o64-clang CXX=o64-clang++ go build -o dist/stash-osx -ldflags \"$VERSION_FLAGS\" -tags extended -v -mod=vendor;" -LINUX="echo '=== Building Linux binary ==='; go build -o dist/stash-linux -ldflags \"$VERSION_FLAGS\" -v -mod=vendor;" -RASPPI="echo '=== Building Raspberry Pi binary ==='; GOOS=linux GOARCH=arm GOARM=5 CC=arm-linux-gnueabi-gcc go build -o dist/stash-pi -ldflags \"$VERSION_FLAGS\" -v -mod=vendor;" +SETENV="BUILD_DATE=\"$BUILD_DATE\" GITHASH=$GITHASH STASH_VERSION=\"$STASH_VERSION\"" +SETUP="export GO111MODULE=on; export CGO_ENABLED=1; set -e; echo '=== Running packr ==='; make packr;" +WINDOWS="echo '=== Building Windows binary ==='; $SETENV GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ LDFLAGS=\"-extldflags '-static' \" OUTPUT=\"dist/stash-win.exe\" make build-release;" +DARWIN="echo '=== Building OSX binary ==='; $SETENV GOOS=darwin GOARCH=amd64 CC=o64-clang CXX=o64-clang++ OUTPUT=\"dist/stash-osx\" make build-release;" +LINUX_AMD64="echo '=== Building Linux (amd64) binary ==='; $SETENV GOOS=linux GOARCH=amd64 OUTPUT=\"dist/stash-linux\" make build-release;" +LINUX_ARM64v8="echo '=== Building Linux (armv8/arm64) binary ==='; $SETENV GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc OUTPUT=\"dist/stash-linux-arm64v8\" make build-release;" +LINUX_ARM32v7="echo '=== Building Linux (armv7/armhf) binary ==='; $SETENV GOOS=linux GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc OUTPUT=\"dist/stash-linux-arm32v7\" make build-release;" +LINUX_ARM32v6="echo '=== Building Linux (armv6 | Raspberry Pi 1) binary ==='; $SETENV GOOS=linux GOARCH=arm GOARM=6 CC=arm-linux-gnueabi-gcc OUTPUT=\"dist/stash-pi\" make build-release;" -COMMAND="$SETUP $WINDOWS $DARWIN $LINUX $RASPPI" +COMMAND="$SETUP $WINDOWS $DARWIN $LINUX_AMD64 $LINUX_ARM64v8 $LINUX_ARM32v7 $LINUX_ARM32v6 echo '=== Build complete ==='" docker run --rm --mount type=bind,source="$(pwd)",target=/stash -w /stash stashapp/compiler:develop /bin/bash -c "$COMMAND" diff --git a/ui/v2.5/.stylelintrc b/ui/v2.5/.stylelintrc index a87318b34..5f5be26f0 100644 --- a/ui/v2.5/.stylelintrc +++ b/ui/v2.5/.stylelintrc @@ -85,7 +85,6 @@ "string-no-newline": true, "string-quotes": "double", "time-min-milliseconds": 100, - "unit-blacklist": ["em"], "value-list-comma-space-after": "always-single-line", "value-list-comma-space-before": "never", "value-no-vendor-prefix": true diff --git a/ui/v2.5/package.json b/ui/v2.5/package.json index 29f895d36..164a6834d 100644 --- a/ui/v2.5/package.json +++ b/ui/v2.5/package.json @@ -28,8 +28,10 @@ "@apollo/react-hooks": "^3.1.5", "@formatjs/intl-numberformat": "^4.2.1", "@fortawesome/fontawesome-svg-core": "^1.2.28", + "@fortawesome/free-regular-svg-icons": "^5.13.0", "@fortawesome/free-solid-svg-icons": "^5.13.0", "@fortawesome/react-fontawesome": "^0.1.9", + "@types/mousetrap": "^1.6.3", "apollo-cache": "^1.3.4", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", @@ -49,6 +51,7 @@ "jimp": "^0.12.1", "localforage": "1.7.3", "lodash": "^4.17.15", + "mousetrap": "^1.6.5", "query-string": "6.12.1", "react": "16.13.1", "react-apollo": "^3.1.5", diff --git a/ui/v2.5/public/jwplayer/jwplayer.controls.js b/ui/v2.5/public/jwplayer/jwplayer.controls.js index eb88b0783..56513ccb8 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.controls.js +++ b/ui/v2.5/public/jwplayer/jwplayer.controls.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * diff --git a/ui/v2.5/public/jwplayer/jwplayer.core.controls.html5.js b/ui/v2.5/public/jwplayer/jwplayer.core.controls.html5.js index 02b9604a1..962b1143f 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.core.controls.html5.js +++ b/ui/v2.5/public/jwplayer/jwplayer.core.controls.html5.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * @@ -92,4 +92,4 @@ COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQ The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders. */ -(window.webpackJsonpjwplayer=window.webpackJsonpjwplayer||[]).push([[4,1,2,3,9],[,,,,,,,,,,,,,,,,,function(e,t,i){"use strict";i.r(t);var n,o=i(8),a=i(3),r=i(7),s=i(43),l=i(5),c=i(15),u=i(40);function d(e){return n||(n=new DOMParser),Object(l.r)(Object(l.s)(n.parseFromString(e,"image/svg+xml").documentElement))}var p=function(e,t,i,n){var o=document.createElement("div");o.className="jw-icon jw-icon-inline jw-button-color jw-reset "+e,o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i&&o.setAttribute("aria-label",i),o.style.display="none";var a=new u.a(o).on("click tap enter",t||function(){});return n&&Array.prototype.forEach.call(n,(function(e){"string"==typeof e?o.appendChild(d(e)):o.appendChild(e)})),{ui:a,element:function(){return o},toggle:function(e){e?this.show():this.hide()},show:function(){o.style.display=""},hide:function(){o.style.display="none"}}},w=i(0),h=i(71),f=i.n(h),g=i(72),j=i.n(g),b=i(73),m=i.n(b),v=i(74),y=i.n(v),k=i(75),x=i.n(k),T=i(76),O=i.n(T),C=i(77),M=i.n(C),_=i(78),S=i.n(_),E=i(79),A=i.n(E),P=i(80),z=i.n(P),L=i(81),B=i.n(L),I=i(82),R=i.n(I),V=i(83),N=i.n(V),H=i(84),F=i.n(H),D=i(85),q=i.n(D),U=i(86),W=i.n(U),Q=i(62),Y=i.n(Q),X=i(87),K=i.n(X),J=i(88),Z=i.n(J),G=i(89),$=i.n(G),ee=i(90),te=i.n(ee),ie=i(91),ne=i.n(ie),oe=i(92),ae=i.n(oe),re=i(93),se=i.n(re),le=i(94),ce=i.n(le),ue=null;function de(e){var t=fe().querySelector(we(e));if(t)return he(t);throw new Error("Icon not found "+e)}function pe(e){var t=fe().querySelectorAll(e.split(",").map(we).join(","));if(!t.length)throw new Error("Icons not found "+e);return Array.prototype.map.call(t,(function(e){return he(e)}))}function we(e){return".jw-svg-icon-".concat(e)}function he(e){return e.cloneNode(!0)}function fe(){return ue||(ue=d(""+f.a+j.a+m.a+y.a+x.a+O.a+M.a+S.a+A.a+z.a+B.a+R.a+N.a+F.a+q.a+W.a+Y.a+K.a+Z.a+$.a+te.a+ne.a+ae.a+se.a+ce.a+"")),ue}var ge=i(10);function je(e,t){for(var i=0;i10&&delete be[t[0]];var i=d(e);be[e]=i}return be[e].cloneNode(!0)}(t):((r=document.createElement("div")).className="jw-icon jw-button-image jw-button-color jw-reset",t&&Object(ge.d)(r,{backgroundImage:"url(".concat(t,")")})),s.appendChild(r),new u.a(s).on("click tap enter",n,this),s.addEventListener("mousedown",(function(e){e.preventDefault()})),this.id=o,this.buttonElement=s}var t,i,n;return t=e,(i=[{key:"element",value:function(){return this.buttonElement}},{key:"toggle",value:function(e){e?this.show():this.hide()}},{key:"show",value:function(){this.buttonElement.style.display=""}},{key:"hide",value:function(){this.buttonElement.style.display="none"}}])&&je(t.prototype,i),n&&je(t,n),e}(),ve=i(11);function ye(e,t){for(var i=0;i=0&&(t.left-=i,t.right-=i),t},xe=function(){function e(t,i){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),Object(w.g)(this,r.a),this.className=t+" jw-background-color jw-reset",this.orientation=i}var t,i,n;return t=e,(i=[{key:"setup",value:function(){this.el=Object(l.e)(function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return''}(this.className,"jw-slider-"+this.orientation)),this.elementRail=this.el.getElementsByClassName("jw-slider-container")[0],this.elementBuffer=this.el.getElementsByClassName("jw-buffer")[0],this.elementProgress=this.el.getElementsByClassName("jw-progress")[0],this.elementThumb=this.el.getElementsByClassName("jw-knob")[0],this.ui=new u.a(this.element(),{preventScrolling:!0}).on("dragStart",this.dragStart,this).on("drag",this.dragMove,this).on("dragEnd",this.dragEnd,this).on("click tap",this.tap,this)}},{key:"dragStart",value:function(){this.trigger("dragStart"),this.railBounds=ke(this.elementRail)}},{key:"dragEnd",value:function(e){this.dragMove(e),this.trigger("dragEnd")}},{key:"dragMove",value:function(e){var t,i,n=this.railBounds=this.railBounds?this.railBounds:ke(this.elementRail);return i="horizontal"===this.orientation?(t=e.pageX)n.right?100:100*Object(s.a)((t-n.left)/n.width,0,1):(t=e.pageY)>=n.bottom?0:t<=n.top?100:100*Object(s.a)((n.height-(t-n.top))/n.height,0,1),this.render(i),this.update(i),!1}},{key:"tap",value:function(e){this.railBounds=ke(this.elementRail),this.dragMove(e)}},{key:"limit",value:function(e){return e}},{key:"update",value:function(e){this.trigger("update",{percentage:e})}},{key:"render",value:function(e){e=Math.max(0,Math.min(e,100)),"horizontal"===this.orientation?(this.elementThumb.style.left=e+"%",this.elementProgress.style.width=e+"%"):(this.elementThumb.style.bottom=e+"%",this.elementProgress.style.height=e+"%")}},{key:"updateBuffer",value:function(e){this.elementBuffer.style.width=e+"%"}},{key:"element",value:function(){return this.el}}])&&ye(t.prototype,i),n&&ye(t,n),e}(),Te=function(e,t){e&&t&&(e.setAttribute("aria-label",t),e.setAttribute("role","button"),e.setAttribute("tabindex","0"))};function Oe(e,t){for(var i=0;i0&&Array.prototype.forEach.call(o,(function(e){"string"==typeof e?a.el.appendChild(d(e)):a.el.appendChild(e)}))}var t,i,n;return t=e,(i=[{key:"addContent",value:function(e){this.content&&this.removeContent(),this.content=e,this.tooltip.appendChild(e)}},{key:"removeContent",value:function(){this.content&&(this.tooltip.removeChild(this.content),this.content=null)}},{key:"hasContent",value:function(){return!!this.content}},{key:"element",value:function(){return this.el}},{key:"openTooltip",value:function(e){this.isOpen||(this.trigger("open-"+this.componentType,e,{isOpen:!0}),this.isOpen=!0,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"closeTooltip",value:function(e){this.isOpen&&(this.trigger("close-"+this.componentType,e,{isOpen:!1}),this.isOpen=!1,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"toggleOpenState",value:function(e){this.isOpen?this.closeTooltip(e):this.openTooltip(e)}}])&&Oe(t.prototype,i),n&&Oe(t,n),e}(),Me=i(22),_e=i(57);function Se(e,t){for(var i=0;i=this.thumbnails.length&&(t=this.thumbnails.length-1);var i=this.thumbnails[t].img;return i.indexOf("://")<0&&(i=this.vttPath?this.vttPath+"/"+i:i),i},loadThumbnail:function(e){var t=this.chooseThumbnail(e),i={margin:"0 auto",backgroundPosition:"0 0"};if(t.indexOf("#xywh")>0)try{var n=/(.+)#xywh=(\d+),(\d+),(\d+),(\d+)/.exec(t);t=n[1],i.backgroundPosition=-1*n[2]+"px "+-1*n[3]+"px",i.width=n[4],this.timeTip.setWidth(+i.width),i.height=n[5]}catch(e){return}else this.individualImage||(this.individualImage=new Image,this.individualImage.onload=Object(w.a)((function(){this.individualImage.onload=null,this.timeTip.image({width:this.individualImage.width,height:this.individualImage.height}),this.timeTip.setWidth(this.individualImage.width)}),this),this.individualImage.src=t);return i.backgroundImage='url("'+t+'")',i},showThumbnail:function(e){this._model.get("containerWidth")<=420||this.thumbnails.length<1||this.timeTip.image(this.loadThumbnail(e))},resetThumbnails:function(){this.timeTip.image({backgroundImage:"",width:0,height:0}),this.thumbnails=[]}};function Le(e,t,i){return(Le="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,i){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=He(e)););return e}(e,t);if(n){var o=Object.getOwnPropertyDescriptor(n,t);return o.get?o.get.call(i):o.value}})(e,t,i||e)}function Be(e){return(Be="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ie(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Re(e,t){for(var i=0;i-1&&(n="Live")}var d=this.timeTip;d.update(n),this.textLength!==n.length&&(this.textLength=n.length,d.resetWidth()),this.showThumbnail(u),Object(l.a)(d.el,"jw-open");var p=d.getWidth(),w=a.width/100,h=o-a.width,f=0;p>h&&(f=(p-h)/(200*w));var g=100*Math.min(1-f,Math.max(f,c)).toFixed(3);Object(ge.d)(d.el,{left:g+"%"})}}},{key:"hideTimeTooltip",value:function(){Object(l.o)(this.timeTip.el,"jw-open")}},{key:"updateCues",value:function(e,t){var i=this;this.resetCues(),t&&t.length&&(t.forEach((function(e){i.addCue(e)})),this.drawCues())}},{key:"updateAriaText",value:function(){var e=this._model;if(!e.get("seeking")){var t=e.get("position"),i=e.get("duration"),n=Object(ve.timeFormat)(t);"DVR"!==this.streamType&&(n+=" of ".concat(Object(ve.timeFormat)(i)));var o=this.el;document.activeElement!==o&&(this.timeUpdateKeeper.textContent=n),Object(l.t)(o,"aria-valuenow",t),Object(l.t)(o,"aria-valuetext",n)}}},{key:"reset",value:function(){this.resetThumbnails(),this.timeTip.resetWidth(),this.textLength=0}}]),t}(xe);Object(w.g)(Ue.prototype,Ae,ze);var We=Ue;function Qe(e,t){for(var i=0;i=75&&!e),Object(l.t)(r,"aria-valuenow",o),Object(l.t)(s,"aria-valuenow",o);var c="Volume ".concat(o,"%");Object(l.t)(r,"aria-valuetext",c),Object(l.t)(s,"aria-valuetext",c),document.activeElement!==r&&document.activeElement!==s&&(this._volumeAnnouncer.textContent=c)}}},{key:"onCastAvailable",value:function(e,t){this.elements.cast.toggle(t)}},{key:"onCastActive",value:function(e,t){this.elements.fullscreen.toggle(!t),this.elements.cast.button&&Object(l.v)(this.elements.cast.button,"jw-off",!t)}},{key:"onElapsed",value:function(e,t){var i,n,o=e.get("duration");if("DVR"===e.get("streamType")){var a=Math.ceil(t),r=this._model.get("dvrSeekLimit");i=n=a>=-r?"":"-"+Object(ve.timeFormat)(-(t+r)),e.set("dvrLive",a>=-r)}else i=Object(ve.timeFormat)(t),n=Object(ve.timeFormat)(o-t);this.elements.elapsed.textContent=i,this.elements.countdown.textContent=n}},{key:"onDuration",value:function(e,t){this.elements.duration.textContent=Object(ve.timeFormat)(Math.abs(t))}},{key:"onAudioMode",value:function(e,t){var i=this.elements.time.element();t?this.elements.buttonContainer.insertBefore(i,this.elements.elapsed):Object(l.m)(this.el,i)}},{key:"element",value:function(){return this.el}},{key:"setAltText",value:function(e,t){this.elements.alt.textContent=t}},{key:"closeMenus",value:function(e){this.menus.forEach((function(t){e&&e.target===t.el||t.closeTooltip(e)}))}},{key:"rewind",value:function(){var e,t=0,i=this._model.get("currentTime");i?e=i-10:(e=this._model.get("position")-10,"DVR"===this._model.get("streamType")&&(t=this._model.get("duration"))),this._api.seek(Math.max(e,t),{reason:"interaction"})}},{key:"onState",value:function(e,t){var i=e.get("localization"),n=i.play;this.setPlayText(n),t===a.pb&&("LIVE"!==e.get("streamType")?(n=i.pause,this.setPlayText(n)):(n=i.stop,this.setPlayText(n))),Object(l.t)(this.elements.play.element(),"aria-label",n)}},{key:"onStreamTypeChange",value:function(e,t){var i="LIVE"===t,n="DVR"===t;this.elements.rewind.toggle(!i),this.elements.live.toggle(i||n),Object(l.t)(this.elements.live.element(),"tabindex",i?"-1":"0"),this.elements.duration.style.display=n?"none":"",this.onDuration(e,e.get("duration")),this.onState(e,e.get("state"))}},{key:"addLogo",value:function(e){var t=this.elements.buttonContainer,i=new me(e.file,this._model.get("localization").logo,(function(){e.link&&Object(l.l)(e.link,"_blank",{rel:"noreferrer"})}),"logo","jw-logo-button");e.link||Object(l.t)(i.element(),"tabindex","-1"),t.insertBefore(i.element(),t.querySelector(".jw-spacer").nextSibling)}},{key:"goToLiveEdge",value:function(){if("DVR"===this._model.get("streamType")){var e=Math.min(this._model.get("position"),-1),t=this._model.get("dvrSeekLimit");this._api.seek(Math.max(-t,e),{reason:"interaction"}),this._api.play({reason:"interaction"})}}},{key:"updateButtons",value:function(e,t,i){if(t){var n,o,a=this.elements.buttonContainer;t!==i&&i?(n=ct(t,i),o=ct(i,t),this.removeButtons(a,o)):n=t;for(var r=n.length-1;r>=0;r--){var s=n[r],l=new me(s.img,s.tooltip,s.callback,s.id,s.btnClass);s.tooltip&&nt(l.element(),s.id,s.tooltip);var c=void 0;"related"===l.id?c=this.elements.settingsButton.element():"share"===l.id?c=a.querySelector('[button="related"]')||this.elements.settingsButton.element():(c=this.elements.spacer.nextSibling)&&"logo"===c.getAttribute("button")&&(c=c.nextSibling),a.insertBefore(l.element(),c)}}}},{key:"removeButtons",value:function(e,t){for(var i=t.length;i--;){var n=e.querySelector('[button="'.concat(t[i].id,'"]'));n&&e.removeChild(n)}}},{key:"toggleCaptionsButtonState",value:function(e){var t=this.elements.captionsButton;t&&Object(l.v)(t.element(),"jw-off",!e)}},{key:"destroy",value:function(){var e=this;this._model.off(null,null,this),Object.keys(this.elements).forEach((function(t){var i=e.elements[t];i&&"function"==typeof i.destroy&&e.elements[t].destroy()})),this.ui.forEach((function(e){e.destroy()})),this.ui=[]}}])&&at(t.prototype,i),n&&at(t,n),e}(),pt=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return'
')+'
')+"
"},wt=function(e){return'
'+pt("rewind",e.rewind)+pt("display",e.playback)+pt("next",e.next)+"
"};function ht(e,t){for(var i=0;i'.concat(a.playback,"
")),Object(l.a)(o.icon,"jw-idle-label"),o.icon.appendChild(s))}return o}var i,n,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&vt(e,t)}(t,e),i=t,(n=[{key:"element",value:function(){return this.el}}])&&jt(i.prototype,n),o&&jt(i,o),t}(r.a);function kt(e,t){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";return'
'+'
'.concat(e,"
")+'
'.concat(t,"
")+'
'.concat(i,"
")+"
"+'')+"
"}());t.querySelector(".jw-nextup-close").appendChild(de("close")),this.addContent(t),this.closeButton=this.content.querySelector(".jw-nextup-close"),this.closeButton.setAttribute("aria-label",this.localization.close),this.tooltip=this.content.querySelector(".jw-nextup-tooltip");var i=this._model,n=i.player;this.enabled=!1,i.on("change:nextUp",this.onNextUp,this),n.change("duration",this.onDuration,this),n.change("position",this.onElapsed,this),n.change("streamType",this.onStreamType,this),n.change("state",(function(e,t){"complete"===t&&this.toggle(!1)}),this),this.closeUi=new u.a(this.closeButton,{directSelect:!0}).on("click tap enter",(function(){this.nextUpSticky=!1,this.toggle(!1)}),this),this.tooltipUi=new u.a(this.tooltip).on("click tap",this.click,this)}},{key:"loadThumbnail",value:function(e){return this.nextUpImage=new Image,this.nextUpImage.onload=function(){this.nextUpImage.onload=null}.bind(this),this.nextUpImage.src=e,{backgroundImage:'url("'+e+'")'}}},{key:"click",value:function(){var e=this.feedShownId;this.reset(),this._api.next({feedShownId:e,reason:"interaction"})}},{key:"toggle",value:function(e,t){if(this.enabled&&(Object(l.v)(this.container,"jw-nextup-sticky",!!this.nextUpSticky),this.shown!==e)){this.shown=e,Object(l.v)(this.container,"jw-nextup-container-visible",e),Object(l.v)(this._playerElement,"jw-flag-nextup",e);var i=this._model.get("nextUp");e&&i?(this.feedShownId=Object(ot.b)(ot.a),this.trigger("nextShown",{mode:i.mode,ui:"nextup",itemsShown:[i],feedData:i.feedData,reason:t,feedShownId:this.feedShownId})):this.feedShownId=""}}},{key:"setNextUpItem",value:function(e){var t=this;setTimeout((function(){if(t.thumbnail=t.content.querySelector(".jw-nextup-thumbnail"),Object(l.v)(t.content,"jw-nextup-thumbnail-visible",!!e.image),e.image){var i=t.loadThumbnail(e.image);Object(ge.d)(t.thumbnail,i)}t.header=t.content.querySelector(".jw-nextup-header"),t.header.textContent=Object(l.e)(t.localization.nextUp).textContent,t.title=t.content.querySelector(".jw-nextup-title");var n=e.title;t.title.textContent=n?Object(l.e)(n).textContent:"";var o=e.duration;o&&(t.duration=t.content.querySelector(".jw-nextup-duration"),t.duration.textContent="number"==typeof o?Object(ve.timeFormat)(o):o)}),500)}},{key:"onNextUp",value:function(e,t){this.reset(),t||(t={showNextUp:!1}),this.enabled=!(!t.title&&!t.image),this.enabled&&(t.showNextUp||(this.nextUpSticky=!1,this.toggle(!1)),this.setNextUpItem(t))}},{key:"onDuration",value:function(e,t){if(t){var i=e.get("nextupoffset"),n=-10;i&&(n=Object(Mt.d)(i,t)),n<0&&(n+=t),Object(Mt.c)(i)&&t-5=this.offset;n&&void 0===i?(this.nextUpSticky=n,this.toggle(n,"time")):!n&&i&&this.reset()}}},{key:"onStreamType",value:function(e,t){"VOD"!==t&&(this.nextUpSticky=!1,this.toggle(!1))}},{key:"element",value:function(){return this.container}},{key:"addContent",value:function(e){this.content&&this.removeContent(),this.content=e,this.container.appendChild(e)}},{key:"removeContent",value:function(){this.content&&(this.container.removeChild(this.content),this.content=null)}},{key:"reset",value:function(){this.nextUpSticky=void 0,this.toggle(!1)}},{key:"destroy",value:function(){this.off(),this._model.off(null,null,this),this.closeUi&&this.closeUi.destroy(),this.tooltipUi&&this.tooltipUi.destroy()}}])&&_t(t.prototype,i),n&&_t(t,n),e}(),Et=function(e,t){var i=e.featured,n=e.showLogo,o=e.type;return e.logo=n?'':"",'
  • ').concat(At[o](e,t),"
  • ")},At={link:function(e){var t=e.link,i=e.title,n=e.logo;return'').concat(n).concat(i||"","")},info:function(e,t){return'")},share:function(e,t){return'")},keyboardShortcuts:function(e,t){return'")}},Pt=i(23),zt=i(6),Lt=i(13);function Bt(e,t){for(var i=0;iJW Player '.concat(e,""),a={items:[{type:"info"},{title:Object(Lt.e)(n)?"".concat(o," ").concat(n):"".concat(n," ").concat(o),type:"link",featured:!0,showLogo:!0,link:"https://jwplayer.com/learn-more?e=".concat(It[i])}]},r=t.get("provider"),s=a.items;if(r&&r.name.indexOf("flash")>=0){var l="Flash Version "+Object(zt.a)();s.push({title:l,type:"link",link:"http://www.adobe.com/software/flash/about/"})}return this.shortcutsTooltip&&s.splice(s.length-1,0,{type:"keyboardShortcuts"}),a}},{key:"rightClick",value:function(e){if(this.lazySetup(),this.mouseOverContext)return!1;this.hideMenu(),this.showMenu(e),this.addHideMenuHandlers()}},{key:"getOffset",value:function(e){var t=Object(l.c)(this.wrapperElement),i=e.pageX-t.left,n=e.pageY-t.top;return this.model.get("touchMode")&&(n-=100),{x:i,y:n}}},{key:"showMenu",value:function(e){var t=this,i=this.getOffset(e);return this.el.style.left=i.x+"px",this.el.style.top=i.y+"px",this.outCount=0,Object(l.a)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.a)(this.el,"jw-open"),clearTimeout(this._menuTimeout),this._menuTimeout=setTimeout((function(){return t.hideMenu()}),3e3),!1}},{key:"hideMenu",value:function(e){e&&this.el&&this.el.contains(e.target)||(Object(l.o)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.o)(this.el,"jw-open"))}},{key:"lazySetup",value:function(){var e,t,i,n,o=this,a=(e=this.buildArray(),t=this.model.get("localization"),i=e.items,n=(void 0===i?[]:i).map((function(e){return Et(e,t)})),'
    '+'
      '.concat(n.join(""),"
    ")+"
    ");if(this.el){if(this.html!==a){this.html=a;var r=Rt(a);Object(l.h)(this.el);for(var s=r.childNodes.length;s--;)this.el.appendChild(r.firstChild)}}else this.html=a,this.el=Rt(this.html),this.wrapperElement.appendChild(this.el),this.hideMenuHandler=function(e){return o.hideMenu(e)},this.overHandler=function(){o.mouseOverContext=!0},this.outHandler=function(e){o.mouseOverContext=!1,e.relatedTarget&&!o.el.contains(e.relatedTarget)&&++o.outCount>1&&o.hideMenu()},this.infoOverlayHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.infoOverlay.open()},this.shortcutsTooltipHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.shortcutsTooltip.open()}}},{key:"setup",value:function(e,t,i){this.wrapperElement=i,this.model=e,this.mouseOverContext=!1,this.playerContainer=t,this.ui=new u.a(i).on("longPress",this.rightClick,this)}},{key:"addHideMenuHandlers",value:function(){this.removeHideMenuHandlers(),this.wrapperElement.addEventListener("touchstart",this.hideMenuHandler),document.addEventListener("touchstart",this.hideMenuHandler),o.OS.mobile||(this.wrapperElement.addEventListener("click",this.hideMenuHandler),document.addEventListener("click",this.hideMenuHandler),this.el.addEventListener("mouseover",this.overHandler),this.el.addEventListener("mouseout",this.outHandler)),this.el.querySelector(".jw-info-overlay-item").addEventListener("click",this.infoOverlayHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").addEventListener("click",this.shortcutsTooltipHandler)}},{key:"removeHideMenuHandlers",value:function(){this.wrapperElement&&(this.wrapperElement.removeEventListener("click",this.hideMenuHandler),this.wrapperElement.removeEventListener("touchstart",this.hideMenuHandler)),this.el&&(this.el.querySelector(".jw-info-overlay-item").removeEventListener("click",this.infoOverlayHandler),this.el.removeEventListener("mouseover",this.overHandler),this.el.removeEventListener("mouseout",this.outHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").removeEventListener("click",this.shortcutsTooltipHandler)),document.removeEventListener("click",this.hideMenuHandler),document.removeEventListener("touchstart",this.hideMenuHandler)}},{key:"destroy",value:function(){clearTimeout(this._menuTimeout),this.removeHideMenuHandlers(),this.el&&(this.hideMenu(),this.hideMenuHandler=null,this.el=null),this.wrapperElement&&(this.wrapperElement.oncontextmenu=null,this.wrapperElement=null),this.model&&(this.model=null),this.ui&&(this.ui.destroy(),this.ui=null)}}])&&Bt(t.prototype,i),n&&Bt(t,n),e}(),Nt=function(e){return'")},Ht=function(e){return'"},Ft=function(e){return'"};function Dt(e){return(Dt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function qt(e,t){return!t||"object"!==Dt(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function Ut(e){return(Ut=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Wt(e,t){return(Wt=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Qt(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Yt(e,t){for(var i=0;i2&&void 0!==arguments[2]?arguments[2]:Nt;Qt(this,e),this.el=Object(l.e)(n(t)),this.ui=new u.a(this.el).on("click tap enter",i,this)}return Xt(e,[{key:"destroy",value:function(){this.ui.destroy()}}]),e}(),Zt=function(e){function t(e,i){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Ft;return Qt(this,t),qt(this,Ut(t).call(this,e,i,n))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Wt(e,t)}(t,e),Xt(t,[{key:"activate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!0),this.el.setAttribute("aria-checked","true"),this.active=!0}},{key:"deactivate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!1),this.el.setAttribute("aria-checked","false"),this.active=!1}}]),t}(Jt),Gt=function(e,t){return e?'':''},$t=function(e,t){var i=e.name,n={captions:"cc-off",audioTracks:"audio-tracks",quality:"quality-100",playbackRates:"playback-rate"}[i];if(n||e.icon){var o=p("jw-settings-".concat(i," jw-submenu-").concat(i),(function(t){e.open(t)}),i,[e.icon&&Object(l.e)(e.icon)||de(n)]),a=o.element();return a.setAttribute("role","menuitemradio"),a.setAttribute("aria-checked","false"),a.setAttribute("aria-label",t),"ontouchstart"in window||(o.tooltip=nt(a,i,t)),o}};function ei(e){return(ei="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ti(e,t){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:Gt;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),a=this,(o=!(r=ii(t).call(this))||"object"!==ei(r)&&"function"!=typeof r?oi(a):r).open=o.open.bind(oi(oi(o))),o.close=o.close.bind(oi(oi(o))),o.toggle=o.toggle.bind(oi(oi(o))),o.onDocumentClick=o.onDocumentClick.bind(oi(oi(o))),o.name=e,o.isSubmenu=!!i,o.el=Object(l.e)(s(o.isSubmenu,e)),o.topbar=o.el.querySelector(".jw-".concat(o.name,"-topbar")),o.buttonContainer=o.el.querySelector(".jw-".concat(o.name,"-topbar-buttons")),o.children={},o.openMenus=[],o.items=[],o.visible=!1,o.parentMenu=i,o.mainMenu=o.parentMenu?o.parentMenu.mainMenu:oi(oi(o)),o.categoryButton=null,o.closeButton=o.parentMenu&&o.parentMenu.closeButton||o.createCloseButton(n),o.isSubmenu?(o.categoryButton=o.parentMenu.categoryButton||o.createCategoryButton(n),o.parentMenu.parentMenu&&!o.mainMenu.backButton&&(o.mainMenu.backButton=o.createBackButton(n)),o.itemsContainer=o.createItemsContainer(),o.parentMenu.appendMenu(oi(oi(o)))):o.ui=ri(oi(oi(o))),o}var i,n,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&ni(e,t)}(t,e),i=t,(n=[{key:"createItemsContainer",value:function(){var e,t,i=this,n=this.el.querySelector(".jw-settings-submenu-items"),o=new u.a(n),a=this.categoryButton&&this.categoryButton.element()||this.parentMenu.categoryButton&&this.parentMenu.categoryButton.element()||this.mainMenu.buttonContainer.firstChild;return this.parentMenu.isSubmenu&&(e=this.mainMenu.closeButton.element(),t=this.mainMenu.backButton.element()),o.on("keydown",(function(o){if(o.target.parentNode===n){var r=function(e,t){e?e.focus():void 0!==t&&n.childNodes[t].focus()},s=o.sourceEvent,c=s.target,u=n.firstChild===c,d=n.lastChild===c,p=i.topbar,w=e||Object(l.k)(a),h=t||Object(l.n)(a),f=Object(l.k)(s.target),g=Object(l.n)(s.target),j=s.key.replace(/(Arrow|ape)/,"");switch(j){case"Tab":r(s.shiftKey?h:w);break;case"Left":r(h||Object(l.n)(document.getElementsByClassName("jw-icon-settings")[0]));break;case"Up":p&&u?r(p.firstChild):r(g,n.childNodes.length-1);break;case"Right":r(w);break;case"Down":p&&d?r(p.firstChild):r(f,0)}s.preventDefault(),"Esc"!==j&&s.stopPropagation()}})),o}},{key:"createCloseButton",value:function(e){var t=p("jw-settings-close",this.close,e.close,[de("close")]);return this.topbar.appendChild(t.element()),t.show(),t.ui.on("keydown",(function(e){var t=e.sourceEvent,i=t.key.replace(/(Arrow|ape)/,"");("Enter"===i||"Right"===i||"Tab"===i&&!t.shiftKey)&&this.close(e)}),this),this.buttonContainer.appendChild(t.element()),t}},{key:"createCategoryButton",value:function(e){var t=e[{captions:"cc",audioTracks:"audioTracks",quality:"hd",playbackRates:"playbackRates"}[this.name]];"sharing"===this.name&&(t=e.sharing.heading);var i=$t(this,t);return i.element().setAttribute("name",this.name),i}},{key:"createBackButton",value:function(e){var t=p("jw-settings-back",(function(e){Kt&&Kt.open(e)}),e.close,[de("arrow-left")]);return Object(l.m)(this.mainMenu.topbar,t.element()),t}},{key:"createTopbar",value:function(){var e=Object(l.e)('
    ');return Object(l.m)(this.el,e),e}},{key:"createItems",value:function(e,t){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Zt,a=this.name,r=e.map((function(e,r){var s,l;switch(a){case"quality":s="Auto"===e.label&&0===r?"".concat(n.defaultText,' '):e.label;break;case"captions":s="Off"!==e.label&&"off"!==e.id||0!==r?e.label:n.defaultText;break;case"playbackRates":l=e,s=Object(Lt.e)(n.tooltipText)?"x"+e:e+"x";break;case"audioTracks":s=e.name}s||(s=e,"object"===ei(e)&&(s.options=n));var c=new o(s,function(e){c.active||(t(l||r),c.deactivate&&(i.items.filter((function(e){return!0===e.active})).forEach((function(e){e.deactivate()})),Kt?Kt.open(e):i.mainMenu.close(e)),c.activate&&c.activate())}.bind(i));return c}));return r}},{key:"setMenuItems",value:function(e,t){var i=this;e?(this.items=[],Object(l.h)(this.itemsContainer.el),e.forEach((function(e){i.items.push(e),i.itemsContainer.el.appendChild(e.el)})),t>-1&&e[t].activate(),this.categoryButton.show()):this.removeMenu()}},{key:"appendMenu",value:function(e){if(e){var t=e.el,i=e.name,n=e.categoryButton;if(this.children[i]=e,n){var o=this.mainMenu.buttonContainer,a=o.querySelector(".jw-settings-sharing"),r="quality"===i?o.firstChild:a||this.closeButton.element();o.insertBefore(n.element(),r)}this.mainMenu.el.appendChild(t)}}},{key:"removeMenu",value:function(e){if(!e)return this.parentMenu.removeMenu(this.name);var t=this.children[e];t&&(delete this.children[e],t.destroy())}},{key:"open",value:function(e){if(!this.visible||this.openMenus){var t;if(Kt=null,this.isSubmenu){var i=this.mainMenu,n=this.parentMenu,o=this.categoryButton;if(n.openMenus.length&&n.closeChildren(),o&&o.element().setAttribute("aria-checked","true"),n.isSubmenu){n.el.classList.remove("jw-settings-submenu-active"),i.topbar.classList.add("jw-nested-menu-open");var a=i.topbar.querySelector(".jw-settings-topbar-text");a.setAttribute("name",this.name),a.innerText=this.title||this.name,i.backButton.show(),Kt=this.parentMenu,t=this.topbar?this.topbar.firstChild:e&&"enter"===e.type?this.items[0].el:a}else i.topbar.classList.remove("jw-nested-menu-open"),i.backButton&&i.backButton.hide();this.el.classList.add("jw-settings-submenu-active"),n.openMenus.push(this.name),i.visible||(i.open(e),this.items&&e&&"enter"===e.type?t=this.topbar?this.topbar.firstChild.focus():this.items[0].el:o.tooltip&&(o.tooltip.suppress=!0,t=o.element())),this.openMenus.length&&this.closeChildren(),t&&t.focus(),this.el.scrollTop=0}else this.el.parentNode.classList.add("jw-settings-open"),this.trigger("menuVisibility",{visible:!0,evt:e}),document.addEventListener("click",this.onDocumentClick);this.visible=!0,this.el.setAttribute("aria-expanded","true")}}},{key:"close",value:function(e){var t=this;this.visible&&(this.visible=!1,this.el.setAttribute("aria-expanded","false"),this.isSubmenu?(this.el.classList.remove("jw-settings-submenu-active"),this.categoryButton.element().setAttribute("aria-checked","false"),this.parentMenu.openMenus=this.parentMenu.openMenus.filter((function(e){return e!==t.name})),!this.mainMenu.openMenus.length&&this.mainMenu.visible&&this.mainMenu.close(e)):(this.el.parentNode.classList.remove("jw-settings-open"),this.trigger("menuVisibility",{visible:!1,evt:e}),document.removeEventListener("click",this.onDocumentClick)),this.openMenus.length&&this.closeChildren())}},{key:"closeChildren",value:function(){var e=this;this.openMenus.forEach((function(t){var i=e.children[t];i&&i.close()}))}},{key:"toggle",value:function(e){this.visible?this.close(e):this.open(e)}},{key:"onDocumentClick",value:function(e){/jw-(settings|video|nextup-close|sharing-link|share-item)/.test(e.target.className)||this.close()}},{key:"destroy",value:function(){var e=this;if(document.removeEventListener("click",this.onDocumentClick),Object.keys(this.children).map((function(t){e.children[t].destroy()})),this.isSubmenu){this.parentMenu.name===this.mainMenu.name&&this.categoryButton&&(this.parentMenu.buttonContainer.removeChild(this.categoryButton.element()),this.categoryButton.ui.destroy()),this.itemsContainer&&this.itemsContainer.destroy();var t=this.parentMenu.openMenus,i=t.indexOf(this.name);t.length&&i>-1&&this.openMenus.splice(i,1),delete this.parentMenu}else this.ui.destroy();this.visible=!1,this.el.parentNode&&this.el.parentNode.removeChild(this.el)}},{key:"defaultChild",get:function(){var e=this.children,t=e.quality,i=e.captions,n=e.audioTracks,o=e.sharing,a=e.playbackRates;return t||i||n||o||a}}])&&ti(i.prototype,n),o&&ti(i,o),t}(r.a),ri=function(e){var t=e.closeButton,i=e.el;return new u.a(i).on("keydown",(function(i){var n=i.sourceEvent,o=i.target,a=Object(l.k)(o),r=Object(l.n)(o),s=n.key.replace(/(Arrow|ape)/,""),c=function(t){r?t||r.focus():e.close(i)};switch(s){case"Esc":e.close(i);break;case"Left":c();break;case"Right":a&&t.element()&&o!==t.element()&&a.focus();break;case"Tab":n.shiftKey&&c(!0);break;case"Up":case"Down":!function(){var t=e.children[o.getAttribute("name")];if(!t&&Kt&&(t=Kt.children[Kt.openMenus]),t)return t.open(i),void(t.topbar?t.topbar.firstChild.focus():t.items&&t.items.length&&t.items[0].el.focus());if(i.target.parentNode.classList.contains("jw-submenu-topbar")){var n=i.target.parentNode.parentNode.querySelector(".jw-settings-submenu-items");("Down"===s?n.childNodes[0]:n.childNodes[n.childNodes.length-1]).focus()}}()}if(n.stopPropagation(),/13|32|37|38|39|40/.test(n.keyCode))return n.preventDefault(),!1}))},si=i(59),li=function(e){return wi[e]},ci=function(e){for(var t,i=Object.keys(wi),n=0;n1;i.elements.settingsButton.toggle(c)};t.change("levels",(function(e,t){r(t)}),o);var s=function(e,i,n){var o=t.get("levels");if(o&&"Auto"===o[0].label&&i&&i.items.length){var a=i.items[0].el.querySelector(".jw-auto-label"),r=o[e.index]||{label:""};a.textContent=n?"":r.label}};t.on("change:visualQuality",(function(e,i){var n=o.children.quality;i&&n&&s(i.level,n,t.get("currentLevel"))})),t.on("change:currentLevel",(function(e,i){var n=o.children.quality,a=t.get("visualQuality");a&&n&&s(a.level,n,i)}),o),t.change("captionsList",(function(i,r){var s={defaultText:n.off},l=t.get("captionsIndex");a("captions",r,(function(t){return e.setCurrentCaptions(t)}),l,s);var c=o.children.captions;if(c&&!c.children.captionsSettings){c.topbar=c.topbar||c.createTopbar();var u=new ai("captionsSettings",c,n);u.title="Subtitle Settings";var d=new Jt("Settings",u.open);c.topbar.appendChild(d.el);var p=new Zt("Reset",(function(){t.set("captions",si.a),f()}));p.el.classList.add("jw-settings-reset");var h=t.get("captions"),f=function(){var e=[];pi.forEach((function(i){h&&h[i.propertyName]&&(i.defaultVal=i.getOption(h[i.propertyName]));var o=new ai(i.name,u,n),a=new Jt({label:i.name,value:i.defaultVal},o.open,Ht),r=o.createItems(i.options,(function(e){var n=a.el.querySelector(".jw-settings-content-item-value");!function(e,i){var n=t.get("captions"),o=e.propertyName,a=e.options&&e.options[i],r=e.getTypedValue(a),s=Object(w.g)({},n);s[o]=r,t.set("captions",s)}(i,e),n.innerText=i.options[e]}),null);o.setMenuItems(r,i.options.indexOf(i.defaultVal)||0),e.push(a)})),e.push(p),u.setMenuItems(e)};f()}}));var l=function(e,t){e&&t>-1&&e.items[t].activate()};t.change("captionsIndex",(function(e,t){var n=o.children.captions;n&&l(n,t),i.toggleCaptionsButtonState(!!t)}),o);var c=function(i){if(t.get("supportsPlaybackRate")&&"LIVE"!==t.get("streamType")&&t.get("playbackRateControls")){var r=i.indexOf(t.get("playbackRate")),s={tooltipText:n.playbackRates};a("playbackRates",i,(function(t){return e.setPlaybackRate(t)}),r,s)}else o.children.playbackRates&&o.removeMenu("playbackRates")};t.on("change:playbackRates",(function(e,t){c(t)}),o);var u=function(i){a("audioTracks",i,(function(t){return e.setCurrentAudioTrack(t)}),t.get("currentAudioTrack"))};return t.on("change:audioTracks",(function(e,t){u(t)}),o),t.on("change:playbackRate",(function(e,i){var n=t.get("playbackRates"),a=-1;n&&(a=n.indexOf(i)),l(o.children.playbackRates,a)}),o),t.on("change:currentAudioTrack",(function(e,t){o.children.audioTracks.items[t].activate()}),o),t.on("change:playlistItem",(function(){o.removeMenu("captions"),i.elements.captionsButton.hide(),o.visible&&o.close()}),o),t.on("change:playbackRateControls",(function(){c(t.get("playbackRates"))})),t.on("change:castActive",(function(e,i,n){i!==n&&(i?(o.removeMenu("audioTracks"),o.removeMenu("quality"),o.removeMenu("playbackRates")):(u(t.get("audioTracks")),r(t.get("levels")),c(t.get("playbackRates"))))}),o),t.on("change:streamType",(function(){c(t.get("playbackRates"))}),o),o},fi=i(58),gi=i(35),ji=i(12),bi=function(e,t,i,n){var o=Object(l.e)('
    '),r=!1,s=null,c=!1,u=function(e){/jw-info/.test(e.target.className)||w.close()},d=function(){var n,a,s,c,u,d=p("jw-info-close",(function(){w.close()}),t.get("localization").close,[de("close")]);d.show(),Object(l.m)(o,d.element()),a=o.querySelector(".jw-info-title"),s=o.querySelector(".jw-info-duration"),c=o.querySelector(".jw-info-description"),u=o.querySelector(".jw-info-clientid"),t.change("playlistItem",(function(e,t){var i=t.description,n=t.title;Object(l.q)(c,i||""),Object(l.q)(a,n||"Unknown Title")})),t.change("duration",(function(e,i){var n="";switch(t.get("streamType")){case"LIVE":n="Live";break;case"DVR":n="DVR";break;default:i&&(n=Object(ve.timeFormat)(i))}s.textContent=n}),w),u.textContent=(n=i.getPlugin("jwpsrv"))&&"function"==typeof n.doNotTrackUser&&n.doNotTrackUser()?"":"Client ID: ".concat(function(){try{return window.localStorage.jwplayerLocalId}catch(e){return"none"}}()),e.appendChild(o),r=!0};var w={open:function(){r||d(),document.addEventListener("click",u),c=!0;var e=t.get("state");e===a.pb&&i.pause("infoOverlayInteraction"),s=e,n(!0)},close:function(){document.removeEventListener("click",u),c=!1,t.get("state")===a.ob&&s===a.pb&&i.play("infoOverlayInteraction"),s=null,n(!1)},destroy:function(){this.close(),t.off(null,null,this)}};return Object.defineProperties(w,{visible:{enumerable:!0,get:function(){return c}}}),w};var mi=function(e,t,i){var n,o=!1,r=null,s=i.get("localization").shortcuts,c=Object(l.e)(function(e,t){var i=e.map((function(e){return'
    '+''.concat(e.description,"")+''.concat(e.key,"")+"
    "})).join("");return'
    ')+'Press shift question mark to access a list of keyboard shortcuts
    '+''.concat(t,"")+'
    '+"".concat(i)+"
    "}(function(e){var t=e.playPause,i=e.volumeToggle,n=e.fullscreenToggle,o=e.seekPercent,a=e.increaseVolume,r=e.decreaseVolume,s=e.seekForward,l=e.seekBackward;return[{key:e.spacebar,description:t},{key:"↑",description:a},{key:"↓",description:r},{key:"→",description:s},{key:"←",description:l},{key:"c",description:e.captionsToggle},{key:"f",description:n},{key:"m",description:i},{key:"0-9",description:o}]}(s),s.keyboardShortcuts)),d={reason:"settingsInteraction"},w=new u.a(c.querySelector(".jw-switch")),h=function(){w.el.setAttribute("aria-checked",i.get("enableShortcuts")),Object(l.a)(c,"jw-open"),r=i.get("state"),c.querySelector(".jw-shortcuts-close").focus(),document.addEventListener("click",g),o=!0,t.pause(d)},f=function(){Object(l.o)(c,"jw-open"),document.removeEventListener("click",g),e.focus(),o=!1,r===a.pb&&t.play(d)},g=function(e){/jw-shortcuts|jw-switch/.test(e.target.className)||f()},j=function(e){var t=e.currentTarget,n="true"!==t.getAttribute("aria-checked");t.setAttribute("aria-checked",n),i.set("enableShortcuts",n)};return n=p("jw-shortcuts-close",f,i.get("localization").close,[de("close")]),Object(l.m)(c,n.element()),n.show(),e.appendChild(c),w.on("click tap enter",j),{el:c,open:h,close:f,destroy:function(){f(),w.destroy()},toggleVisibility:function(){o?f():h()}}},vi=function(e){return'
    ')+"
    "};function yi(e){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ki(e,t){for(var i=0;i16?n.activeTimeout=setTimeout(n.userInactiveTimeout,e):n.playerContainer.querySelector(".jw-tab-focus")?n.resetActiveTimeout():n.userInactive()},n}var i,n,r;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Ai(e,t)}(t,e),i=t,(n=[{key:"resetActiveTimeout",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.inactiveTime=0}},{key:"enable",value:function(e,t){var i=this,n=this.context.createElement("div");n.className="jw-controls jw-reset",this.div=n;var r=this.context.createElement("div");r.className="jw-controls-backdrop jw-reset",this.backdrop=r,this.logo=this.playerContainer.querySelector(".jw-logo");var c=t.get("touchMode"),u=function(){(t.get("isFloating")?i.wrapperElement:i.playerContainer).focus()};if(!this.displayContainer){var d=new Ot(t,e);d.buttons.display.on("click tap enter",(function(){i.trigger(a.p),i.userActive(1e3),e.playToggle(Li()),u()})),this.div.appendChild(d.element()),this.displayContainer=d}this.infoOverlay=new bi(n,t,e,(function(e){Object(l.v)(i.div,"jw-info-open",e),e&&i.div.querySelector(".jw-info-close").focus()})),o.OS.mobile||(this.shortcutsTooltip=new mi(this.wrapperElement,e,t)),this.rightClickMenu=new Vt(this.infoOverlay,this.shortcutsTooltip),c?(Object(l.a)(this.playerContainer,"jw-flag-touch"),this.rightClickMenu.setup(t,this.playerContainer,this.wrapperElement)):t.change("flashBlocked",(function(e,t){t?i.rightClickMenu.destroy():i.rightClickMenu.setup(e,i.playerContainer,i.wrapperElement)}),this);var w=t.get("floating");if(w){var h=new Ci(n,t.get("localization").close);h.on(a.sb,(function(){return i.trigger("dismissFloating",{doNotForward:!0})})),!1!==w.dismissible&&Object(l.a)(this.playerContainer,"jw-floating-dismissible")}var f=this.controlbar=new dt(e,t,this.playerContainer.querySelector(".jw-hidden-accessibility"));if(f.on(a.sb,(function(){return i.userActive()})),f.on("nextShown",(function(e){this.trigger("nextShown",e)}),this),f.on("adjustVolume",k,this),t.get("nextUpDisplay")&&!f.nextUpToolTip){var g=new St(t,e,this.playerContainer);g.on("all",this.trigger,this),g.setup(this.context),f.nextUpToolTip=g,this.div.appendChild(g.element())}this.div.appendChild(f.element());var j=t.get("localization"),b=this.settingsMenu=hi(e,t.player,this.controlbar,j),m=null;this.controlbar.on("menuVisibility",(function(n){var o=n.visible,r=n.evt,s=t.get("state"),l={reason:"settingsInteraction"},c=i.controlbar.elements.settingsButton,d="keydown"===(r&&r.sourceEvent||r||{}).type,p=o||d?0:Pi;i.userActive(p),m=s,Object(fi.a)(t.get("containerWidth"))<2&&(o&&s===a.pb?e.pause(l):o||s!==a.ob||m!==a.pb||e.play(l)),!o&&d&&c?c.element().focus():r&&u()})),b.on("menuVisibility",(function(e){return i.controlbar.trigger("menuVisibility",e)})),this.controlbar.on("settingsInteraction",(function(e,t,i){if(t)return b.defaultChild.toggle(i);b.children[e].toggle(i)})),o.OS.mobile?this.div.appendChild(b.el):(this.playerContainer.setAttribute("aria-describedby","jw-shortcuts-tooltip-explanation"),this.div.insertBefore(b.el,f.element()));var v=function(t){if(t.get("autostartMuted")){var n=function(){return i.unmuteAutoplay(e,t)},a=function(e,t){t||n()};o.OS.mobile&&(i.mute=p("jw-autostart-mute jw-off",n,t.get("localization").unmute,[de("volume-0")]),i.mute.show(),i.div.appendChild(i.mute.element())),f.renderVolume(!0,t.get("volume")),Object(l.a)(i.playerContainer,"jw-flag-autostart"),t.on("change:autostartFailed",n,i),t.on("change:autostartMuted change:mute",a,i),i.muteChangeCallback=a,i.unmuteCallback=n}};function y(i){var n=0,o=t.get("duration"),a=t.get("position");if("DVR"===t.get("streamType")){var r=t.get("dvrSeekLimit");n=o,o=Math.max(a,-r)}var l=Object(s.a)(a+i,n,o);e.seek(l,Li())}function k(i){var n=Object(s.a)(t.get("volume")+i,0,100);e.setVolume(n)}t.once("change:autostartMuted",v),v(t);var x=function(n){if(n.ctrlKey||n.metaKey)return!0;var o=!i.settingsMenu.visible,a=!0===t.get("enableShortcuts"),r=i.instreamState;if(a||-1!==zi.indexOf(n.keyCode)){switch(n.keyCode){case 27:if(t.get("fullscreen"))e.setFullscreen(!1),i.playerContainer.blur(),i.userInactive();else{var s=e.getPlugin("related");s&&s.close({type:"escape"})}i.rightClickMenu.el&&i.rightClickMenu.hideMenuHandler(),i.infoOverlay.visible&&i.infoOverlay.close(),i.shortcutsTooltip&&i.shortcutsTooltip.close();break;case 13:case 32:if(document.activeElement.classList.contains("jw-switch")&&13===n.keyCode)return!0;e.playToggle(Li());break;case 37:!r&&o&&y(-5);break;case 39:!r&&o&&y(5);break;case 38:o&&k(10);break;case 40:o&&k(-10);break;case 67:var l=e.getCaptionsList().length;if(l){var c=(e.getCurrentCaptions()+1)%l;e.setCurrentCaptions(c)}break;case 77:e.setMute();break;case 70:e.setFullscreen();break;case 191:i.shortcutsTooltip&&i.shortcutsTooltip.toggleVisibility();break;default:if(n.keyCode>=48&&n.keyCode<=59){var u=(n.keyCode-48)/10*t.get("duration");e.seek(u,Li())}}return/13|32|37|38|39|40/.test(n.keyCode)?(n.preventDefault(),!1):void 0}};this.playerContainer.addEventListener("keydown",x),this.keydownCallback=x;var T=function(e){switch(e.keyCode){case 9:var t=i.playerContainer.contains(e.target)?0:Pi;i.userActive(t);break;case 32:e.preventDefault()}};this.playerContainer.addEventListener("keyup",T),this.keyupCallback=T;var O=function(e){var t=e.relatedTarget||document.querySelector(":focus");t&&(i.playerContainer.contains(t)||i.userInactive())};this.playerContainer.addEventListener("blur",O,!0),this.blurCallback=O;var C=function e(){"jw-shortcuts-tooltip-explanation"===i.playerContainer.getAttribute("aria-describedby")&&i.playerContainer.removeAttribute("aria-describedby"),i.playerContainer.removeEventListener("blur",e,!0)};this.shortcutsTooltip&&(this.playerContainer.addEventListener("blur",C,!0),this.onRemoveShortcutsDescription=C),this.userActive(),this.addControls(),this.addBackdrop(),t.set("controlsEnabled",!0)}},{key:"addControls",value:function(){this.wrapperElement.appendChild(this.div)}},{key:"disable",value:function(e){var t=this.nextUpToolTip,i=this.settingsMenu,n=this.infoOverlay,o=this.controlbar,a=this.rightClickMenu,r=this.shortcutsTooltip,s=this.playerContainer,c=this.div;clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.off(),e.off(null,null,this),e.set("controlsEnabled",!1),c.parentNode&&(Object(l.o)(s,"jw-flag-touch"),c.parentNode.removeChild(c)),o&&o.destroy(),a&&a.destroy(),this.keydownCallback&&s.removeEventListener("keydown",this.keydownCallback),this.keyupCallback&&s.removeEventListener("keyup",this.keyupCallback),this.blurCallback&&s.removeEventListener("blur",this.blurCallback),this.onRemoveShortcutsDescription&&s.removeEventListener("blur",this.onRemoveShortcutsDescription),this.displayContainer&&this.displayContainer.destroy(),t&&t.destroy(),i&&i.destroy(),n&&n.destroy(),r&&r.destroy(),this.removeBackdrop()}},{key:"controlbarHeight",value:function(){return this.dimensions.cbHeight||(this.dimensions.cbHeight=this.controlbar.element().clientHeight),this.dimensions.cbHeight}},{key:"element",value:function(){return this.div}},{key:"resize",value:function(){this.dimensions={}}},{key:"unmuteAutoplay",value:function(e,t){var i=!t.get("autostartFailed"),n=t.get("mute");i?n=!1:t.set("playOnViewable",!1),this.muteChangeCallback&&(t.off("change:autostartMuted change:mute",this.muteChangeCallback),this.muteChangeCallback=null),this.unmuteCallback&&(t.off("change:autostartFailed",this.unmuteCallback),this.unmuteCallback=null),t.set("autostartFailed",void 0),t.set("autostartMuted",void 0),e.setMute(n),this.controlbar.renderVolume(n,t.get("volume")),this.mute&&this.mute.hide(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.userActive()}},{key:"mouseMove",value:function(e){var t=this.controlbar.element().contains(e.target),i=this.controlbar.nextUpToolTip&&this.controlbar.nextUpToolTip.element().contains(e.target),n=this.logo&&this.logo.contains(e.target),o=t||i||n?0:Pi;this.userActive(o)}},{key:"userActive",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Pi;e>0?(this.inactiveTime=Object(c.a)()+e,-1===this.activeTimeout&&(this.activeTimeout=setTimeout(this.userInactiveTimeout,e))):this.resetActiveTimeout(),this.showing||(Object(l.o)(this.playerContainer,"jw-flag-user-inactive"),this.showing=!0,this.trigger("userActive"))}},{key:"userInactive",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.settingsMenu.visible||(this.inactiveTime=0,this.showing=!1,Object(l.a)(this.playerContainer,"jw-flag-user-inactive"),this.trigger("userInactive"))}},{key:"addBackdrop",value:function(){var e=this.instreamState?this.div:this.wrapperElement.querySelector(".jw-captions");this.wrapperElement.insertBefore(this.backdrop,e)}},{key:"removeBackdrop",value:function(){var e=this.backdrop.parentNode;e&&e.removeChild(this.backdrop)}},{key:"setupInstream",value:function(){this.instreamState=!0,this.userActive(),this.addBackdrop(),this.settingsMenu&&this.settingsMenu.close(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","-1")}},{key:"destroyInstream",value:function(e){this.instreamState=null,this.addBackdrop(),e.get("autostartMuted")&&Object(l.a)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","0")}}])&&_i(i.prototype,n),r&&_i(i,r),t}(r.a)},function(e,t,i){"use strict";i.r(t);var n=i(0),o=i(12),a=i(50),r=i(36);var s=i(44),l=i(51),c=i(26),u=i(25),d=i(3),p=i(46),w=i(2),h=i(7),f=i(34);function g(e){var t=!1;return{async:function(){var i=this,n=arguments;return Promise.resolve().then((function(){if(!t)return e.apply(i,n)}))},cancel:function(){t=!0},cancelled:function(){return t}}}var j=i(1);function b(e){return function(t,i){var o=e.mediaModel,a=Object(n.g)({},i,{type:t});switch(t){case d.T:if(o.get(d.T)===i.mediaType)return;o.set(d.T,i.mediaType);break;case d.U:return void o.set(d.U,Object(n.g)({},i));case d.M:if(i[t]===e.model.getMute())return;break;case d.bb:i.newstate===d.mb&&(e.thenPlayPromise.cancel(),o.srcReset());var r=o.attributes.mediaState;o.attributes.mediaState=i.newstate,o.trigger("change:mediaState",o,i.newstate,r);break;case d.F:return e.beforeComplete=!0,e.trigger(d.B,a),void(e.attached&&!e.background&&e._playbackComplete());case d.G:o.get("setup")?(e.thenPlayPromise.cancel(),o.srcReset()):(t=d.tb,a.code+=1e5);break;case d.K:a.metadataType||(a.metadataType="unknown");var s=i.duration;Object(n.u)(s)&&(o.set("seekRange",i.seekRange),o.set("duration",s));break;case d.D:o.set("buffer",i.bufferPercent);case d.S:o.set("seekRange",i.seekRange),o.set("position",i.position),o.set("currentTime",i.currentTime);var l=i.duration;Object(n.u)(l)&&o.set("duration",l),t===d.S&&Object(n.r)(e.item.starttime)&&delete e.item.starttime;break;case d.R:var c=e.mediaElement;c&&c.paused&&o.set("mediaState","paused");break;case d.I:o.set(d.I,i.levels);case d.J:var u=i.currentQuality,p=i.levels;u>-1&&p.length>1&&o.set("currentLevel",parseInt(u));break;case d.f:o.set(d.f,i.tracks);case d.g:var w=i.currentTrack,h=i.tracks;w>-1&&h.length>0&&w=Math.max(l,p.a)&&(e.preloadNextItem(),v=!0)}function P(e){var t={};b.tag&&(t.tag=b.tag),this.trigger(d.F,t),z.call(this,e)}function z(e){g={},a&&f+10?e:null,h&&h.model.set("skipOffset",s)}};Object(n.g)(le.prototype,h.a);var ce=le,ue=i(66),de=i(63),pe=function(e){var t=this,i=[],n={},o=0,a=0;function r(e){if(e.data=e.data||[],e.name=e.label||e.name||e.language,e._id=Object(de.a)(e,i.length),!e.name){var t=Object(de.b)(e,o);e.name=t.label,o=t.unknownCount}n[e._id]=e,i.push(e)}function s(){for(var e=[{id:"off",label:"Off"}],t=0;t')+'
    '},fe=i(35),ge=44,je=function(e){var t=e.get("height");if(e.get("aspectratio"))return!1;if("string"==typeof t&&t.indexOf("%")>-1)return!1;var i=1*t||NaN;return!!(i=isNaN(i)?e.get("containerHeight"):i)&&(i&&i<=ge)},be=i(54);function me(e,t){if(e.get("fullscreen"))return 1;if(!e.get("activeTab"))return 0;if(e.get("isFloating"))return 1;var i=e.get("intersectionRatio");return void 0===i&&(i=function(e){var t=document.documentElement,i=document.body,n={top:0,left:0,right:t.clientWidth||i.clientWidth,width:t.clientWidth||i.clientWidth,bottom:t.clientHeight||i.clientHeight,height:t.clientHeight||i.clientHeight};if(!i.contains(e))return 0;if("none"===window.getComputedStyle(e).display)return 0;var o=ve(e);if(!o)return 0;var a=o,r=e.parentNode,s=!1;for(;!s;){var l=null;if(r===i||r===t||1!==r.nodeType?(s=!0,l=n):"visible"!==window.getComputedStyle(r).overflow&&(l=ve(r)),l&&(c=l,u=a,d=void 0,p=void 0,w=void 0,h=void 0,f=void 0,g=void 0,d=Math.max(c.top,u.top),p=Math.min(c.bottom,u.bottom),w=Math.max(c.left,u.left),h=Math.min(c.right,u.right),g=p-d,!(a=(f=h-w)>=0&&g>=0&&{top:d,bottom:p,left:w,right:h,width:f,height:g})))return 0;r=r.parentNode}var c,u,d,p,w,h,f,g;var j=o.width*o.height,b=a.width*a.height;return j?b/j:0}(t),window.top!==window.self&&i)?0:i}function ve(e){try{return e.getBoundingClientRect()}catch(e){}}var ye=i(49),ke=i(42),xe=i(58),Te=i(10);var Oe=i(32),Ce=i(5),Me=i(6),_e=["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],Se=function(e,t,i){for(var n=e.requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen,o=t.exitFullscreen||t.webkitExitFullscreen||t.webkitCancelFullScreen||t.mozCancelFullScreen||t.msExitFullscreen,a=!(!n||!o),r=_e.length;r--;)t.addEventListener(_e[r],i);return{events:_e,supportsDomFullscreen:function(){return a},requestFullscreen:function(){n.call(e,{navigationUI:"hide"})},exitFullscreen:function(){null!==this.fullscreenElement()&&o.apply(t)},fullscreenElement:function(){var e=t.fullscreenElement,i=t.webkitCurrentFullScreenElement,n=t.mozFullScreenElement,o=t.msFullscreenElement;return null===e?e:e||i||n||o},destroy:function(){for(var e=_e.length;e--;)t.removeEventListener(_e[e],i)}}},Ee=i(40);function Ae(e,t){for(var i=0;i
    ')},Be={linktarget:"_blank",margin:8,hide:!1,position:"top-right"};function Ie(e){var t,i;Object(n.g)(this,h.a);var o=new Image;this.setup=function(){(i=Object(n.g)({},Be,e.get("logo"))).position=i.position||Be.position,i.hide="true"===i.hide.toString(),i.file&&"control-bar"!==i.position&&(t||(t=Object(Ce.e)(Le(i.position,i.hide))),e.set("logo",i),o.onload=function(){var n=this.height,o=this.width,a={backgroundImage:'url("'+this.src+'")'};if(i.margin!==Be.margin){var r=/(\w+)-(\w+)/.exec(i.position);3===r.length&&(a["margin-"+r[1]]=i.margin,a["margin-"+r[2]]=i.margin)}var s=.15*e.get("containerHeight"),l=.15*e.get("containerWidth");if(n>s||o>l){var c=o/n;l/s>c?(n=s,o=s*c):(o=l,n=l/c)}a.width=Math.round(o),a.height=Math.round(n),Object(Te.d)(t,a),e.set("logoWidth",a.width)},o.src=i.file,i.link&&(t.setAttribute("tabindex","0"),t.setAttribute("aria-label",e.get("localization").logo)),this.ui=new Ee.a(t).on("click tap enter",(function(e){e&&e.stopPropagation&&e.stopPropagation(),this.trigger(d.A,{link:i.link,linktarget:i.linktarget})}),this))},this.setContainer=function(e){t&&e.appendChild(t)},this.element=function(){return t},this.position=function(){return i.position},this.destroy=function(){o.onload=null,this.ui&&this.ui.destroy()}}var Re=function(e){this.model=e,this.image=null};Object(n.g)(Re.prototype,{setup:function(e){this.el=e},setImage:function(e){var t=this.image;t&&(t.onload=null),this.image=null;var i="";"string"==typeof e&&(i='url("'+e+'")',(t=this.image=new Image).src=e),Object(Te.d)(this.el,{backgroundImage:i})},resize:function(e,t,i){if("uniform"===i){if(e&&(this.playerAspectRatio=e/t),!this.playerAspectRatio||!this.image||"complete"!==(s=this.model.get("state"))&&"idle"!==s&&"error"!==s&&"buffering"!==s)return;var n=this.image,o=null;if(n){if(0===n.width){var a=this;return void(n.onload=function(){a.resize(e,t,i)})}var r=n.width/n.height;Math.abs(this.playerAspectRatio-r)<.09&&(o="cover")}Object(Te.d)(this.el,{backgroundSize:o})}var s},element:function(){return this.el}});var Ve=Re,Ne=function(e){this.model=e.player};Object(n.g)(Ne.prototype,{hide:function(){Object(Te.d)(this.el,{display:"none"})},show:function(){Object(Te.d)(this.el,{display:""})},setup:function(e){this.el=e;var t=this.el.getElementsByTagName("div");this.title=t[0],this.description=t[1],this.model.on("change:logoWidth",this.update,this),this.model.change("playlistItem",this.playlistItem,this)},update:function(e){var t={},i=e.get("logo");if(i){var n=1*(""+i.margin).replace("px",""),o=e.get("logoWidth")+(isNaN(n)?0:n+10);"top-left"===i.position?t.paddingLeft=o:"top-right"===i.position&&(t.paddingRight=o)}Object(Te.d)(this.el,t)},playlistItem:function(e,t){if(t)if(e.get("displaytitle")||e.get("displaydescription")){var i="",n="";t.title&&e.get("displaytitle")&&(i=t.title),t.description&&e.get("displaydescription")&&(n=t.description),this.updateText(i,n)}else this.hide()},updateText:function(e,t){Object(Ce.q)(this.title,e),Object(Ce.q)(this.description,t),this.title.firstChild||this.description.firstChild?this.show():this.hide()},element:function(){return this.el}});var He=Ne;function Fe(e,t){for(var i=0;ie)}if(t.get("controls")){var r=je(t);Object(Ce.v)(u,"jw-flag-audio-player",r),t.set("audioMode",r)}}function I(){t.set("visibility",me(t,u))}this.updateBounds=function(){Object(ke.a)(k);var e=t.get("isFloating")?p:u,i=document.body.contains(e),n=Object(Ce.c)(e),r=Math.round(n.width),s=Math.round(n.height);if(S=Object(Ce.c)(u),r===o&&s===a)return o&&a||z(),void t.set("inDom",i);r&&s||o&&a||z(),(r||s||i)&&(t.set("containerWidth",r),t.set("containerHeight",s)),t.set("inDom",i),i&&be.a.observe(u)},this.updateStyles=function(){var e=t.get("containerWidth"),i=t.get("containerHeight");B(e,i),A&&A.resize(e,i),$(e,i),v.resize(),T&&F()},this.checkResized=function(){var e=t.get("containerWidth"),i=t.get("containerHeight"),n=t.get("isFloating");if(e!==o||i!==a){this.resizeListener||(this.resizeListener=new Ue.a(p,this,t)),o=e,a=i,l.trigger(d.hb,{width:e,height:i});var s=Object(xe.a)(e);E!==s&&(E=s,l.trigger(d.j,{breakpoint:E}))}n!==r&&(r=n,l.trigger(d.x,{floating:n}),I())},this.responsiveListener=z,this.setup=function(){j.setup(u.querySelector(".jw-preview")),b.setup(u.querySelector(".jw-title")),(i=new Ie(t)).setup(),i.setContainer(p),i.on(d.A,J),v.setup(u.id,t.get("captions")),b.element().parentNode.insertBefore(v.element(),b.element()),O=function(e,t,i){var n=new Pe(t,i),o=t.get("controls");n.on({click:function(){l.trigger(d.p),A&&(ce()?A.settingsMenu.close():ue()?A.infoOverlay.close():e.playToggle({reason:"interaction"}))},tap:function(){l.trigger(d.p),ce()&&A.settingsMenu.close(),ue()&&A.infoOverlay.close();var i=t.get("state");if(o&&(i===d.mb||i===d.kb||t.get("instream")&&i===d.ob)&&e.playToggle({reason:"interaction"}),o&&i===d.ob){if(t.get("instream")||t.get("castActive")||"audio"===t.get("mediaType"))return;Object(Ce.v)(u,"jw-flag-controls-hidden"),l.dismissible&&Object(Ce.v)(u,"jw-floating-dismissible",Object(Ce.i)(u,"jw-flag-controls-hidden")),v.renderCues(!0)}else A&&(A.showing?A.userInactive():A.userActive())},doubleClick:function(){return A&&e.setFullscreen()}}),We||(u.addEventListener("mousemove",W),u.addEventListener("mouseover",Q),u.addEventListener("mouseout",Y));return n}(e,t,f),M=new Ee.a(u).on("click",(function(){})),C=Se(u,document,te),t.on("change:hideAdsControls",(function(e,t){Object(Ce.v)(u,"jw-flag-ads-hide-controls",t)})),t.on("change:scrubbing",(function(e,t){Object(Ce.v)(u,"jw-flag-dragging",t)})),t.on("change:playRejected",(function(e,t){Object(Ce.v)(u,"jw-flag-play-rejected",t)})),t.on(d.X,ee),t.on("change:".concat(d.U),(function(){$(),v.resize()})),t.player.on("change:errorEvent",ae),t.change("stretching",X);var n=t.get("width"),o=t.get("height"),a=G(n,o);Object(Te.d)(u,a),t.change("aspectratio",K),B(n,o),t.get("controls")||(Object(Ce.a)(u,"jw-flag-controls-hidden"),Object(Ce.o)(u,"jw-floating-dismissible")),Qe&&Object(Ce.a)(u,"jw-ie");var r=t.get("skin")||{};r.name&&Object(Ce.p)(u,/jw-skin-\S+/,"jw-skin-"+r.name);var s=function(e){e||(e={});var t=e.active,i=e.inactive,n=e.background,o={};return o.controlbar=function(e){if(e||t||i||n){var o={};return e=e||{},o.iconsActive=e.iconsActive||t,o.icons=e.icons||i,o.text=e.text||i,o.background=e.background||n,o}}(e.controlbar),o.timeslider=function(e){if(e||t){var i={};return e=e||{},i.progress=e.progress||t,i.rail=e.rail,i}}(e.timeslider),o.menus=function(e){if(e||t||i||n){var o={};return e=e||{},o.text=e.text||i,o.textActive=e.textActive||t,o.background=e.background||n,o}}(e.menus),o.tooltips=function(e){if(e||i||n){var t={};return e=e||{},t.text=e.text||i,t.background=e.background||n,t}}(e.tooltips),o}(r);!function(e,t){var i;function n(t,i,n,o){if(n){t=Object(w.f)(t,"#"+e+(o?"":" "));var a={};a[i]=n,Object(Te.b)(t.join(", "),a,e)}}t&&(t.controlbar&&function(t){n([".jw-controlbar .jw-icon-inline.jw-text",".jw-title-primary",".jw-title-secondary"],"color",t.text),t.icons&&(n([".jw-button-color:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:not(.jw-icon-cast)"],"color",t.icons),n([".jw-display-icon-container .jw-button-color"],"color",t.icons),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.icons,"}"),e));t.iconsActive&&(n([".jw-display-icon-container .jw-button-color:hover",".jw-display-icon-container .jw-button-color:focus"],"color",t.iconsActive),n([".jw-button-color.jw-toggle:not(.jw-icon-cast)",".jw-button-color:hover:not(.jw-icon-cast)",".jw-button-color:focus:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:hover:not(.jw-icon-cast)"],"color",t.iconsActive),n([".jw-svg-icon-buffer"],"fill",t.icons),Object(Te.b)("#".concat(e," .jw-icon-cast:hover google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:focus google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher.jw-off:focus"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher:focus"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:hover google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:focus google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e));n([" .jw-settings-topbar",":not(.jw-state-idle) .jw-controlbar",".jw-flag-audio-player .jw-controlbar"],"background",t.background,!0)}(t.controlbar),t.timeslider&&function(e){var t=e.progress;"none"!==t&&(n([".jw-progress",".jw-knob"],"background-color",t),n([".jw-buffer"],"background-color",Object(Te.c)(t,50)));n([".jw-rail"],"background-color",e.rail),n([".jw-background-color.jw-slider-time",".jw-slider-time .jw-cue"],"background-color",e.background)}(t.timeslider),t.menus&&(n([".jw-option",".jw-toggle.jw-off",".jw-skip .jw-skip-icon",".jw-nextup-tooltip",".jw-nextup-close",".jw-settings-content-item",".jw-related-title"],"color",(i=t.menus).text),n([".jw-option.jw-active-option",".jw-option:not(.jw-active-option):hover",".jw-option:not(.jw-active-option):focus",".jw-settings-content-item:hover",".jw-nextup-tooltip:hover",".jw-nextup-tooltip:focus",".jw-nextup-close:hover"],"color",i.textActive),n([".jw-nextup",".jw-settings-menu"],"background",i.background)),t.tooltips&&function(e){n([".jw-skip",".jw-tooltip .jw-text",".jw-time-tip .jw-text"],"background-color",e.background),n([".jw-time-tip",".jw-tooltip"],"color",e.background),n([".jw-skip"],"border","none"),n([".jw-skip .jw-text",".jw-skip .jw-icon",".jw-time-tip .jw-text",".jw-tooltip .jw-text"],"color",e.text)}(t.tooltips),t.menus&&function(t){if(t.textActive){var i={color:t.textActive,borderColor:t.textActive,stroke:t.textActive};Object(Te.b)("#".concat(e," .jw-color-active"),i,e),Object(Te.b)("#".concat(e," .jw-color-active-hover:hover"),i,e)}if(t.text){var n={color:t.text,borderColor:t.text,stroke:t.text};Object(Te.b)("#".concat(e," .jw-color-inactive"),n,e),Object(Te.b)("#".concat(e," .jw-color-inactive-hover:hover"),n,e)}}(t.menus))}(t.get("id"),s),t.set("mediaContainer",f),t.set("iFrame",m.Features.iframe),t.set("activeTab",Object(ye.a)()),t.set("touchMode",We&&("string"==typeof o||o>=ge)),be.a.add(this),t.get("enableGradient")&&!Qe&&Object(Ce.a)(u,"jw-ab-drop-shadow"),this.isSetup=!0,t.trigger("viewSetup",u);var c=document.body.contains(u);c&&be.a.observe(u),t.set("inDom",c)},this.init=function(){this.updateBounds(),t.on("change:fullscreen",Z),t.on("change:activeTab",I),t.on("change:fullscreen",I),t.on("change:intersectionRatio",I),t.on("change:visibility",U),t.on("instreamMode",(function(e){e?de():pe()})),I(),1!==be.a.size()||t.get("visibility")||U(t,1,0);var e=t.player;t.change("state",re),e.change("controls",D),t.change("streamType",ne),t.change("mediaType",oe),e.change("playlistItem",(function(e,t){le(e,t)})),o=a=null,T&&We&&be.a.addScrollHandler(F),this.checkResized()};var R,V=62,N=!0;function H(){var e=t.get("isFloating"),i=S.top0&&void 0!==arguments[0])||arguments[0],t={x:0,y:0,width:o||0,height:a||0};return A&&e&&(t.height-=A.controlbarHeight()),t},this.setCaptions=function(e){v.clear(),v.setup(t.get("id"),e),v.resize()},this.setIntersection=function(e){var i=Math.round(100*e.intersectionRatio)/100;t.set("intersectionRatio",i),T&&!P()&&(_=_||i>=.5)&&we(i)},this.stopFloating=function(e,i){if(e&&(T=null,be.a.removeScrollHandler(F)),Ye===u){Ye=null,t.set("isFloating",!1);var n=function(){Object(Ce.o)(u,"jw-flag-floating"),K(t,t.get("aspectratio")),Object(Te.d)(u,{backgroundImage:null}),Object(Te.d)(p,{maxWidth:null,width:null,height:null,left:null,right:null,top:null,bottom:null,margin:null,transform:null,transition:null,"transition-timing-function":null})};i?(Object(Te.d)(p,{transform:"translateY(-".concat(V-S.top,"px)"),"transition-timing-function":"ease-out"}),setTimeout(n,150)):n(),g.disable(),z()}},this.destroy=function(){t.destroy(),be.a.unobserve(u),be.a.remove(this),this.isSetup=!1,this.off(),Object(ke.a)(k),clearTimeout(y),Ye===u&&(Ye=null),M&&(M.destroy(),M=null),C&&(C.destroy(),C=null),A&&A.disable(t),O&&(O.destroy(),u.removeEventListener("mousemove",W),u.removeEventListener("mouseout",Y),u.removeEventListener("mouseover",Q),O=null),v.destroy(),i&&(i.destroy(),i=null),Object(Te.a)(t.get("id")),this.resizeListener&&(this.resizeListener.destroy(),delete this.resizeListener),T&&We&&be.a.removeScrollHandler(F)}};function Ke(e,t,i){return(Ke="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,i){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=tt(e)););return e}(e,t);if(n){var o=Object.getOwnPropertyDescriptor(n,t);return o.get?o.get.call(i):o.value}})(e,t,i||e)}function Je(e){return(Je="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ze(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Ge(e,t){for(var i=0;it&&e(),t=n}};function Ot(e,t){t.off(d.N,e._onPlayAttempt),t.off(d.fb,e._triggerFirstFrame),t.off(d.S,e._onTime),e.off("change:activeTab",e._onTabVisible)}var Ct=function(e,t){e.change("mediaModel",(function(e,i,n){e._qoeItem&&n&&e._qoeItem.end(n.get("mediaState")),e._qoeItem=new yt.a,e._qoeItem.getFirstFrame=function(){var e=this.between(d.N,d.H),t=this.between(xt,d.H);return t>0&&t0&&re(t,e.tracks)}),O).on(d.F,(function(){Promise.resolve().then(ae)}),O).on(d.G,O.triggerError,O),Ct(C,R),C.on(d.w,O.triggerError,O),C.on("change:state",(function(e,t,i){X()||K.call(T,e,t,i)}),this),C.on("change:castState",(function(e,t){O.trigger(d.m,t)})),C.on("change:fullscreen",(function(e,t){O.trigger(d.y,{fullscreen:t}),t&&e.set("playOnViewable",!1)})),C.on("change:volume",(function(e,t){O.trigger(d.V,{volume:t})})),C.on("change:mute",(function(e){O.trigger(d.M,{mute:e.getMute()})})),C.on("change:playbackRate",(function(e,t){O.trigger(d.ab,{playbackRate:t,position:e.get("position")})}));var V=function e(t,i){"clickthrough"!==i&&"interaction"!==i&&"external"!==i||(C.set("playOnViewable",!1),C.off("change:playReason change:pauseReason",e))};function N(e,t){Object(n.t)(t)||C.set("viewable",Math.round(t))}function H(){de&&(!0!==C.get("autostart")||C.get("playOnViewable")||$("autostart"),de.flush())}function F(e,t){O.trigger("viewable",{viewable:t}),D()}function D(){if((o.a[0]===t||1===C.get("viewable"))&&"idle"===C.get("state")&&!1===C.get("autostart"))if(!b.primed()&&m.OS.android){var e=b.getTestElement(),i=O.getMute();Promise.resolve().then((function(){return ht(e,{muted:i})})).then((function(){"idle"===C.get("state")&&R.preloadVideo()})).catch(St)}else R.preloadVideo()}function q(e){O._instreamAdapter.noResume=!e,e||te({reason:"viewable"})}function U(e){e||(O.pause({reason:"viewable"}),C.set("playOnViewable",!e))}function W(e,t){var i=X();if(e.get("playOnViewable")){if(t){var n=e.get("autoPause").pauseAds,o=e.get("pauseReason");J()===d.mb?$("viewable"):i&&!n||"interaction"===o||Z({reason:"viewable"})}else m.OS.mobile&&!i&&(O.pause({reason:"autostart"}),C.set("playOnViewable",!0));m.OS.mobile&&i&&q(t)}}function Q(e,t){var i=e.get("state"),n=X(),o=e.get("playReason");n?e.get("autoPause").pauseAds?U(t):q(t):i===d.pb||i===d.jb?U(t):i===d.mb&&"playlist"===o&&e.once("change:state",(function(){U(t)}))}function X(){var e=O._instreamAdapter;return!!e&&e.getState()}function J(){var e=X();return e||C.get("state")}function Z(e){if(E.cancel(),_=!1,C.get("state")===d.lb)return Promise.resolve();var i=G(e);return C.set("playReason",i),X()?(t.pauseAd(!1,e),Promise.resolve()):(C.get("state")===d.kb&&(ee(!0),O.setItemIndex(0)),!M&&(M=!0,O.trigger(d.C,{playReason:i,startTime:e&&e.startTime?e.startTime:C.get("playlistItem").starttime}),M=!1,vt()&&!b.primed()&&b.prime(),"playlist"===i&&C.get("autoPause").viewability&&Q(C,C.get("viewable")),x)?(vt()&&!B&&C.get("mediaElement").load(),x=!1,k=null,Promise.resolve()):R.playVideo(i).then(b.played))}function G(e){return e&&e.reason?e.reason:"unknown"}function $(e){if(J()===d.mb){E=g(H);var t=C.get("advertising");(function(e,t){var i=t.cancelable,n=t.muted,o=void 0!==n&&n,a=t.allowMuted,r=void 0!==a&&a,s=t.timeout,l=void 0===s?1e4:s,c=e.getTestElement(),u=o?"muted":"".concat(r);bt[u]||(bt[u]=ht(c,{muted:o}).catch((function(e){if(!i.cancelled()&&!1===o&&r)return ht(c,{muted:o=!0});throw e})).then((function(){return o?(bt[u]=null,gt):ft})).catch((function(e){throw clearTimeout(d),bt[u]=null,e.reason=jt,e})));var d,p=bt[u].then((function(e){if(clearTimeout(d),i.cancelled()){var t=new Error("Autoplay test was cancelled");throw t.reason="cancelled",t}return e})),w=new Promise((function(e,t){d=setTimeout((function(){bt[u]=null;var e=new Error("Autoplay test timed out");e.reason="timeout",t(e)}),l)}));return Promise.race([p,w])})(b,{cancelable:E,muted:O.getMute(),allowMuted:!t||t.autoplayadsmuted}).then((function(t){return C.set("canAutoplay",t),t!==gt||O.getMute()||(C.set("autostartMuted",!0),ue(),C.once("change:autostartMuted",(function(e){e.off("change:viewable",W),O.trigger(d.M,{mute:C.getMute()})}))),O.getMute()&&C.get("enableDefaultCaptions")&&y.selectDefaultIndex(1),Z({reason:e}).catch((function(){O._instreamAdapter||C.set("autostartFailed",!0),k=null}))})).catch((function(e){if(C.set("canAutoplay",jt),C.set("autostart",!1),!E.cancelled()){var t=Object(j.w)(e);O.trigger(d.h,{reason:e.reason,code:t,error:e})}}))}}function ee(e){if(E.cancel(),de.empty(),X()){var t=O._instreamAdapter;return t&&(t.noResume=!0),void(k=function(){return R.stopVideo()})}k=null,!e&&(_=!0),M&&(x=!0),C.set("errorEvent",void 0),R.stopVideo()}function te(e){var t=G(e);C.set("pauseReason",t),C.set("playOnViewable","viewable"===t)}function ie(e){k=null,E.cancel();var i=X();if(i&&i!==d.ob)return te(e),void t.pauseAd(!0,e);switch(C.get("state")){case d.lb:return;case d.pb:case d.jb:te(e),R.pause();break;default:M&&(x=!0)}}function ne(e,t){ee(!0),O.setItemIndex(e),O.play(t)}function oe(e){ne(C.get("item")+1,e)}function ae(){O.completeCancelled()||(k=O.completeHandler,O.shouldAutoAdvance()?O.nextItem():C.get("repeat")?oe({reason:"repeat"}):(m.OS.iOS&&le(!1),C.set("playOnViewable",!1),C.set("state",d.kb),O.trigger(d.cb,{})))}function re(e,t){e=parseInt(e,10)||0,C.persistVideoSubtitleTrack(e,t),R.subtitles=e,O.trigger(d.k,{tracks:se(),track:e})}function se(){return y.getCaptionsList()}function le(e){Object(n.n)(e)||(e=!C.get("fullscreen")),C.set("fullscreen",e),O._instreamAdapter&&O._instreamAdapter._adModel&&O._instreamAdapter._adModel.set("fullscreen",e)}function ue(){R.mute=C.getMute(),R.volume=C.get("volume")}C.on("change:playReason change:pauseReason",V),O.on(d.c,(function(e){return V(0,e.playReason)})),O.on(d.b,(function(e){return V(0,e.pauseReason)})),C.on("change:scrubbing",(function(e,t){t?(S=C.get("state")!==d.ob,ie()):S&&Z({reason:"interaction"})})),C.on("change:captionsList",(function(e,t){O.trigger(d.l,{tracks:t,track:C.get("captionsIndex")||0})})),C.on("change:mediaModel",(function(e,t){var i=this;e.set("errorEvent",void 0),t.change("mediaState",(function(t,i){var n;e.get("errorEvent")||e.set(d.bb,(n=i)===d.nb||n===d.qb?d.jb:n)}),this),t.change("duration",(function(t,i){if(0!==i){var n=e.get("minDvrWindow"),o=Object(mt.b)(i,n);e.setStreamType(o)}}),this);var n=e.get("item")+1,o="autoplay"===(e.get("related")||{}).oncomplete,a=e.get("playlist")[n];if((a||o)&&B){t.on("change:position",(function e(n,r){var s=a&&!a.daiSetting,l=t.get("duration");s&&r&&l>0&&r>=l-p.b?(t.off("change:position",e,i),R.backgroundLoad(a)):o&&(a=C.get("nextUp"))}),this)}})),(y=new we(C)).on("all",L,O),I.on("viewSetup",(function(e){Object(a.b)(T,e)})),this.playerReady=function(){v.once(d.hb,(function(){try{!function(){C.change("visibility",N),P.off(),O.trigger(d.gb,{setupTime:0}),C.change("playlist",(function(e,t){if(t.length){var i={playlist:t},o=C.get("feedData");o&&(i.feedData=Object(n.g)({},o)),O.trigger(d.eb,i)}})),C.change("playlistItem",(function(e,t){if(t){var i=t.title,n=t.image;if("mediaSession"in navigator&&window.MediaMetadata&&(i||n))try{navigator.mediaSession.metadata=new window.MediaMetadata({title:i,artist:window.location.hostname,artwork:[{src:n||""}]})}catch(e){}e.set("cues",[]),O.trigger(d.db,{index:C.get("item"),item:t})}})),P.flush(),P.destroy(),P=null,C.change("viewable",F),C.change("viewable",W),C.get("autoPause").viewability?C.change("viewable",Q):C.once("change:autostartFailed change:mute",(function(e){e.off("change:viewable",W)}));H(),C.on("change:itemReady",(function(e,t){t&&de.flush()}))}()}catch(e){O.triggerError(Object(j.v)(j.m,j.a,e))}})),v.init()},this.preload=D,this.load=function(e,t){var i,n=O._instreamAdapter;switch(n&&(n.noResume=!0),O.trigger("destroyPlugin",{}),ee(!0),E.cancel(),E=g(H),A.cancel(),vt()&&b.prime(),Mt(e)){case"string":C.attributes.item=0,C.attributes.itemReady=!1,A=g((function(e){if(e)return O.updatePlaylist(Object(c.a)(e.playlist),e)})),i=function(e){var t=this;return new Promise((function(i,n){var o=new l.a;o.on(d.eb,(function(e){i(e)})),o.on(d.w,n,t),o.load(e)}))}(e).then(A.async);break;case"object":C.attributes.item=0,i=O.updatePlaylist(Object(c.a)(e),t||{});break;case"number":i=O.setItemIndex(e);break;default:return}i.catch((function(e){O.triggerError(Object(j.u)(e,j.c))})),i.then(E.async).catch(St)},this.play=function(e){return Z(e).catch(St)},this.pause=ie,this.seek=function(e,t){var i=C.get("state");if(i!==d.lb){R.position=e;var n=i===d.mb;C.get("scrubbing")||!n&&i!==d.kb||(n&&((t=t||{}).startTime=e),this.play(t))}},this.stop=ee,this.playlistItem=ne,this.playlistNext=oe,this.playlistPrev=function(e){ne(C.get("item")-1,e)},this.setCurrentCaptions=re,this.setCurrentQuality=function(e){R.quality=e},this.setFullscreen=le,this.getCurrentQuality=function(){return R.quality},this.getQualityLevels=function(){return R.qualities},this.setCurrentAudioTrack=function(e){R.audioTrack=e},this.getCurrentAudioTrack=function(){return R.audioTrack},this.getAudioTracks=function(){return R.audioTracks},this.getCurrentCaptions=function(){return y.getCurrentIndex()},this.getCaptionsList=se,this.getVisualQuality=function(){var e=this._model.get("mediaModel");return e?e.get(d.U):null},this.getConfig=function(){return this._model?this._model.getConfiguration():void 0},this.getState=J,this.next=St,this.completeHandler=ae,this.completeCancelled=function(){return(e=C.get("state"))!==d.mb&&e!==d.kb&&e!==d.lb||!!_&&(_=!1,!0);var e},this.shouldAutoAdvance=function(){return C.get("item")!==C.get("playlist").length-1},this.nextItem=function(){oe({reason:"playlist"})},this.setConfig=function(e){!function(e,t){var i=e._model,n=i.attributes;t.height&&(t.height=Object(r.b)(t.height),t.width=t.width||n.width),t.width&&(t.width=Object(r.b)(t.width),t.aspectratio?(n.width=t.width,delete t.width):t.height=n.height),t.width&&t.height&&!t.aspectratio&&e._view.resize(t.width,t.height),Object.keys(t).forEach((function(o){var a=t[o];if(void 0!==a)switch(o){case"aspectratio":i.set(o,Object(r.a)(a,n.width));break;case"autostart":!function(e,t,i){e.setAutoStart(i),"idle"===e.get("state")&&!0===i&&t.play({reason:"autostart"})}(i,e,a);break;case"mute":e.setMute(a);break;case"volume":e.setVolume(a);break;case"playbackRateControls":case"playbackRates":case"repeat":case"stretching":i.set(o,a)}}))}(O,e)},this.setItemIndex=function(e){R.stopVideo();var t=C.get("playlist").length;return(e=(parseInt(e,10)||0)%t)<0&&(e+=t),R.setActiveItem(e).catch((function(e){e.code>=151&&e.code<=162&&(e=Object(j.u)(e,j.e)),T.triggerError(Object(j.v)(j.k,j.d,e))}))},this.detachMedia=function(){if(M&&(x=!0),C.get("autoPause").viewability&&Q(C,C.get("viewable")),!B)return R.setAttached(!1);R.backgroundActiveMedia()},this.attachMedia=function(){B?R.restoreBackgroundMedia():R.setAttached(!0),"function"==typeof k&&k()},this.routeEvents=function(e){return R.routeEvents(e)},this.forwardEvents=function(){return R.forwardEvents()},this.playVideo=function(e){return R.playVideo(e)},this.stopVideo=function(){return R.stopVideo()},this.castVideo=function(e,t){return R.castVideo(e,t)},this.stopCast=function(){return R.stopCast()},this.backgroundActiveMedia=function(){return R.backgroundActiveMedia()},this.restoreBackgroundMedia=function(){return R.restoreBackgroundMedia()},this.preloadNextItem=function(){R.background.currentMedia&&R.preloadVideo()},this.isBeforeComplete=function(){return R.beforeComplete},this.setVolume=function(e){C.setVolume(e),ue()},this.setMute=function(e){C.setMute(e),ue()},this.setPlaybackRate=function(e){C.setPlaybackRate(e)},this.getProvider=function(){return C.get("provider")},this.getWidth=function(){return C.get("containerWidth")},this.getHeight=function(){return C.get("containerHeight")},this.getItemQoe=function(){return C._qoeItem},this.addButton=function(e,t,i,n,o){var a=C.get("customButtons")||[],r=!1,s={img:e,tooltip:t,callback:i,id:n,btnClass:o};a=a.reduce((function(e,t){return t.id===n?(r=!0,e.push(s)):e.push(t),e}),[]),r||a.unshift(s),C.set("customButtons",a)},this.removeButton=function(e){var t=C.get("customButtons")||[];t=t.filter((function(t){return t.id!==e})),C.set("customButtons",t)},this.resize=v.resize,this.getSafeRegion=v.getSafeRegion,this.setCaptions=v.setCaptions,this.checkBeforePlay=function(){return M},this.setControls=function(e){Object(n.n)(e)||(e=!C.get("controls")),C.set("controls",e),R.controls=e},this.addCues=function(e){this.setCues(C.get("cues").concat(e))},this.setCues=function(e){C.set("cues",e)},this.updatePlaylist=function(e,t){try{var i=Object(c.b)(e,C,t);Object(c.e)(i);var o=Object(n.g)({},t);delete o.playlist,C.set("feedData",o),C.set("playlist",i)}catch(e){return Promise.reject(e)}return this.setItemIndex(C.get("item"))},this.setPlaylistItem=function(e,t){(t=Object(c.d)(C,new u.a(t),t.feedData||{}))&&(C.get("playlist")[e]=t,e===C.get("item")&&"idle"===C.get("state")&&this.setItemIndex(e))},this.playerDestroy=function(){this.off(),this.stop(),Object(a.b)(this,this.originalContainer),v&&v.destroy(),C&&C.destroy(),de&&de.destroy(),y&&y.destroy(),R&&R.destroy(),this.instreamDestroy()},this.isBeforePlay=this.checkBeforePlay,this.createInstream=function(){return this.instreamDestroy(),this._instreamAdapter=new ce(this,C,v,b),this._instreamAdapter},this.instreamDestroy=function(){O._instreamAdapter&&(O._instreamAdapter.destroy(),O._instreamAdapter=null)};var de=new s.a(this,["play","pause","setCurrentAudioTrack","setCurrentCaptions","setCurrentQuality","setFullscreen"],(function(){return!T._model.get("itemReady")||P}));de.queue.push.apply(de.queue,f),v.setup()},get:function(e){if(e in y.a){var t=this._model.get("mediaModel");return t?t.get(e):y.a[e]}return this._model.get(e)},getContainer:function(){return this.currentContainer||this.originalContainer},getMute:function(){return this._model.getMute()},triggerError:function(e){var t=this._model;e.message=t.get("localization").errors[e.key],delete e.key,t.set("errorEvent",e),t.set("state",d.lb),t.once("change:state",(function(){this.set("errorEvent",void 0)}),t),this.trigger(d.w,e)}});t.default=_t},,,,,,,,,,,,,function(e,t,i){"use strict";i.r(t);var n=i(0);var o=i(8),a=i(52),r=i(3),s=i(43),l={canplay:function(){this.trigger(r.E)},play:function(){this.stallTime=-1,this.video.paused||this.state===r.pb||this.setState(r.nb)},loadedmetadata:function(){var e={metadataType:"media",duration:this.getDuration(),height:this.video.videoHeight,width:this.video.videoWidth,seekRange:this.getSeekRange()},t=this.drmUsed;t&&(e.drm=t),this.trigger(r.K,e)},timeupdate:function(){var e=this.getVideoCurrentTime(),t=this.getCurrentTime(),i=this.getDuration();if(!isNaN(i)){this.seeking||this.video.paused||this.state!==r.qb&&this.state!==r.nb||this.stallTime===e||(this.stallTime=-1,this.setState(r.pb),this.trigger(r.fb));var n={position:t,duration:i,currentTime:e,seekRange:this.getSeekRange(),metadata:{currentTime:e}};if(this.getPtsOffset){var o=this.getPtsOffset();o>=0&&(n.metadata.mpegts=o+t)}var a=this.getLiveLatency();null!==a&&(n.latency=a),(this.state===r.pb||this.seeking)&&this.trigger(r.S,n)}},click:function(e){this.trigger(r.n,e)},volumechange:function(){var e=this.video;this.trigger(r.V,{volume:Math.round(100*e.volume)}),this.trigger(r.M,{mute:e.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(r.R))},playing:function(){-1===this.stallTime&&this.setState(r.pb),this.trigger(r.fb)},pause:function(){this.state!==r.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(r.ob))},progress:function(){var e=this.getDuration();if(!(e<=0||e===1/0)){var t=this.video.buffered;if(t&&0!==t.length){var i=Object(s.a)(t.end(t.length-1)/e,0,1);this.trigger(r.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:e,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(r.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==r.mb&&this.state!==r.kb&&this.trigger(r.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},c=i(10);function u(e){return e&&e.length?e.end(e.length-1):0}var d={container:null,volume:function(e){this.video.volume=Math.min(Math.max(0,e/100),1)},mute:function(e){this.video.muted=!!e,this.video.muted||this.video.removeAttribute("muted")},resize:function(e,t,i){var n=this.video,a=n.videoWidth,r=n.videoHeight;if(e&&t&&a&&r){var s={objectFit:"",width:"",height:""};if("uniform"===i){var l=e/t,u=a/r,d=Math.abs(l-u);d<.09&&d>.0025&&(s.objectFit="fill",i="exactfit")}if(o.Browser.ie||o.OS.iOS&&o.OS.version.major<9||o.Browser.androidNative)if("uniform"!==i){s.objectFit="contain";var p=e/t,w=a/r,h=1,f=1;"none"===i?h=f=p>w?Math.ceil(100*r/t)/100:Math.ceil(100*a/e)/100:"fill"===i?h=f=p>w?p/w:w/p:"exactfit"===i&&(p>w?(h=p/w,f=1):(h=1,f=w/p)),Object(c.e)(n,"matrix(".concat(h.toFixed(2),", 0, 0, ").concat(f.toFixed(2),", 0, 0)"))}else s.top=s.left=s.margin="",Object(c.e)(n,"");Object(c.d)(n,s)}},getContainer:function(){return this.container},setContainer:function(e){this.container=e,this.video.parentNode!==e&&e.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var e=this.container;e&&e===this.video.parentNode&&e.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return u(this.video.buffered)-this.video.currentTime<=2}},p={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},w=i(65),h=i(5),f=i(53),g=i(7),j=i(66),b=i(63),m={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function v(e,t){for(var i,n,o,a=e.length,r="",s=t||0;s>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:r+=String.fromCharCode(i);break;case 12:case 13:n=e[s++],r+=String.fromCharCode((31&i)<<6|63&n);break;case 14:n=e[s++],o=e[s++],r+=String.fromCharCode((15&i)<<12|(63&n)<<6|(63&o)<<0)}return r}function y(e){var t=function(e){for(var t="0x",i=0;i>1|(8323072&t)>>2|(2130706432&t)>>3}function k(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(e,t){if(!("value"in t)&&"data"in t&&t.data instanceof ArrayBuffer){var i=new Uint8Array(t.data),n=i.length;t={value:{key:"",data:""}};for(var o=10;o<14&&o0){var c=v(i.subarray(a,a+=s),0);if("PRIV"===t.value.key){if("com.apple.streaming.transportStreamTimestamp"===c){var u=1&y(i.subarray(a,a+=4)),d=y(i.subarray(a,a+=4))+(u?4294967296:0);t.value.data=d}else t.value.data=v(i,a+1);t.value.info=c}else t.value.info=c,t.value.data=v(i,a+1)}else{var p=i[a];t.value.data=1===p||2===p?function(e,t){for(var i=e.length-1,n="",o=t||0;o=0&&o[a].startTime>t.startTime;a--)i.unshift(o[a]),e.removeCue(o[a]);try{e.addCue(t),i.forEach((function(t){return e.addCue(t)}))}catch(e){console.error(e)}e.mode=n}(t,n)}else try{t.addCue(i)}catch(e){console.error(e)}}function _(e,t){t&&t.length&&Object(n.f)(t,(function(t){if(!(o.Browser.ie&&e&&/^(native|subtitle|cc)/.test(t._id))){o.Browser.ie&&"disabled"===t.mode||(t.mode="disabled",t.mode="hidden");for(var i=t.cues.length;i--;)t.removeCue(t.cues[i]);t.embedded||(t.mode="disabled"),t.inuse=!1}}))}function S(e){return"subtitles"===e||"captions"===e}function E(e){var t,i=Object(b.b)(e,this._unknownCount),o=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===e.kind){var a=this.video.textTracks;(t=Object(n.j)(a,{label:o}))||(t=this.video.addTextTrack(e.kind,o,e.language||"")),t.default=e.default,t.mode="disabled",t.inuse=!0}else(t=e).data=t.data||[];return t._id||(t._id=Object(b.a)(e,this._textTracks.length)),t}function A(e){this._textTracks.push(e),this._tracksById[e._id]=e}function P(){if(this._textTracks){var e=this._textTracks.filter((function(e){return e.embedded||"subs"===e.groupid}));this._initTextTracks(),e.forEach((function(e){this._tracksById[e._id]=e})),this._textTracks=e}}function z(e){this.triggerActiveCues(e.currentTarget.activeCues)}function L(e,t,i){var n=e.kind;this._cachedVTTCues[e._id]||(this._cachedVTTCues[e._id]={});var o,a=this._cachedVTTCues[e._id];switch(n){case"captions":case"subtitles":o=i||Math.floor(20*t.startTime);var r="_"+t.line,s=Math.floor(20*t.endTime),l=a[o+r]||a[o+1+r]||a[o-1+r];return!(l&&Math.abs(l-s)<=1)&&(a[o+r]=s,!0);case"metadata":var c=t.data?new Uint8Array(t.data).join(""):t.text;return!a[o=i||t.startTime+c]&&(a[o]=t.endTime,!0);default:return!1}}function B(e){if(e.length>this._textTracks.length)return!0;for(var t=0;t=0&&(f.retries=0);var e=f.getVideoCurrentTime();f.currentTime=e,_&&C!==e&&$(e),l.timeupdate.call(f),he(),o.Browser.ie&&G()},resize:G,ended:function(){M=-1,fe(),l.ended.call(f)},loadedmetadata:function(){var e=f.getDuration();B&&e===1/0&&(e=0);var t={metadataType:"media",duration:e,height:v.videoHeight,width:v.videoWidth,seekRange:f.getSeekRange()};f.trigger(r.K,t),G()},durationchange:function(){B||l.progress.call(f)},loadeddata:function(){var e;!function(){if(v.getStartDate){var e=v.getStartDate(),t=e.getTime?e.getTime():NaN;if(t!==f.startDateTime&&!isNaN(t)){f.startDateTime=t;var i=e.toISOString(),n=f.getSeekRange(),o=n.start,a=n.end,s={metadataType:"program-date-time",programDateTime:i,start:o,end:a},l=f.createCue(o,a,JSON.stringify(s));f.addVTTCue({type:"metadata",cue:l}),delete s.metadataType,f.trigger(r.L,{metadataType:"program-date-time",metadata:s})}}}(),l.loadeddata.call(f),function(e){if(E=null,!e)return;if(e.length){for(var t=0;t0&&(t=e.map((function(e,t){return{label:e.label||t}}))),t}function ie(e){f.currentTime=-1,j=e.minDvrWindow,m=e.sources,M=function(e){var i=Math.max(0,M),n=t.qualityLabel;if(e)for(var o=0;o0&&(T=-1,f.seek(e)),e>0&&f.getVideoCurrentTime()!==e&&f.seek(e);var n=te(m);n&&f.trigger(r.I,{levels:n,currentQuality:M}),m.length&&"hls"!==m[0].type&&we()}function ae(e){E=null,A=-1,y.reason||(y.reason="initial choice",y.level={}),x=!1;var t=document.createElement("source");t.src=e.file,v.src!==t.src&&(v.src=e.file)}function re(){v&&(f.disableTextTrack(),v.removeAttribute("preload"),v.removeAttribute("src"),Object(h.h)(v),Object(c.d)(v,{objectFit:""}),M=-1,!o.Browser.msie&&"load"in v&&v.load())}function se(){var e=1/0;return["buffered","seekable"].forEach((function(t){for(var i=v[t],o=i?i.length:0;o--;){var a=Math.min(e,i.start(o));Object(n.o)(a)&&(e=a)}})),e}function le(){var e=0;return["buffered","seekable"].forEach((function(t){for(var i=v[t],o=i?i.length:0;o--;){var a=Math.max(e,i.end(o));Object(n.o)(a)&&(e=a)}})),e}function ce(){for(var e=-1,t=0;t-1&&e1)&&function(e){X=e.end,J=Math.min(0,f.getVideoCurrentTime()-X),Z=Object(V.a)()}(t),Object(w.a)(t.end-t.start,j))return J}return e}(f.getVideoCurrentTime())},f.getDuration=function(){if(t.getDurationHook)return t.getDurationHook();var e=v.duration;if(B&&e===1/0&&0===f.getVideoCurrentTime()||isNaN(e))return 0;var i=le();if(v.duration===1/0&&i){var n=i-se();Object(w.a)(n,j)&&(e=-n)}return e},f.getSeekRange=function(){var e={start:0,end:f.getDuration()};return v.seekable.length&&(e.end=le(),e.start=se()),e},f.getLiveLatency=function(){var e=null,t=le();return f.isLive()&&t&&(e=t+(Object(V.a)()-Z)/1e3-f.getVideoCurrentTime()),e},this.stop=function(){fe(),re(),this.clearTracks(),o.Browser.ie&&v.pause(),this.setState(r.mb)},this.destroy=function(){S=Q,Y(b,v),this.removeTracksListener(v.audioTracks,"change",ce),this.removeTracksListener(v.textTracks,"change",f.textTrackChangeHandler),this.off()},this.init=function(e){f.retries=0,f.maxRetries=e.adType?0:3,ie(e);var t=m[M];(B=Object(a.a)(t))&&(f.supportsPlaybackRate=!1,b.waiting=Q),f.eventsOn_(),m.length&&"hls"!==m[0].type&&this.sendMediaType(m),y.reason=""},this.preload=function(e){ie(e);var t=m[M],i=t.preload||"metadata";"none"!==i&&(v.setAttribute("preload",i),ae(t))},this.load=function(e){ie(e),oe(e.starttime),this.setupSideloadedTracks(e.tracks)},this.play=function(){return S(),ne()},this.pause=function(){fe(),S=function(){if(v.paused&&f.getVideoCurrentTime()&&f.isLive()){var e=le(),t=e-se(),i=!Object(w.a)(t,j),o=e-f.getVideoCurrentTime();if(i&&e&&(o>15||o<0)){if(O=Math.max(e-10,e-t),!Object(n.o)(O))return;$(f.getVideoCurrentTime()),v.currentTime=O}}},v.pause()},this.seek=function(e){if(t.seekHook)return t.seekHook(e,v);var i=f.getSeekRange(),n=e;if(e<0&&(n+=i.end),x||(x=!!le()),x){T=0;try{if(f.seeking=!0,f.isLive()&&Object(w.a)(i.end-i.start,j))if(J=Math.min(0,n-X),e<0)n+=Math.min(12,(Object(V.a)()-Z)/1e3);O=n,$(f.getVideoCurrentTime()),v.currentTime=n}catch(e){f.seeking=!1,T=n}}else T=n,o.Browser.firefox&&v.paused&&ne()},this.setVisibility=function(e){(e=!!e)||o.OS.android?Object(c.d)(f.container,{visibility:"visible",opacity:1}):Object(c.d)(f.container,{visibility:"",opacity:0})},this.setFullscreen=function(e){if(e=!!e){try{var t=v.webkitEnterFullscreen||v.webkitEnterFullScreen;t&&t.apply(v)}catch(e){return!1}return f.getFullScreen()}var i=v.webkitExitFullscreen||v.webkitExitFullScreen;return i&&i.apply(v),e},f.getFullScreen=function(){return _||!!v.webkitDisplayingFullscreen},this.setCurrentQuality=function(e){M!==e&&e>=0&&m&&m.length>e&&(M=e,y.reason="api",y.level={},this.trigger(r.J,{currentQuality:e,levels:te(m)}),t.qualityLabel=m[e].label,oe(f.getVideoCurrentTime()||0),ne())},this.setPlaybackRate=function(e){v.playbackRate=v.defaultPlaybackRate=e},this.getPlaybackRate=function(){return v.playbackRate},this.getCurrentQuality=function(){return M},this.getQualityLevels=function(){return Array.isArray(m)?m.map((function(e){return function(e){return{bitrate:e.bitrate,label:e.label,width:e.width,height:e.height}}(e)})):[]},this.getName=function(){return{name:W}},this.setCurrentAudioTrack=de,this.getAudioTracks=function(){return E||[]},this.getCurrentAudioTrack=function(){return A}}Object(n.g)(X.prototype,f.a),X.getName=function(){return{name:"html5"}};t.default=X;var K=220001},,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,i){"use strict";i.d(t,"a",(function(){return o}));var n=i(2);function o(e){var t=[],i=(e=Object(n.i)(e)).split("\r\n\r\n");1===i.length&&(i=e.split("\n\n"));for(var o=0;o0&&(o=0),i.length>o+1&&i[o+1]){var a=i[o],r=a.indexOf(" --\x3e ");r>0&&(t.begin=Object(n.g)(a.substr(0,r)),t.end=Object(n.g)(a.substr(r+5)),t.text=i.slice(o+1).join("\r\n"))}return t}},function(e,t,i){"use strict";i.d(t,"a",(function(){return o})),i.d(t,"b",(function(){return a}));var n=i(5);function o(e){var t=-1;return e>=1280?t=7:e>=960?t=6:e>=800?t=5:e>=640?t=4:e>=540?t=3:e>=420?t=2:e>=320?t=1:e>=250&&(t=0),t}function a(e,t){var i="jw-breakpoint-"+t;Object(n.p)(e,/jw-breakpoint--?\d+/,i)}},function(e,t,i){"use strict";i.d(t,"a",(function(){return d}));var n,o=i(0),a=i(8),r=i(16),s=i(7),l=i(3),c=i(10),u=i(5),d={back:!0,backgroundOpacity:50,edgeStyle:null,fontSize:14,fontOpacity:100,fontScale:.05,preprocessor:o.k,windowOpacity:0},p=function(e){var t,s,p,w,h,f,g,j,b,m=this,v=e.player;function y(){Object(o.o)(t.fontSize)&&(v.get("containerHeight")?j=d.fontScale*(t.userFontScale||1)*t.fontSize/d.fontSize:v.once("change:containerHeight",y,this))}function k(){var e=v.get("containerHeight");if(e){var t;if(v.get("fullscreen")&&a.OS.iOS)t=null;else{var i=e*j;t=Math.round(10*function(e){var t=v.get("mediaElement");if(t&&t.videoHeight){var i=t.videoWidth,n=t.videoHeight,o=i/n,r=v.get("containerHeight"),s=v.get("containerWidth");if(v.get("fullscreen")&&a.OS.mobile){var l=window.screen;l.orientation&&(r=l.availHeight,s=l.availWidth)}if(s&&r&&i&&n)return(s/r>o?r:n*s/i)*j}return e}(i))/10}v.get("renderCaptionsNatively")?function(e,t){var i="#".concat(e," .jw-video::-webkit-media-text-track-display");t&&(t+="px",a.OS.iOS&&Object(c.b)(i,{fontSize:"inherit"},e,!0));b.fontSize=t,Object(c.b)(i,b,e,!0)}(v.get("id"),t):Object(c.d)(h,{fontSize:t})}}function x(e,t,i){var n=Object(c.c)("#000000",i);"dropshadow"===e?t.textShadow="0 2px 1px "+n:"raised"===e?t.textShadow="0 0 5px "+n+", 0 1px 5px "+n+", 0 2px 5px "+n:"depressed"===e?t.textShadow="0 -2px 1px "+n:"uniform"===e&&(t.textShadow="-2px 0 1px "+n+",2px 0 1px "+n+",0 -2px 1px "+n+",0 2px 1px "+n+",-1px 1px 1px "+n+",1px 1px 1px "+n+",1px -1px 1px "+n+",1px 1px 1px "+n)}(h=document.createElement("div")).className="jw-captions jw-reset",this.show=function(){Object(u.a)(h,"jw-captions-enabled")},this.hide=function(){Object(u.o)(h,"jw-captions-enabled")},this.populate=function(e){v.get("renderCaptionsNatively")||(p=[],s=e,e?this.selectCues(e,w):this.renderCues())},this.resize=function(){k(),this.renderCues(!0)},this.renderCues=function(e){e=!!e,n&&n.processCues(window,p,h,e)},this.selectCues=function(e,t){if(e&&e.data&&t&&!v.get("renderCaptionsNatively")){var i=this.getAlignmentPosition(e,t);!1!==i&&(p=this.getCurrentCues(e.data,i),this.renderCues(!0))}},this.getCurrentCues=function(e,t){return Object(o.h)(e,(function(e){return t>=e.startTime&&(!e.endTime||t<=e.endTime)}))},this.getAlignmentPosition=function(e,t){var i=e.source,n=t.metadata,a=t.currentTime;return i&&n&&Object(o.r)(n[i])&&(a=n[i]),a},this.clear=function(){Object(u.g)(h)},this.setup=function(e,i){f=document.createElement("div"),g=document.createElement("span"),f.className="jw-captions-window jw-reset",g.className="jw-captions-text jw-reset",t=Object(o.g)({},d,i),j=d.fontScale;var n=function(){if(!v.get("renderCaptionsNatively")){y(t.fontSize);var i=t.windowColor,n=t.windowOpacity,o=t.edgeStyle;b={};var r={};!function(e,t){var i=t.color,n=t.fontOpacity;(i||n!==d.fontOpacity)&&(e.color=Object(c.c)(i||"#ffffff",n));if(t.back){var o=t.backgroundColor,a=t.backgroundOpacity;o===d.backgroundColor&&a===d.backgroundOpacity||(e.backgroundColor=Object(c.c)(o,a))}else e.background="transparent";t.fontFamily&&(e.fontFamily=t.fontFamily);t.fontStyle&&(e.fontStyle=t.fontStyle);t.fontWeight&&(e.fontWeight=t.fontWeight);t.textDecoration&&(e.textDecoration=t.textDecoration)}(r,t),(i||n!==d.windowOpacity)&&(b.backgroundColor=Object(c.c)(i||"#000000",n)),x(o,r,t.fontOpacity),t.back||null!==o||x("uniform",r),Object(c.d)(f,b),Object(c.d)(g,r),function(e,t){k(),function(e,t){a.Browser.safari&&Object(c.b)("#"+e+" .jw-video::-webkit-media-text-track-display-backdrop",{backgroundColor:t.backgroundColor},e,!0);Object(c.b)("#"+e+" .jw-video::-webkit-media-text-track-display",b,e,!0),Object(c.b)("#"+e+" .jw-video::cue",t,e,!0)}(e,t),function(e,t){Object(c.b)("#"+e+" .jw-text-track-display",b,e),Object(c.b)("#"+e+" .jw-text-track-cue",t,e)}(e,t)}(e,r)}};n(),f.appendChild(g),h.appendChild(f),v.change("captionsTrack",(function(e,t){this.populate(t)}),this),v.set("captions",t),v.on("change:captions",(function(e,i){t=i,n()}))},this.element=function(){return h},this.destroy=function(){v.off(null,null,this),this.off()};var T=function(e){w=e,m.selectCues(s,w)};v.on("change:playlistItem",(function(){w=null,p=[]}),this),v.on(l.Q,(function(e){p=[],T(e)}),this),v.on(l.S,T,this),v.on("subtitlesTrackData",(function(){this.selectCues(s,w)}),this),v.on("change:captionsList",(function e(t,o){var a=this;1!==o.length&&(t.get("renderCaptionsNatively")||n||(i.e(8).then(function(e){n=i(68).default}.bind(null,i)).catch(Object(r.c)(301121)).catch((function(e){a.trigger(l.tb,e)})),t.off("change:captionsList",e,this)))}),this)};Object(o.g)(p.prototype,s.a),t.b=p},function(e,t,i){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var i=function(e,t){var i=e[1]||"",n=e[3];if(!n)return i;if(t&&"function"==typeof btoa){var o=(r=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */"),a=n.sources.map((function(e){return"/*# sourceURL="+n.sourceRoot+e+" */"}));return[i].concat(a).concat([o]).join("\n")}var r;return[i].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+i+"}":i})).join("")},t.i=function(e,i){"string"==typeof e&&(e=[[null,e,""]]);for(var n={},o=0;o'},function(e,t,i){"use strict";function n(e,t){var i=e.kind||"cc";return e.default||e.defaulttrack?"default":e._id||e.file||i+t}function o(e,t){var i=e.label||e.name||e.language;return i||(i="Unknown CC",(t+=1)>1&&(i+=" ["+t+"]")),{label:i,unknownCount:t}}i.d(t,"a",(function(){return n})),i.d(t,"b",(function(){return o}))},function(e,t,i){"use strict";function n(e){return new Promise((function(t,i){if(e.paused)return i(o("NotAllowedError",0,"play() failed."));var n=function(){e.removeEventListener("play",a),e.removeEventListener("playing",r),e.removeEventListener("pause",r),e.removeEventListener("abort",r),e.removeEventListener("error",r)},a=function(){e.addEventListener("playing",r),e.addEventListener("abort",r),e.addEventListener("error",r),e.addEventListener("pause",r)},r=function(e){if(n(),"playing"===e.type)t();else{var a='The play() request was interrupted by a "'.concat(e.type,'" event.');"error"===e.type?i(o("NotSupportedError",9,a)):i(o("AbortError",20,a))}};e.addEventListener("play",a)}))}function o(e,t,i){var n=new Error(i);return n.name=e,n.code=t,n}i.d(t,"a",(function(){return n}))},function(e,t,i){"use strict";function n(e,t){return e!==1/0&&Math.abs(e)>=Math.max(a(t),0)}function o(e,t){var i="VOD";return e===1/0?i="LIVE":e<0&&(i=n(e,a(t))?"DVR":"LIVE"),i}function a(e){return void 0===e?120:Math.max(e,0)}i.d(t,"a",(function(){return n})),i.d(t,"b",(function(){return o}))},function(e,t,i){"use strict";var n=i(67),o=i(16),a=i(22),r=i(4),s=i(57),l=i(2),c=i(1);function u(e){throw new c.n(null,e)}function d(e,t,n){e.xhr=Object(a.a)(e.file,(function(a){!function(e,t,n,a){var d,p,h=e.responseXML?e.responseXML.firstChild:null;if(h)for("xml"===Object(r.b)(h)&&(h=h.nextSibling);h.nodeType===h.COMMENT_NODE;)h=h.nextSibling;try{if(h&&"tt"===Object(r.b)(h))d=function(e){e||u(306007);var t=[],i=e.getElementsByTagName("p"),n=30,o=e.getElementsByTagName("tt");if(o&&o[0]){var a=parseFloat(o[0].getAttribute("ttp:frameRate"));isNaN(a)||(n=a)}i||u(306005),i.length||(i=e.getElementsByTagName("tt:p")).length||(i=e.getElementsByTagName("tts:p"));for(var r=0;r\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(h){var f=s.getAttribute("begin"),g=s.getAttribute("dur"),j=s.getAttribute("end"),b={begin:Object(l.g)(f,n),text:h};j?b.end=Object(l.g)(j,n):g&&(b.end=b.begin+Object(l.g)(g,n)),t.push(b)}}return t.length||u(306005),t}(e.responseXML),p=w(d),delete t.xhr,n(p);else{var f=e.responseText;f.indexOf("WEBVTT")>=0?i.e(10).then(function(e){return i(97).default}.bind(null,i)).catch(Object(o.c)(301131)).then((function(e){var i=new e(window);p=[],i.oncue=function(e){p.push(e)},i.onflush=function(){delete t.xhr,n(p)},i.parse(f)})).catch((function(e){delete t.xhr,a(Object(c.v)(null,c.b,e))})):(d=Object(s.a)(f),p=w(d),delete t.xhr,n(p))}}catch(e){delete t.xhr,a(Object(c.v)(null,c.b,e))}}(a,e,t,n)}),(function(e,t,i,o){n(Object(c.u)(o,c.b))}))}function p(e){e&&e.forEach((function(e){var t=e.xhr;t&&(t.onload=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()),delete e.xhr}))}function w(e){return e.map((function(e){return new n.a(e.begin,e.end,e.text)}))}i.d(t,"c",(function(){return d})),i.d(t,"a",(function(){return p})),i.d(t,"b",(function(){return w}))},function(e,t,i){"use strict";var n=window.VTTCue;function o(e){if("string"!=typeof e)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[e.toLowerCase()]&&e.toLowerCase()}if(!n){(n=function(e,t,i){var n=this;n.hasBeenReset=!1;var a="",r=!1,s=e,l=t,c=i,u=null,d="",p=!0,w="auto",h="start",f="auto",g=100,j="middle";Object.defineProperty(n,"id",{enumerable:!0,get:function(){return a},set:function(e){a=""+e}}),Object.defineProperty(n,"pauseOnExit",{enumerable:!0,get:function(){return r},set:function(e){r=!!e}}),Object.defineProperty(n,"startTime",{enumerable:!0,get:function(){return s},set:function(e){if("number"!=typeof e)throw new TypeError("Start time must be set to a number.");s=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"endTime",{enumerable:!0,get:function(){return l},set:function(e){if("number"!=typeof e)throw new TypeError("End time must be set to a number.");l=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"text",{enumerable:!0,get:function(){return c},set:function(e){c=""+e,this.hasBeenReset=!0}}),Object.defineProperty(n,"region",{enumerable:!0,get:function(){return u},set:function(e){u=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"vertical",{enumerable:!0,get:function(){return d},set:function(e){var t=function(e){return"string"==typeof e&&(!!{"":!0,lr:!0,rl:!0}[e.toLowerCase()]&&e.toLowerCase())}(e);if(!1===t)throw new SyntaxError("An invalid or illegal string was specified.");d=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"snapToLines",{enumerable:!0,get:function(){return p},set:function(e){p=!!e,this.hasBeenReset=!0}}),Object.defineProperty(n,"line",{enumerable:!0,get:function(){return w},set:function(e){if("number"!=typeof e&&"auto"!==e)throw new SyntaxError("An invalid number or illegal string was specified.");w=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"lineAlign",{enumerable:!0,get:function(){return h},set:function(e){var t=o(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");h=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"position",{enumerable:!0,get:function(){return f},set:function(e){if(e<0||e>100)throw new Error("Position must be between 0 and 100.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"size",{enumerable:!0,get:function(){return g},set:function(e){if(e<0||e>100)throw new Error("Size must be between 0 and 100.");g=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"align",{enumerable:!0,get:function(){return j},set:function(e){var t=o(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");j=t,this.hasBeenReset=!0}}),n.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}t.a=n},,function(e,t,i){var n=i(70);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(e.exports=n.locals)},function(e,t,i){(e.exports=i(60)(!1)).push([e.i,'.jw-reset{text-align:left;direction:ltr}.jw-reset-text,.jw-reset{color:inherit;background-color:transparent;padding:0;margin:0;float:none;font-family:Arial,Helvetica,sans-serif;font-size:1em;line-height:1em;list-style:none;text-transform:none;vertical-align:baseline;border:0;font-variant:inherit;font-stretch:inherit;-webkit-tap-highlight-color:rgba(255,255,255,0)}body .jw-error,body .jwplayer.jw-state-error{height:100%;width:100%}.jw-title{position:absolute;top:0}.jw-background-color{background:rgba(0,0,0,0.4)}.jw-text{color:rgba(255,255,255,0.8)}.jw-knob{color:rgba(255,255,255,0.8);background-color:#fff}.jw-button-color{color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):focus,:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):hover{color:#fff}.jw-toggle{color:#fff}.jw-toggle.jw-off{color:rgba(255,255,255,0.8)}.jw-toggle.jw-off:focus{color:#fff}.jw-toggle:focus{outline:none}:not(.jw-flag-touch) .jw-toggle.jw-off:hover{color:#fff}.jw-rail{background:rgba(255,255,255,0.3)}.jw-buffer{background:rgba(255,255,255,0.3)}.jw-progress{background:#f2f2f2}.jw-time-tip,.jw-volume-tip{border:0}.jw-slider-volume.jw-volume-tip.jw-background-color.jw-slider-vertical{background:none}.jw-skip{padding:.5em;outline:none}.jw-skip .jw-skiptext,.jw-skip .jw-skip-icon{color:rgba(255,255,255,0.8)}.jw-skip.jw-skippable:hover .jw-skip-icon,.jw-skip.jw-skippable:focus .jw-skip-icon{color:#fff}.jw-icon-cast google-cast-launcher{--connected-color:#fff;--disconnected-color:rgba(255,255,255,0.8)}.jw-icon-cast google-cast-launcher:focus{outline:none}.jw-icon-cast google-cast-launcher.jw-off{--connected-color:rgba(255,255,255,0.8)}.jw-icon-cast:focus google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-icon-cast:hover google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-nextup-container{bottom:2.5em;padding:5px .5em}.jw-nextup{border-radius:0}.jw-color-active{color:#fff;stroke:#fff;border-color:#fff}:not(.jw-flag-touch) .jw-color-active-hover:hover,:not(.jw-flag-touch) .jw-color-active-hover:focus{color:#fff;stroke:#fff;border-color:#fff}.jw-color-inactive{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-color-inactive-hover:hover{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}.jw-option{color:rgba(255,255,255,0.8)}.jw-option.jw-active-option{color:#fff;background-color:rgba(255,255,255,0.1)}:not(.jw-flag-touch) .jw-option:hover{color:#fff}.jwplayer{width:100%;font-size:16px;position:relative;display:block;min-height:0;overflow:hidden;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:none}.jwplayer *{box-sizing:inherit}.jwplayer.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jwplayer.jw-flag-aspect-mode{height:auto !important}.jwplayer.jw-flag-aspect-mode .jw-aspect{display:block}.jwplayer .jw-aspect{display:none}.jwplayer .jw-swf{outline:none}.jw-media,.jw-preview{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}.jw-media{overflow:hidden;cursor:pointer}.jw-plugin{position:absolute;bottom:66px}.jw-breakpoint-7 .jw-plugin{bottom:132px}.jw-plugin .jw-banner{max-width:100%;opacity:0;cursor:pointer;position:absolute;margin:auto auto 0;left:0;right:0;bottom:0;display:block}.jw-preview,.jw-captions,.jw-title{pointer-events:none}.jw-media,.jw-logo{pointer-events:all}.jw-wrapper{background-color:#000;position:absolute;top:0;left:0;right:0;bottom:0}.jw-hidden-accessibility{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.jw-contract-trigger::before{content:"";overflow:hidden;width:200%;height:200%;display:block;position:absolute;top:0;left:0}.jwplayer .jw-media video{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;margin:auto;background:transparent}.jwplayer .jw-media video::-webkit-media-controls-start-playback-button{display:none}.jwplayer.jw-stretch-uniform .jw-media video{object-fit:contain}.jwplayer.jw-stretch-none .jw-media video{object-fit:none}.jwplayer.jw-stretch-fill .jw-media video{object-fit:cover}.jwplayer.jw-stretch-exactfit .jw-media video{object-fit:fill}.jw-preview{position:absolute;display:none;opacity:1;visibility:visible;width:100%;height:100%;background:#000 no-repeat 50% 50%}.jwplayer .jw-preview,.jw-error .jw-preview{background-size:contain}.jw-stretch-none .jw-preview{background-size:auto auto}.jw-stretch-fill .jw-preview{background-size:cover}.jw-stretch-exactfit .jw-preview{background-size:100% 100%}.jw-title{display:none;padding-top:20px;width:100%;z-index:1}.jw-title-primary,.jw-title-secondary{color:#fff;padding-left:20px;padding-right:20px;padding-bottom:.5em;overflow:hidden;text-overflow:ellipsis;direction:unset;white-space:nowrap;width:100%}.jw-title-primary{font-size:1.625em}.jw-breakpoint-2 .jw-title-primary,.jw-breakpoint-3 .jw-title-primary{font-size:1.5em}.jw-flag-small-player .jw-title-primary{font-size:1.25em}.jw-flag-small-player .jw-title-secondary,.jw-title-secondary:empty{display:none}.jw-captions{position:absolute;width:100%;height:100%;text-align:center;display:none;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-decoration:none;pointer-events:none;overflow:hidden;top:0}.jw-captions.jw-captions-enabled{display:block}.jw-captions-window{display:none;padding:.25em;border-radius:.25em}.jw-captions-window.jw-captions-window-active{display:inline-block}.jw-captions-text{display:inline-block;color:#fff;background-color:#000;word-wrap:normal;word-break:normal;white-space:pre-line;font-style:normal;font-weight:normal;text-align:center;text-decoration:none}.jw-text-track-display{font-size:inherit;line-height:1.5}.jw-text-track-cue{background-color:rgba(0,0,0,0.5);color:#fff;padding:.1em .3em}.jwplayer video::-webkit-media-controls{display:none;justify-content:flex-start}.jwplayer video::-webkit-media-text-track-display{min-width:-webkit-min-content}.jwplayer video::cue{background-color:rgba(0,0,0,0.5)}.jwplayer video::-webkit-media-controls-panel-container{display:none}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing) .jw-captions,.jwplayer.jw-flag-media-audio.jw-state-playing .jw-captions,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden) .jw-captions{max-height:calc(100% - 60px)}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-flag-media-audio.jw-state-playing:not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container{max-height:calc(100% - 60px)}.jw-logo{position:absolute;margin:20px;cursor:pointer;pointer-events:all;background-repeat:no-repeat;background-size:contain;top:auto;right:auto;left:auto;bottom:auto;outline:none}.jw-logo.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-flag-audio-player .jw-logo{display:none}.jw-logo-top-right{top:0;right:0}.jw-logo-top-left{top:0;left:0}.jw-logo-bottom-left{left:0}.jw-logo-bottom-right{right:0}.jw-logo-bottom-left,.jw-logo-bottom-right{bottom:44px;transition:bottom 150ms cubic-bezier(0, .25, .25, 1)}.jw-state-idle .jw-logo{z-index:1}.jw-state-setup .jw-wrapper{background-color:inherit}.jw-state-setup .jw-logo,.jw-state-setup .jw-controls,.jw-state-setup .jw-controls-backdrop{visibility:hidden}span.jw-break{display:block}body .jw-error,body .jwplayer.jw-state-error{background-color:#333;color:#fff;font-size:16px;display:table;opacity:1;position:relative}body .jw-error .jw-display,body .jwplayer.jw-state-error .jw-display{display:none}body .jw-error .jw-media,body .jwplayer.jw-state-error .jw-media{cursor:default}body .jw-error .jw-preview,body .jwplayer.jw-state-error .jw-preview{background-color:#333}body .jw-error .jw-error-msg,body .jwplayer.jw-state-error .jw-error-msg{background-color:#000;border-radius:2px;display:flex;flex-direction:row;align-items:stretch;padding:20px}body .jw-error .jw-error-msg .jw-icon,body .jwplayer.jw-state-error .jw-error-msg .jw-icon{height:30px;width:30px;margin-right:20px;flex:0 0 auto;align-self:center}body .jw-error .jw-error-msg .jw-icon:empty,body .jwplayer.jw-state-error .jw-error-msg .jw-icon:empty{display:none}body .jw-error .jw-error-msg .jw-info-container,body .jwplayer.jw-state-error .jw-error-msg .jw-info-container{margin:0;padding:0}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg{flex-direction:column}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text{text-align:center}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon{flex:.5 0 auto;margin-right:0;margin-bottom:20px}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break{display:inline}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break:before{content:" "}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg{height:100%;width:100%;top:0;position:absolute;left:0;background:#000;-webkit-transform:none;transform:none;padding:4px 16px;z-index:1}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg.jw-info-overlay{max-width:none;max-height:none}body .jwplayer.jw-state-error .jw-title,.jw-state-idle .jw-title,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-title{display:block}body .jwplayer.jw-state-error .jw-preview,.jw-state-idle .jw-preview,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-preview{display:block}.jw-state-idle .jw-captions,.jwplayer.jw-state-complete .jw-captions,body .jwplayer.jw-state-error .jw-captions{display:none}.jw-state-idle video::-webkit-media-text-track-container,.jwplayer.jw-state-complete video::-webkit-media-text-track-container,body .jwplayer.jw-state-error video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-fullscreen{width:100% !important;height:100% !important;top:0;right:0;bottom:0;left:0;z-index:1000;margin:0;position:fixed}body .jwplayer.jw-flag-flash-blocked .jw-title{display:block}.jwplayer.jw-flag-controls-hidden .jw-media{cursor:default}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:45px}.jw-flag-floating{background-size:cover;background-color:#000}.jw-flag-floating .jw-wrapper{position:fixed;z-index:2147483647;-webkit-animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;top:auto;bottom:1rem;left:auto;right:1rem;max-width:400px;max-height:400px;margin:0 auto}@media screen and (max-width:480px){.jw-flag-floating .jw-wrapper{width:100%;left:0;right:0}}.jw-flag-floating .jw-wrapper .jw-media{touch-action:none}@media screen and (max-device-width:480px) and (orientation:portrait){.jw-flag-touch.jw-flag-floating .jw-wrapper{-webkit-animation:none;animation:none;top:62px;bottom:auto;left:0;right:0;max-width:none;max-height:none}}.jw-flag-floating .jw-float-icon{pointer-events:all;cursor:pointer;display:none}.jw-flag-floating .jw-float-icon .jw-svg-icon{-webkit-filter:drop-shadow(0 0 1px #000);filter:drop-shadow(0 0 1px #000)}.jw-flag-floating.jw-floating-dismissible .jw-dismiss-icon{display:none}.jw-flag-floating.jw-floating-dismissible.jw-flag-ads .jw-float-icon{display:flex}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-logo,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-logo{display:none}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-float-icon,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-float-icon{display:flex}.jw-float-icon{display:none;position:absolute;top:3px;right:5px;align-items:center;justify-content:center}@-webkit-keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.jw-flag-top{margin-top:2em;overflow:visible}.jw-top{height:2em;line-height:2;pointer-events:none;text-align:center;opacity:.8;position:absolute;top:-2em;width:100%}.jw-top .jw-icon{cursor:pointer;pointer-events:all;height:auto;width:auto}.jw-top .jw-text{color:#555}',""])},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t,i){var n=i(96);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(e.exports=n.locals)},function(e,t,i){(e.exports=i(60)(!1)).push([e.i,'.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-flag-small-player .jw-settings-menu,.jw-settings-submenu{height:100%;width:100%}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;right:0}.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-settings-item-active::before{top:0;position:absolute;left:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;bottom:0;left:0}.jw-nextup-close{position:absolute;top:0;right:0}.jw-overlays,.jw-controls,.jw-flag-small-player .jw-settings-menu{position:absolute;bottom:0;right:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-time-tip::after,.jw-settings-menu .jw-icon.jw-button-color::after,.jw-text-live::before,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{content:"";display:block}.jw-svg-icon{height:24px;width:24px;fill:currentColor;pointer-events:none}.jw-icon{height:44px;width:44px;background-color:transparent;outline:none}.jw-icon.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-icon-airplay .jw-svg-icon-airplay-off{display:none}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-off{display:block}.jw-icon-airplay .jw-svg-icon-airplay-on{display:block}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-on{display:none}.jw-icon-cc .jw-svg-icon-cc-off{display:none}.jw-off.jw-icon-cc .jw-svg-icon-cc-off{display:block}.jw-icon-cc .jw-svg-icon-cc-on{display:block}.jw-off.jw-icon-cc .jw-svg-icon-cc-on{display:none}.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:none}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:block}.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:block}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:none}.jw-icon-volume .jw-svg-icon-volume-0{display:none}.jw-off.jw-icon-volume .jw-svg-icon-volume-0{display:block}.jw-icon-volume .jw-svg-icon-volume-100{display:none}.jw-full.jw-icon-volume .jw-svg-icon-volume-100{display:block}.jw-icon-volume .jw-svg-icon-volume-50{display:block}.jw-off.jw-icon-volume .jw-svg-icon-volume-50,.jw-full.jw-icon-volume .jw-svg-icon-volume-50{display:none}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon[aria-checked="true"]::after,.jw-settings-open .jw-icon-settings::after,.jw-icon-volume.jw-open::after{opacity:1}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-cc,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-settings,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-audio-tracks,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-hd,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-settings-sharing,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-fullscreen,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-airplay,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-cast{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-text-live{bottom:6px}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume::after{display:none}.jw-overlays,.jw-controls{pointer-events:none}.jw-controls-backdrop{display:block;background:linear-gradient(to bottom, transparent, rgba(0,0,0,0.4) 77%, rgba(0,0,0,0.4) 100%) 100% 100% / 100% 240px no-repeat transparent;transition:opacity 250ms cubic-bezier(0, .25, .25, 1),background-size 250ms cubic-bezier(0, .25, .25, 1);pointer-events:none}.jw-overlays{cursor:auto}.jw-controls{overflow:hidden}.jw-flag-small-player .jw-controls{text-align:center}.jw-text{height:1em;font-family:Arial,Helvetica,sans-serif;font-size:.75em;font-style:normal;font-weight:normal;color:#fff;text-align:center;font-variant:normal;font-stretch:normal}.jw-controlbar,.jw-skip,.jw-display-icon-container .jw-icon,.jw-nextup-container,.jw-autostart-mute,.jw-overlays .jw-plugin{pointer-events:all}.jwplayer .jw-display-icon-container,.jw-error .jw-display-icon-container{width:auto;height:auto;box-sizing:content-box}.jw-display{display:table;height:100%;padding:57px 0;position:relative;width:100%}.jw-flag-dragging .jw-display{display:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-display-container{display:table-cell;height:100%;text-align:center;vertical-align:middle}.jw-display-controls{display:inline-block}.jwplayer .jw-display-icon-container{float:left}.jw-display-icon-container{display:inline-block;padding:5.5px;margin:0 22px}.jw-display-icon-container .jw-icon{height:75px;width:75px;cursor:pointer;display:flex;justify-content:center;align-items:center}.jw-display-icon-container .jw-icon .jw-svg-icon{height:33px;width:33px;padding:0;position:relative}.jw-display-icon-container .jw-icon .jw-svg-icon-rewind{padding:.2em .05em}.jw-breakpoint--1 .jw-nextup-container{display:none}.jw-breakpoint-0 .jw-display-icon-next,.jw-breakpoint--1 .jw-display-icon-next,.jw-breakpoint-0 .jw-display-icon-rewind,.jw-breakpoint--1 .jw-display-icon-rewind{display:none}.jw-breakpoint-0 .jw-display .jw-icon,.jw-breakpoint--1 .jw-display .jw-icon,.jw-breakpoint-0 .jw-display .jw-svg-icon,.jw-breakpoint--1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-0 .jw-display .jw-icon:before,.jw-breakpoint--1 .jw-display .jw-icon:before,.jw-breakpoint-0 .jw-display .jw-svg-icon:before,.jw-breakpoint--1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon,.jw-breakpoint-1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-1 .jw-display .jw-icon:before,.jw-breakpoint-1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon.jw-icon-rewind:before{width:33px;height:33px}.jw-breakpoint-2 .jw-display .jw-icon,.jw-breakpoint-3 .jw-display .jw-icon,.jw-breakpoint-2 .jw-display .jw-svg-icon,.jw-breakpoint-3 .jw-display .jw-svg-icon{width:77px;height:77px;line-height:77px}.jw-breakpoint-2 .jw-display .jw-icon:before,.jw-breakpoint-3 .jw-display .jw-icon:before,.jw-breakpoint-2 .jw-display .jw-svg-icon:before,.jw-breakpoint-3 .jw-display .jw-svg-icon:before{width:38.5px;height:38.5px}.jw-breakpoint-4 .jw-display .jw-icon,.jw-breakpoint-5 .jw-display .jw-icon,.jw-breakpoint-6 .jw-display .jw-icon,.jw-breakpoint-7 .jw-display .jw-icon,.jw-breakpoint-4 .jw-display .jw-svg-icon,.jw-breakpoint-5 .jw-display .jw-svg-icon,.jw-breakpoint-6 .jw-display .jw-svg-icon,.jw-breakpoint-7 .jw-display .jw-svg-icon{width:88px;height:88px;line-height:88px}.jw-breakpoint-4 .jw-display .jw-icon:before,.jw-breakpoint-5 .jw-display .jw-icon:before,.jw-breakpoint-6 .jw-display .jw-icon:before,.jw-breakpoint-7 .jw-display .jw-icon:before,.jw-breakpoint-4 .jw-display .jw-svg-icon:before,.jw-breakpoint-5 .jw-display .jw-svg-icon:before,.jw-breakpoint-6 .jw-display .jw-svg-icon:before,.jw-breakpoint-7 .jw-display .jw-svg-icon:before{width:44px;height:44px}.jw-controlbar{display:flex;flex-flow:row wrap;align-items:center;justify-content:center;position:absolute;left:0;bottom:0;width:100%;border:none;border-radius:0;background-size:auto;box-shadow:none;max-height:72px;transition:250ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s}.jw-breakpoint-7 .jw-controlbar{max-height:140px}.jw-breakpoint-7 .jw-controlbar .jw-button-container{padding:0 48px 20px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-tooltip{margin-bottom:-7px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-overlay{padding-bottom:40%}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text{font-size:1em}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text.jw-text-elapsed{justify-content:flex-end}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume{height:60px;width:60px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline .jw-svg-icon,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time{padding:0 60px;height:34px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time .jw-slider-container{height:10px}.jw-controlbar .jw-button-image{background:no-repeat 50% 50%;background-size:contain;max-height:24px}.jw-controlbar .jw-spacer{flex:1 1 auto;align-self:stretch}.jw-controlbar .jw-icon.jw-button-color:hover{color:#fff}.jw-button-container{display:flex;flex-flow:row nowrap;flex:1 1 auto;align-items:center;justify-content:center;width:100%;padding:0 12px}.jw-slider-horizontal{background-color:transparent}.jw-icon-inline{position:relative}.jw-icon-inline,.jw-icon-tooltip{height:44px;width:44px;align-items:center;display:flex;justify-content:center}.jw-icon-inline:not(.jw-text),.jw-icon-tooltip,.jw-slider-horizontal{cursor:pointer}.jw-text-elapsed,.jw-text-duration{justify-content:flex-start;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.jw-icon-tooltip{position:relative}.jw-knob:hover,.jw-icon-inline:hover,.jw-icon-tooltip:hover,.jw-icon-display:hover,.jw-option:before:hover{color:#fff}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{pointer-events:none}.jw-icon-cast{display:none;margin:0;padding:0}.jw-icon-cast google-cast-launcher{background-color:transparent;border:none;padding:0;width:24px;height:24px;cursor:pointer}.jw-icon-inline.jw-icon-volume{display:none}.jwplayer .jw-text-countdown{display:none}.jw-flag-small-player .jw-display{padding-top:0;padding-bottom:0}.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-rewind,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-next,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-playback{display:none}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop{opacity:0}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-countdown{display:flex}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-duration,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-duration{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-text-countdown,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-related-btn,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-slider-volume{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-controlbar{flex-direction:column-reverse}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-button-container{height:30px}.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-volume,.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-fullscreen{display:none}.jwplayer:not(.jw-breakpoint-0) .jw-text-duration:before,.jwplayer:not(.jw-breakpoint--1) .jw-text-duration:before{content:"/";padding-right:1ch;padding-left:1ch}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar{will-change:transform}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar .jw-text{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.jw-slider-container{display:flex;align-items:center;position:relative;touch-action:none}.jw-rail,.jw-buffer,.jw-progress{position:absolute;cursor:pointer}.jw-progress{background-color:#f2f2f2}.jw-rail{background-color:rgba(255,255,255,0.3)}.jw-buffer{background-color:rgba(255,255,255,0.3)}.jw-knob{height:13px;width:13px;background-color:#fff;border-radius:50%;box-shadow:0 0 10px rgba(0,0,0,0.4);opacity:1;pointer-events:none;position:absolute;-webkit-transform:translate(-50%, -50%) scale(0);transform:translate(-50%, -50%) scale(0);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform}.jw-flag-dragging .jw-slider-time .jw-knob,.jw-icon-volume:active .jw-slider-volume .jw-knob{box-shadow:0 0 26px rgba(0,0,0,0.2),0 0 10px rgba(0,0,0,0.4),0 0 0 6px rgba(255,255,255,0.2)}.jw-slider-horizontal,.jw-slider-vertical{display:flex}.jw-slider-horizontal .jw-slider-container{height:5px;width:100%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue,.jw-slider-horizontal .jw-knob{top:50%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue{-webkit-transform:translate(0, -50%);transform:translate(0, -50%)}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress{height:5px}.jw-slider-horizontal .jw-rail{width:100%}.jw-slider-vertical{align-items:center;flex-direction:column}.jw-slider-vertical .jw-slider-container{height:88px;width:5px}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress,.jw-slider-vertical .jw-knob{left:50%}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress{height:100%;width:5px;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out;bottom:0}.jw-slider-vertical .jw-knob{-webkit-transform:translate(-50%, 50%);transform:translate(-50%, 50%)}.jw-slider-time.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-slider-time,.jw-flag-audio-player .jw-slider-volume{height:17px;width:100%;align-items:center;background:transparent none;padding:0 12px}.jw-slider-time .jw-cue{background-color:rgba(33,33,33,0.8);cursor:pointer;position:absolute;width:6px}.jw-slider-time,.jw-horizontal-volume-container{z-index:1;outline:none}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail,.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer,.jw-slider-time .jw-progress,.jw-horizontal-volume-container .jw-progress,.jw-slider-time .jw-cue,.jw-horizontal-volume-container .jw-cue{-webkit-backface-visibility:hidden;backface-visibility:hidden;height:100%;-webkit-transform:translate(0, -50%) scale(1, .6);transform:translate(0, -50%) scale(1, .6);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out}.jw-slider-time:hover .jw-rail,.jw-horizontal-volume-container:hover .jw-rail,.jw-slider-time:focus .jw-rail,.jw-horizontal-volume-container:focus .jw-rail,.jw-flag-dragging .jw-slider-time .jw-rail,.jw-flag-dragging .jw-horizontal-volume-container .jw-rail,.jw-flag-touch .jw-slider-time .jw-rail,.jw-flag-touch .jw-horizontal-volume-container .jw-rail,.jw-slider-time:hover .jw-buffer,.jw-horizontal-volume-container:hover .jw-buffer,.jw-slider-time:focus .jw-buffer,.jw-horizontal-volume-container:focus .jw-buffer,.jw-flag-dragging .jw-slider-time .jw-buffer,.jw-flag-dragging .jw-horizontal-volume-container .jw-buffer,.jw-flag-touch .jw-slider-time .jw-buffer,.jw-flag-touch .jw-horizontal-volume-container .jw-buffer,.jw-slider-time:hover .jw-progress,.jw-horizontal-volume-container:hover .jw-progress,.jw-slider-time:focus .jw-progress,.jw-horizontal-volume-container:focus .jw-progress,.jw-flag-dragging .jw-slider-time .jw-progress,.jw-flag-dragging .jw-horizontal-volume-container .jw-progress,.jw-flag-touch .jw-slider-time .jw-progress,.jw-flag-touch .jw-horizontal-volume-container .jw-progress,.jw-slider-time:hover .jw-cue,.jw-horizontal-volume-container:hover .jw-cue,.jw-slider-time:focus .jw-cue,.jw-horizontal-volume-container:focus .jw-cue,.jw-flag-dragging .jw-slider-time .jw-cue,.jw-flag-dragging .jw-horizontal-volume-container .jw-cue,.jw-flag-touch .jw-slider-time .jw-cue,.jw-flag-touch .jw-horizontal-volume-container .jw-cue{-webkit-transform:translate(0, -50%) scale(1, 1);transform:translate(0, -50%) scale(1, 1)}.jw-slider-time:hover .jw-knob,.jw-horizontal-volume-container:hover .jw-knob,.jw-slider-time:focus .jw-knob,.jw-horizontal-volume-container:focus .jw-knob{-webkit-transform:translate(-50%, -50%) scale(1);transform:translate(-50%, -50%) scale(1)}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail{background-color:rgba(255,255,255,0.2)}.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer{background-color:rgba(255,255,255,0.4)}.jw-flag-touch .jw-slider-time::before,.jw-flag-touch .jw-horizontal-volume-container::before{height:44px;width:100%;content:"";position:absolute;display:block;bottom:calc(100% - 17px);left:0}.jw-slider-time.jw-tab-focus:focus .jw-rail,.jw-horizontal-volume-container.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time{height:17px;padding:0}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-slider-container{height:10px}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-knob{border-radius:0;border:1px solid rgba(0,0,0,0.75);height:12px;width:10px}.jw-modal{width:284px}.jw-breakpoint-7 .jw-modal,.jw-breakpoint-6 .jw-modal,.jw-breakpoint-5 .jw-modal{height:232px}.jw-breakpoint-4 .jw-modal,.jw-breakpoint-3 .jw-modal{height:192px}.jw-breakpoint-2 .jw-modal,.jw-flag-small-player .jw-modal{bottom:0;right:0;height:100%;width:100%;max-height:none;max-width:none;z-index:2}.jwplayer .jw-rightclick{display:none;position:absolute;white-space:nowrap}.jwplayer .jw-rightclick.jw-open{display:block}.jwplayer .jw-rightclick .jw-rightclick-list{border-radius:1px;list-style:none;margin:0;padding:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item{background-color:rgba(0,0,0,0.8);border-bottom:1px solid #444;margin:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo{color:#fff;display:inline-flex;padding:0 10px 0 0;vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo .jw-svg-icon{height:20px;width:20px}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-link{border:none;color:#fff;display:block;font-size:11px;line-height:1em;padding:15px 23px;text-align:start;text-decoration:none;width:100%}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:last-child{border-bottom:none}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:hover{cursor:pointer}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured{vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link{color:#fff}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link span{color:#fff}.jwplayer .jw-rightclick .jw-info-overlay-item,.jwplayer .jw-rightclick .jw-share-item,.jwplayer .jw-rightclick .jw-shortcuts-item{border:none;background-color:transparent;outline:none;cursor:pointer}.jw-icon-tooltip.jw-open .jw-overlay{opacity:1;pointer-events:auto;transition-delay:0s}.jw-icon-tooltip.jw-open .jw-overlay:focus{outline:none}.jw-icon-tooltip.jw-open .jw-overlay:focus.jw-tab-focus{outline:solid 2px #4d90fe}.jw-slider-time .jw-overlay:before{height:1em;top:auto}.jw-slider-time .jw-icon-tooltip.jw-open .jw-overlay{pointer-events:none}.jw-volume-tip{padding:13px 0 26px}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{height:auto;width:100%;box-shadow:0 0 10px rgba(0,0,0,0.4);color:#fff;display:block;margin:0 0 14px;pointer-events:none;position:relative;z-index:0}.jw-time-tip::after,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{top:100%;position:absolute;left:50%;height:14px;width:14px;border-radius:1px;background-color:currentColor;-webkit-transform-origin:75% 50%;transform-origin:75% 50%;-webkit-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg);z-index:-1}.jw-time-tip .jw-text,.jw-controlbar .jw-tooltip .jw-text,.jw-settings-menu .jw-tooltip .jw-text{background-color:#fff;border-radius:1px;color:#000;font-size:10px;height:auto;line-height:1;padding:7px 10px;display:inline-block;min-width:100%;vertical-align:middle}.jw-controlbar .jw-overlay{position:absolute;bottom:100%;left:50%;margin:0;min-height:44px;min-width:44px;opacity:0;pointer-events:none;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s, 150ms;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);width:100%;z-index:1}.jw-controlbar .jw-overlay .jw-contents{position:relative}.jw-controlbar .jw-option{position:relative;white-space:nowrap;cursor:pointer;list-style:none;height:1.5em;font-family:inherit;line-height:1.5em;padding:0 .5em;font-size:.8em;margin:0}.jw-controlbar .jw-option::before{padding-right:.125em}.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{position:absolute;bottom:100%;left:50%;opacity:0;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:100ms 0s cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility, -webkit-transform;transition-property:opacity, transform, visibility;transition-property:opacity, transform, visibility, -webkit-transform;visibility:hidden;white-space:nowrap;width:auto;z-index:1}.jw-controlbar .jw-tooltip.jw-open,.jw-settings-menu .jw-tooltip.jw-open{opacity:1;-webkit-transform:translate(-50%, -10px);transform:translate(-50%, -10px);transition-duration:150ms;transition-delay:500ms,0s,500ms;visibility:visible}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen{left:auto;right:0;-webkit-transform:translate(0, 0);transform:translate(0, 0)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen.jw-open,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen.jw-open{-webkit-transform:translate(0, -10px);transform:translate(0, -10px)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen::after,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen::after{left:auto;right:9px}.jw-tooltip-time{height:auto;width:0;bottom:100%;line-height:normal;padding:0;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jw-tooltip-time .jw-overlay{bottom:0;min-height:0;width:auto}.jw-tooltip{bottom:57px;display:none;position:absolute}.jw-tooltip .jw-text{height:100%;white-space:nowrap;text-overflow:ellipsis;direction:unset;max-width:246px;overflow:hidden}.jw-flag-audio-player .jw-tooltip{display:none}.jw-flag-small-player .jw-time-thumb{display:none}.jwplayer .jw-shortcuts-tooltip{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column;z-index:1}.jwplayer .jw-shortcuts-tooltip.jw-open{display:flex}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-close{flex:0 0 auto;margin:5px 5px 5px auto}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container{display:flex;flex:1 1 auto;flex-flow:column;font-size:12px;margin:0 20px 20px;overflow-y:auto;padding:5px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar{background-color:transparent;width:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-title{font-weight:bold}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list{display:flex;max-width:340px;margin:0 10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-tooltip-descriptions{width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row{display:flex;align-items:center;justify-content:space-between;margin:10px 0;width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-description{margin-right:10px;max-width:70%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-key{background:#fefefe;color:#333;overflow:hidden;padding:7px 10px;text-overflow:ellipsis;white-space:nowrap}.jw-skip{color:rgba(255,255,255,0.8);cursor:default;position:absolute;display:flex;right:.75em;bottom:56px;padding:.5em;border:1px solid #333;background-color:#000;align-items:center;height:2em}.jw-skip.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-skip.jw-skippable{cursor:pointer;padding:.25em .75em}.jw-skip.jw-skippable:hover{cursor:pointer;color:#fff}.jw-skip.jw-skippable .jw-skip-icon{display:inline;height:24px;width:24px;margin:0}.jw-breakpoint-7 .jw-skip{padding:1.35em 1em;bottom:130px}.jw-breakpoint-7 .jw-skip .jw-text{font-size:1em;font-weight:normal}.jw-breakpoint-7 .jw-skip .jw-icon-inline{height:30px;width:30px}.jw-breakpoint-7 .jw-skip .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-skip .jw-skip-icon{display:none;margin-left:-0.75em;padding:0 .5em;pointer-events:none}.jw-skip .jw-skip-icon .jw-svg-icon-next{display:block;padding:0}.jw-skip .jw-text,.jw-skip .jw-skip-icon{vertical-align:middle;font-size:.7em}.jw-skip .jw-text{font-weight:bold}.jw-cast{background-size:cover;display:none;height:100%;position:relative;width:100%}.jw-cast-container{background:linear-gradient(180deg, rgba(25,25,25,0.75), rgba(25,25,25,0.25), rgba(25,25,25,0));left:0;padding:20px 20px 80px;position:absolute;top:0;width:100%}.jw-cast-text{color:#fff;font-size:1.6em}.jw-breakpoint--1 .jw-cast-text,.jw-breakpoint-0 .jw-cast-text{font-size:1.15em}.jw-breakpoint-1 .jw-cast-text,.jw-breakpoint-2 .jw-cast-text,.jw-breakpoint-3 .jw-cast-text{font-size:1.3em}.jw-nextup-container{position:absolute;bottom:66px;left:0;background-color:transparent;cursor:pointer;margin:0 auto;padding:12px;pointer-events:none;right:0;text-align:right;visibility:hidden;width:100%}.jw-settings-open .jw-nextup-container,.jw-info-open .jw-nextup-container{display:none}.jw-breakpoint-7 .jw-nextup-container{padding:60px}.jw-flag-small-player .jw-nextup-container{padding:0 12px 0 0}.jw-flag-small-player .jw-nextup-container .jw-nextup-title,.jw-flag-small-player .jw-nextup-container .jw-nextup-duration,.jw-flag-small-player .jw-nextup-container .jw-nextup-close{display:none}.jw-flag-small-player .jw-nextup-container .jw-nextup-tooltip{height:30px}.jw-flag-small-player .jw-nextup-container .jw-nextup-header{font-size:12px}.jw-flag-small-player .jw-nextup-container .jw-nextup-body{justify-content:center;align-items:center;padding:.75em .3em}.jw-flag-small-player .jw-nextup-container .jw-nextup-thumbnail{width:50%}.jw-flag-small-player .jw-nextup-container .jw-nextup{max-width:65px}.jw-flag-small-player .jw-nextup-container .jw-nextup.jw-nextup-thumbnail-visible{max-width:120px}.jw-nextup{background:#333;border-radius:0;box-shadow:0 0 10px rgba(0,0,0,0.5);color:rgba(255,255,255,0.8);display:inline-block;max-width:280px;overflow:hidden;opacity:0;position:relative;width:64%;pointer-events:all;-webkit-transform:translate(0, -5px);transform:translate(0, -5px);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform;transition-delay:0s}.jw-nextup:hover .jw-nextup-tooltip{color:#fff}.jw-nextup.jw-nextup-thumbnail-visible{max-width:400px}.jw-nextup.jw-nextup-thumbnail-visible .jw-nextup-thumbnail{display:block}.jw-nextup-container-visible{visibility:visible}.jw-nextup-container-visible .jw-nextup{opacity:1;-webkit-transform:translate(0, 0);transform:translate(0, 0);transition-delay:0s, 0s, 150ms}.jw-nextup-tooltip{display:flex;height:80px}.jw-nextup-thumbnail{width:120px;background-position:center;background-size:cover;flex:0 0 auto;display:none}.jw-nextup-body{flex:1 1 auto;overflow:hidden;padding:.75em .875em;display:flex;flex-flow:column wrap;justify-content:space-between}.jw-nextup-header,.jw-nextup-title{font-size:14px;line-height:1.35}.jw-nextup-header{font-weight:bold}.jw-nextup-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.jw-nextup-duration{align-self:flex-end;text-align:right;font-size:12px}.jw-nextup-close{height:24px;width:24px;border:none;color:rgba(255,255,255,0.8);cursor:pointer;margin:6px;visibility:hidden}.jw-nextup-close:hover{color:#fff}.jw-nextup-sticky .jw-nextup-close{visibility:visible}.jw-autostart-mute{position:absolute;bottom:0;right:12px;height:44px;width:44px;background-color:rgba(33,33,33,0.4);padding:5px 4px 5px 6px;display:none}.jwplayer.jw-flag-autostart:not(.jw-flag-media-audio) .jw-nextup{display:none}.jw-settings-menu{position:absolute;bottom:57px;right:12px;align-items:flex-start;background-color:#333;display:none;flex-flow:column nowrap;max-width:284px;pointer-events:auto}.jw-settings-open .jw-settings-menu{display:flex}.jw-breakpoint-7 .jw-settings-menu{bottom:130px;right:60px;max-height:none;max-width:none;height:35%;width:25%}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline{height:60px;width:60px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-tooltip .jw-text{font-size:1em}.jw-breakpoint-7 .jw-settings-menu .jw-settings-back{min-width:60px}.jw-breakpoint-6 .jw-settings-menu,.jw-breakpoint-5 .jw-settings-menu{height:232px;width:284px;max-height:232px}.jw-breakpoint-4 .jw-settings-menu,.jw-breakpoint-3 .jw-settings-menu{height:192px;width:284px;max-height:192px}.jw-breakpoint-2 .jw-settings-menu{height:179px;width:284px;max-height:179px}.jw-flag-small-player .jw-settings-menu{max-width:none}.jw-settings-menu .jw-icon.jw-button-color::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon.jw-button-color[aria-checked="true"]::after{opacity:1}.jw-settings-menu .jw-settings-reset{text-decoration:underline}.jw-settings-topbar{align-items:center;background-color:rgba(0,0,0,0.4);display:flex;flex:0 0 auto;padding:3px 5px 0;width:100%}.jw-settings-topbar.jw-nested-menu-open{padding:0}.jw-settings-topbar.jw-nested-menu-open .jw-icon:not(.jw-settings-close):not(.jw-settings-back){display:none}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-close{width:20px}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-arrow-left{height:12px}.jw-settings-topbar.jw-nested-menu-open .jw-settings-topbar-text{display:block;outline:none}.jw-settings-topbar .jw-settings-back{min-width:44px}.jw-settings-topbar .jw-settings-topbar-buttons{display:inherit;width:100%;height:100%}.jw-settings-topbar .jw-settings-topbar-text{display:none;color:#fff;font-size:13px;width:100%}.jw-settings-topbar .jw-settings-close{margin-left:auto}.jw-settings-submenu{display:none;flex:1 1 auto;overflow-y:auto;padding:8px 20px 0 5px}.jw-settings-submenu::-webkit-scrollbar{background-color:transparent;width:6px}.jw-settings-submenu::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-settings-submenu.jw-settings-submenu-active{display:block}.jw-settings-submenu .jw-submenu-topbar{box-shadow:0 2px 9px 0 #1d1d1d;background-color:#2f2d2d;margin:-8px -20px 0 -5px}.jw-settings-submenu .jw-submenu-topbar .jw-settings-content-item{cursor:pointer;text-align:right;padding-right:15px;text-decoration:underline}.jw-settings-submenu .jw-settings-value-wrapper{float:right;display:flex;align-items:center}.jw-settings-submenu .jw-settings-value-wrapper .jw-settings-content-item-arrow{display:flex}.jw-settings-submenu .jw-settings-value-wrapper .jw-svg-icon-arrow-right{width:8px;margin-left:5px;height:12px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item{font-size:1em;padding:11px 15px 11px 30px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-settings-item-active::before{justify-content:flex-end}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-auto-label{font-size:.85em;padding-left:10px}.jw-flag-touch .jw-settings-submenu{overflow-y:scroll;-webkit-overflow-scrolling:touch}.jw-auto-label{font-size:10px;font-weight:initial;opacity:.75;padding-left:5px}.jw-settings-content-item{position:relative;color:rgba(255,255,255,0.8);cursor:pointer;font-size:12px;line-height:1;padding:7px 0 7px 15px;width:100%;text-align:left;outline:none}.jw-settings-content-item:hover{color:#fff}.jw-settings-content-item:focus{font-weight:bold}.jw-flag-small-player .jw-settings-content-item{line-height:1.75}.jw-settings-content-item.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-settings-item-active{font-weight:bold;position:relative}.jw-settings-item-active::before{height:100%;width:1em;align-items:center;content:"\\2022";display:inline-flex;justify-content:center}.jw-breakpoint-2 .jw-settings-open .jw-display-container,.jw-flag-small-player .jw-settings-open .jw-display-container,.jw-flag-touch .jw-settings-open .jw-display-container{display:none}.jw-breakpoint-2 .jw-settings-open.jw-controls,.jw-flag-small-player .jw-settings-open.jw-controls,.jw-flag-touch .jw-settings-open.jw-controls{z-index:1}.jw-flag-small-player .jw-settings-open .jw-controlbar{display:none}.jw-settings-open .jw-icon-settings::after{opacity:1}.jw-settings-open .jw-tooltip-settings{display:none}.jw-sharing-link{cursor:pointer}.jw-shortcuts-container .jw-switch{position:relative;display:inline-block;transition:ease-out .15s;transition-property:opacity, background;border-radius:18px;width:80px;height:20px;padding:10px;background:rgba(80,80,80,0.8);cursor:pointer;font-size:inherit;vertical-align:middle}.jw-shortcuts-container .jw-switch.jw-tab-focus{outline:solid 2px #4d90fe}.jw-shortcuts-container .jw-switch .jw-switch-knob{position:absolute;top:2px;left:1px;transition:ease-out .15s;box-shadow:0 0 10px rgba(0,0,0,0.4);border-radius:13px;width:15px;height:15px;background:#fefefe}.jw-shortcuts-container .jw-switch:before,.jw-shortcuts-container .jw-switch:after{position:absolute;top:3px;transition:inherit;color:#fefefe}.jw-shortcuts-container .jw-switch:before{content:attr(data-jw-switch-disabled);right:8px}.jw-shortcuts-container .jw-switch:after{content:attr(data-jw-switch-enabled);left:8px;opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]{background:#475470}.jw-shortcuts-container .jw-switch[aria-checked="true"]:before{opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]:after{opacity:1}.jw-shortcuts-container .jw-switch[aria-checked="true"] .jw-switch-knob{left:60px}.jw-idle-icon-text{display:none;line-height:1;position:absolute;text-align:center;text-indent:.35em;top:100%;white-space:nowrap;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.jw-idle-label{border-radius:50%;color:#fff;-webkit-filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));font:normal 16px/1 Arial,Helvetica,sans-serif;position:relative;transition:background-color 150ms cubic-bezier(0, .25, .25, 1);transition-property:background-color,-webkit-filter;transition-property:background-color,filter;transition-property:background-color,filter,-webkit-filter;-webkit-font-smoothing:antialiased}.jw-state-idle .jw-icon-display.jw-idle-label .jw-idle-icon-text{display:block}.jw-state-idle .jw-icon-display.jw-idle-label .jw-svg-icon-play{-webkit-transform:scale(.7, .7);transform:scale(.7, .7)}.jw-breakpoint-0.jw-state-idle .jw-icon-display.jw-idle-label,.jw-breakpoint--1.jw-state-idle .jw-icon-display.jw-idle-label{font-size:12px}.jw-info-overlay{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column}.jw-info-overlay .jw-info-close{flex:0 0 auto;margin:5px 5px 5px auto}.jw-info-open .jw-info-overlay{display:flex}.jw-info-container{display:flex;flex:1 1 auto;flex-flow:column;margin:0 20px 20px;overflow-y:auto;padding:5px}.jw-info-container [class*="jw-info"]:not(:first-of-type){color:rgba(255,255,255,0.8);padding-top:10px;font-size:12px}.jw-info-container .jw-info-description{margin-bottom:30px;text-align:start}.jw-info-container .jw-info-description:empty{display:none}.jw-info-container .jw-info-duration{text-align:start}.jw-info-container .jw-info-title{text-align:start;font-size:12px;font-weight:bold}.jw-info-container::-webkit-scrollbar{background-color:transparent;width:6px}.jw-info-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-info-clientid{align-self:flex-end;font-size:12px;color:rgba(255,255,255,0.8);margin:0 20px 20px 44px;text-align:right}.jw-flag-touch .jw-info-open .jw-display-container{display:none}@supports ((-webkit-filter: drop-shadow(0 0 3px #000)) or (filter: drop-shadow(0 0 3px #000))){.jwplayer.jw-ab-drop-shadow .jw-controls .jw-svg-icon,.jwplayer.jw-ab-drop-shadow .jw-controls .jw-icon.jw-text,.jwplayer.jw-ab-drop-shadow .jw-slider-container .jw-rail,.jwplayer.jw-ab-drop-shadow .jw-title{text-shadow:none;box-shadow:none;-webkit-filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3));filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3))}.jwplayer.jw-ab-drop-shadow .jw-button-color{opacity:.8;transition-property:color, opacity}.jwplayer.jw-ab-drop-shadow .jw-button-color:not(:hover){color:#fff;opacity:.8}.jwplayer.jw-ab-drop-shadow .jw-button-color:hover{opacity:1}.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 0.00787) 10.79%, hsla(0, 0%, 0%, 0.02963) 21.99%, hsla(0, 0%, 0%, 0.0625) 33.34%, hsla(0, 0%, 0%, 0.1037) 44.59%, hsla(0, 0%, 0%, 0.15046) 55.48%, hsla(0, 0%, 0%, 0.2) 65.75%, hsla(0, 0%, 0%, 0.24954) 75.14%, hsla(0, 0%, 0%, 0.2963) 83.41%, hsla(0, 0%, 0%, 0.3375) 90.28%, hsla(0, 0%, 0%, 0.37037) 95.51%, hsla(0, 0%, 0%, 0.39213) 98.83%, hsla(0, 0%, 0%, 0.4));mix-blend-mode:multiply;transition-property:opacity}.jw-state-idle.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0.2), hsla(0, 0%, 0%, 0.19606) 1.17%, hsla(0, 0%, 0%, 0.18519) 4.49%, hsla(0, 0%, 0%, 0.16875) 9.72%, hsla(0, 0%, 0%, 0.14815) 16.59%, hsla(0, 0%, 0%, 0.12477) 24.86%, hsla(0, 0%, 0%, 0.1) 34.25%, hsla(0, 0%, 0%, 0.07523) 44.52%, hsla(0, 0%, 0%, 0.05185) 55.41%, hsla(0, 0%, 0%, 0.03125) 66.66%, hsla(0, 0%, 0%, 0.01481) 78.01%, hsla(0, 0%, 0%, 0.00394) 89.21%, hsla(0, 0%, 0%, 0));background-size:100% 7rem;background-position:50% 0}.jwplayer.jw-ab-drop-shadow.jw-state-idle .jw-controls{background-color:transparent}}.jw-video-thumbnail-container{position:relative;overflow:hidden}.jw-video-thumbnail-container:not(.jw-related-shelf-item-image){height:100%;width:100%}.jw-video-thumbnail-container.jw-video-thumbnail-generated{position:absolute;top:0;left:0}.jw-video-thumbnail-container:hover,.jw-related-item-content:hover .jw-video-thumbnail-container,.jw-related-shelf-item:hover .jw-video-thumbnail-container{cursor:pointer}.jw-video-thumbnail-container:hover .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-item-content:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-shelf-item:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail{position:absolute;top:50%;left:50%;bottom:unset;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:100%;height:auto;min-width:100%;min-height:100%;opacity:0;transition:opacity .3s ease;object-fit:cover;background:#000}.jw-related-item-next-up .jw-video-thumbnail-container .jw-video-thumbnail{height:100%;width:auto}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-visible:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-completed{opacity:0}.jw-video-thumbnail-container .jw-video-thumbnail~.jw-svg-icon-play{display:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-shelf-item-aspect{pointer-events:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-item-poster-content{pointer-events:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-state-idle .jw-controls{background:rgba(0,0,0,0.4)}.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay),.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay){display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon:focus{border:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon .jw-svg-icon-buffer{-webkit-animation:jw-spin 2s linear infinite;animation:jw-spin 2s linear infinite;display:block}@-webkit-keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jwplayer.jw-state-buffering .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-pause{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-pause{display:block}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-controls-backdrop{opacity:0}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-logo-bottom-left,.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio):not(.jw-flag-autostart) .jw-logo-bottom-right{bottom:0}.jwplayer .jw-icon-playback .jw-svg-icon-stop{display:none}.jwplayer.jw-state-paused .jw-svg-icon-pause,.jwplayer.jw-state-idle .jw-svg-icon-pause,.jwplayer.jw-state-error .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-svg-icon-pause{display:none}.jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-complete .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-play{display:none}.jwplayer:not(.jw-state-buffering) .jw-svg-icon-buffer{display:none}.jwplayer:not(.jw-state-complete) .jw-svg-icon-replay{display:none}.jwplayer:not(.jw-state-error) .jw-svg-icon-error{display:none}.jwplayer.jw-state-complete .jw-display .jw-icon-display .jw-svg-icon-replay{display:block}.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-state-complete .jw-controls{background:rgba(0,0,0,0.4);height:100%}.jw-state-idle .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-state-idle .jw-display-icon-rewind,.jwplayer.jw-state-buffering .jw-display-icon-rewind,.jwplayer.jw-state-complete .jw-display-icon-rewind,body .jw-error .jw-display-icon-rewind,body .jwplayer.jw-state-error .jw-display-icon-rewind,.jw-state-idle .jw-display-icon-next,.jwplayer.jw-state-buffering .jw-display-icon-next,.jwplayer.jw-state-complete .jw-display-icon-next,body .jw-error .jw-display-icon-next,body .jwplayer.jw-state-error .jw-display-icon-next{display:none}body .jw-error .jw-icon-display,body .jwplayer.jw-state-error .jw-icon-display{cursor:default}body .jw-error .jw-icon-display .jw-svg-icon-error,body .jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-error{display:block}body .jw-error .jw-icon-container{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-preview{display:none}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title{padding-top:4px}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-primary{width:auto;display:inline-block;padding-right:.5ch}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-secondary{width:auto;display:inline-block;padding-left:0}body .jwplayer.jw-state-error .jw-controlbar,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-controlbar{display:none}body .jwplayer.jw-state-error .jw-settings-menu,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-settings-menu{height:100%;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}body .jwplayer.jw-state-error .jw-display,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-display{padding:0}body .jwplayer.jw-state-error .jw-logo-bottom-left,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-left,body .jwplayer.jw-state-error .jw-logo-bottom-right,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-right{bottom:0}.jwplayer.jw-state-playing.jw-flag-user-inactive .jw-display{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-state-playing:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display,.jwplayer.jw-state-paused:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting):not(.jw-flag-play-rejected) .jw-display{display:none}.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-rewind,.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-next{display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-text,.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-flag-casting:not(.jw-flag-audio-player) .jw-cast{display:block}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-display-icon-container{display:none}.jwplayer.jw-flag-casting .jw-icon-hd,.jwplayer.jw-flag-casting .jw-captions,.jwplayer.jw-flag-casting .jw-icon-fullscreen,.jwplayer.jw-flag-casting .jw-icon-audio-tracks{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-volume{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-airplay{color:#fff}.jw-state-playing.jw-flag-casting:not(.jw-flag-audio-player) .jw-display,.jw-state-paused.jw-flag-casting:not(.jw-flag-audio-player) .jw-display{display:table}.jwplayer.jw-flag-cast-available .jw-icon-cast,.jwplayer.jw-flag-cast-available .jw-icon-airplay{display:flex}.jwplayer.jw-flag-cardboard-available .jw-icon-cardboard{display:flex}.jwplayer.jw-flag-live .jw-display-icon-rewind{visibility:hidden}.jwplayer.jw-flag-live .jw-controlbar .jw-text-elapsed,.jwplayer.jw-flag-live .jw-controlbar .jw-text-duration,.jwplayer.jw-flag-live .jw-controlbar .jw-text-countdown,.jwplayer.jw-flag-live .jw-controlbar .jw-slider-time{display:none}.jwplayer.jw-flag-live .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-live .jw-controlbar .jw-overlay:after{display:none}.jwplayer.jw-flag-live .jw-nextup-container{bottom:44px}.jwplayer.jw-flag-live .jw-text-elapsed,.jwplayer.jw-flag-live .jw-text-duration{display:none}.jwplayer.jw-flag-live .jw-text-live{cursor:default}.jwplayer.jw-flag-live .jw-text-live:hover{color:rgba(255,255,255,0.8)}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-stop,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-stop{display:block}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-text-live{height:24px;width:auto;align-items:center;border-radius:1px;color:rgba(255,255,255,0.8);display:flex;font-size:12px;font-weight:bold;margin-right:10px;padding:0 1ch;text-rendering:geometricPrecision;text-transform:uppercase;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:box-shadow,color}.jw-text-live::before{height:8px;width:8px;background-color:currentColor;border-radius:50%;margin-right:6px;opacity:1;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-text-live.jw-dvr-live{box-shadow:inset 0 0 0 2px currentColor}.jw-text-live.jw-dvr-live::before{opacity:.5}.jw-text-live.jw-dvr-live:hover{color:#fff}.jwplayer.jw-flag-controls-hidden .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-controls-hidden:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-controls-hidden .jw-plugin{bottom:.5em}.jwplayer.jw-flag-controls-hidden .jw-nextup-container{bottom:0}.jw-flag-controls-hidden .jw-controlbar,.jw-flag-controls-hidden .jw-display{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-controls-hidden .jw-controls-backdrop{opacity:0}.jw-flag-controls-hidden .jw-logo{visibility:visible}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-plugin{bottom:.5em}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-nextup-container{bottom:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-controls-hidden) .jw-media{cursor:none;-webkit-cursor-visibility:auto-hide}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing.jw-flag-casting .jw-display{display:table}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-ads) .jw-autostart-mute{display:flex}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting .jw-nextup-container{bottom:66px}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting.jw-state-idle .jw-nextup-container{display:none}.jw-flag-media-audio .jw-preview{display:block}.jwplayer.jw-flag-ads .jw-preview,.jwplayer.jw-flag-ads .jw-logo,.jwplayer.jw-flag-ads .jw-captions.jw-captions-enabled,.jwplayer.jw-flag-ads .jw-nextup-container,.jwplayer.jw-flag-ads .jw-text-duration,.jwplayer.jw-flag-ads .jw-text-elapsed{display:none}.jwplayer.jw-flag-ads video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-rewind,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-next,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-display{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player.jw-state-buffering .jw-display-icon-display{display:inline-block}.jwplayer.jw-flag-ads .jw-controlbar{flex-wrap:wrap-reverse}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time{height:auto;padding:0;pointer-events:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-slider-container{height:5px}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-rail,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-knob,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-buffer,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-cue,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-icon-settings{display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-progress{-webkit-transform:none;transform:none;top:auto}.jwplayer.jw-flag-ads .jw-controlbar .jw-tooltip,.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-tooltip:not(.jw-icon-volume),.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-inline:not(.jw-icon-playback):not(.jw-icon-fullscreen):not(.jw-icon-volume){display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-volume-tip{padding:13px 0}.jwplayer.jw-flag-ads .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid) .jw-controls .jw-controlbar,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart .jw-controls .jw-controlbar{display:flex;pointer-events:all;visibility:visible;opacity:1}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-user-inactive .jw-controls-backdrop,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart.jw-flag-user-inactive .jw-controls-backdrop{opacity:1;background-size:100% 60px}.jwplayer.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-ads-vpaid .jw-skip,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-skip{display:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls{background:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls::after{content:none}.jwplayer.jw-flag-ads-hide-controls .jw-controls-backdrop,.jwplayer.jw-flag-ads-hide-controls .jw-controls{display:none !important}.jw-flag-overlay-open-related .jw-controls,.jw-flag-overlay-open-related .jw-title,.jw-flag-overlay-open-related .jw-logo{display:none}.jwplayer.jw-flag-rightclick-open{overflow:visible}.jwplayer.jw-flag-rightclick-open .jw-rightclick{z-index:16777215}body .jwplayer.jw-flag-flash-blocked .jw-controls,body .jwplayer.jw-flag-flash-blocked .jw-overlays,body .jwplayer.jw-flag-flash-blocked .jw-controls-backdrop,body .jwplayer.jw-flag-flash-blocked .jw-preview{display:none}body .jwplayer.jw-flag-flash-blocked .jw-error-msg{top:25%}.jw-flag-touch.jw-breakpoint-7 .jw-captions,.jw-flag-touch.jw-breakpoint-6 .jw-captions,.jw-flag-touch.jw-breakpoint-5 .jw-captions,.jw-flag-touch.jw-breakpoint-4 .jw-captions,.jw-flag-touch.jw-breakpoint-7 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-6 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-5 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-4 .jw-nextup-container{bottom:4.25em}.jw-flag-touch .jw-controlbar .jw-icon-volume{display:flex}.jw-flag-touch .jw-display,.jw-flag-touch .jw-display-container,.jw-flag-touch .jw-display-controls{pointer-events:none}.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-rewind,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-rewind{display:none}.jw-flag-touch.jw-state-paused.jw-flag-dragging .jw-display{display:none}.jw-flag-audio-player{background-color:#000}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:44px}.jw-flag-audio-player:not(.jw-flag-live) .jw-spacer{display:none}.jw-flag-audio-player .jw-preview,.jw-flag-audio-player .jw-display,.jw-flag-audio-player .jw-title,.jw-flag-audio-player .jw-nextup-container{display:none}.jw-flag-audio-player .jw-controlbar{position:relative}.jw-flag-audio-player .jw-controlbar .jw-button-container{padding-right:3px;padding-left:0}.jw-flag-audio-player .jw-controlbar .jw-icon-tooltip,.jw-flag-audio-player .jw-controlbar .jw-icon-inline{display:none}.jw-flag-audio-player .jw-controlbar .jw-icon-volume,.jw-flag-audio-player .jw-controlbar .jw-icon-playback,.jw-flag-audio-player .jw-controlbar .jw-icon-next,.jw-flag-audio-player .jw-controlbar .jw-icon-rewind,.jw-flag-audio-player .jw-controlbar .jw-icon-cast,.jw-flag-audio-player .jw-controlbar .jw-text-live,.jw-flag-audio-player .jw-controlbar .jw-icon-airplay,.jw-flag-audio-player .jw-controlbar .jw-logo-button,.jw-flag-audio-player .jw-controlbar .jw-text-elapsed,.jw-flag-audio-player .jw-controlbar .jw-text-duration{display:flex;flex:0 0 auto}.jw-flag-audio-player .jw-controlbar .jw-text-duration,.jw-flag-audio-player .jw-controlbar .jw-text-countdown{padding-right:10px}.jw-flag-audio-player .jw-controlbar .jw-slider-time{flex:0 1 auto;align-items:center;display:flex;order:1}.jw-flag-audio-player .jw-controlbar .jw-icon-volume{margin-right:0;transition:margin-right 150ms cubic-bezier(0, .25, .25, 1)}.jw-flag-audio-player .jw-controlbar .jw-icon-volume .jw-overlay{display:none}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container{transition:width 300ms cubic-bezier(0, .25, .25, 1);width:0}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open{width:140px}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open .jw-slider-volume{padding-right:24px;transition:opacity 300ms;opacity:1}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open~.jw-slider-time{flex:1 1 auto;width:auto;transition:opacity 300ms, width 300ms}.jw-flag-audio-player .jw-controlbar .jw-slider-volume{opacity:0}.jw-flag-audio-player .jw-controlbar .jw-slider-volume .jw-knob{-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.jw-flag-audio-player .jw-controlbar .jw-slider-volume~.jw-icon-volume{margin-right:140px}.jw-flag-audio-player.jw-breakpoint-1 .jw-horizontal-volume-container.jw-open~.jw-slider-time,.jw-flag-audio-player.jw-breakpoint-2 .jw-horizontal-volume-container.jw-open~.jw-slider-time{opacity:0}.jw-flag-audio-player.jw-flag-small-player .jw-text-elapsed,.jw-flag-audio-player.jw-flag-small-player .jw-text-duration{display:none}.jw-flag-audio-player.jw-flag-ads .jw-slider-time{display:none}.jw-hidden{display:none}',""])}]]); \ No newline at end of file +(window.webpackJsonpjwplayer=window.webpackJsonpjwplayer||[]).push([[4,1,2,3,9],[,,,,,,,,,,,,,,,,,function(e,t,i){"use strict";i.r(t);var n,o=i(8),a=i(3),r=i(7),s=i(43),l=i(5),c=i(15),u=i(40);function d(e){return n||(n=new DOMParser),Object(l.r)(Object(l.s)(n.parseFromString(e,"image/svg+xml").documentElement))}var p=function(e,t,i,n){var o=document.createElement("div");o.className="jw-icon jw-icon-inline jw-button-color jw-reset "+e,o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i&&o.setAttribute("aria-label",i),o.style.display="none";var a=new u.a(o).on("click tap enter",t||function(){});return n&&Array.prototype.forEach.call(n,(function(e){"string"==typeof e?o.appendChild(d(e)):o.appendChild(e)})),{ui:a,element:function(){return o},toggle:function(e){e?this.show():this.hide()},show:function(){o.style.display=""},hide:function(){o.style.display="none"}}},w=i(0),h=i(71),f=i.n(h),g=i(72),j=i.n(g),b=i(73),m=i.n(b),v=i(74),y=i.n(v),k=i(75),x=i.n(k),T=i(76),O=i.n(T),C=i(77),M=i.n(C),_=i(78),S=i.n(_),E=i(79),A=i.n(E),P=i(80),z=i.n(P),L=i(81),B=i.n(L),I=i(82),R=i.n(I),V=i(83),N=i.n(V),H=i(84),F=i.n(H),D=i(85),q=i.n(D),U=i(86),W=i.n(U),Q=i(62),Y=i.n(Q),X=i(87),K=i.n(X),J=i(88),Z=i.n(J),G=i(89),$=i.n(G),ee=i(90),te=i.n(ee),ie=i(91),ne=i.n(ie),oe=i(92),ae=i.n(oe),re=i(93),se=i.n(re),le=i(94),ce=i.n(le),ue=null;function de(e){var t=fe().querySelector(we(e));if(t)return he(t);throw new Error("Icon not found "+e)}function pe(e){var t=fe().querySelectorAll(e.split(",").map(we).join(","));if(!t.length)throw new Error("Icons not found "+e);return Array.prototype.map.call(t,(function(e){return he(e)}))}function we(e){return".jw-svg-icon-".concat(e)}function he(e){return e.cloneNode(!0)}function fe(){return ue||(ue=d(""+f.a+j.a+m.a+y.a+x.a+O.a+M.a+S.a+A.a+z.a+B.a+R.a+N.a+F.a+q.a+W.a+Y.a+K.a+Z.a+$.a+te.a+ne.a+ae.a+se.a+ce.a+"")),ue}var ge=i(10);function je(e,t){for(var i=0;i10&&delete be[t[0]];var i=d(e);be[e]=i}return be[e].cloneNode(!0)}(t):((r=document.createElement("div")).className="jw-icon jw-button-image jw-button-color jw-reset",t&&Object(ge.d)(r,{backgroundImage:"url(".concat(t,")")})),s.appendChild(r),new u.a(s).on("click tap enter",n,this),s.addEventListener("mousedown",(function(e){e.preventDefault()})),this.id=o,this.buttonElement=s}var t,i,n;return t=e,(i=[{key:"element",value:function(){return this.buttonElement}},{key:"toggle",value:function(e){e?this.show():this.hide()}},{key:"show",value:function(){this.buttonElement.style.display=""}},{key:"hide",value:function(){this.buttonElement.style.display="none"}}])&&je(t.prototype,i),n&&je(t,n),e}(),ve=i(11);function ye(e,t){for(var i=0;i=0&&(t.left-=i,t.right-=i),t},xe=function(){function e(t,i){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),Object(w.g)(this,r.a),this.className=t+" jw-background-color jw-reset",this.orientation=i}var t,i,n;return t=e,(i=[{key:"setup",value:function(){this.el=Object(l.e)(function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return''}(this.className,"jw-slider-"+this.orientation)),this.elementRail=this.el.getElementsByClassName("jw-slider-container")[0],this.elementBuffer=this.el.getElementsByClassName("jw-buffer")[0],this.elementProgress=this.el.getElementsByClassName("jw-progress")[0],this.elementThumb=this.el.getElementsByClassName("jw-knob")[0],this.ui=new u.a(this.element(),{preventScrolling:!0}).on("dragStart",this.dragStart,this).on("drag",this.dragMove,this).on("dragEnd",this.dragEnd,this).on("click tap",this.tap,this)}},{key:"dragStart",value:function(){this.trigger("dragStart"),this.railBounds=ke(this.elementRail)}},{key:"dragEnd",value:function(e){this.dragMove(e),this.trigger("dragEnd")}},{key:"dragMove",value:function(e){var t,i,n=this.railBounds=this.railBounds?this.railBounds:ke(this.elementRail);return i="horizontal"===this.orientation?(t=e.pageX)n.right?100:100*Object(s.a)((t-n.left)/n.width,0,1):(t=e.pageY)>=n.bottom?0:t<=n.top?100:100*Object(s.a)((n.height-(t-n.top))/n.height,0,1),this.render(i),this.update(i),!1}},{key:"tap",value:function(e){this.railBounds=ke(this.elementRail),this.dragMove(e)}},{key:"limit",value:function(e){return e}},{key:"update",value:function(e){this.trigger("update",{percentage:e})}},{key:"render",value:function(e){e=Math.max(0,Math.min(e,100)),"horizontal"===this.orientation?(this.elementThumb.style.left=e+"%",this.elementProgress.style.width=e+"%"):(this.elementThumb.style.bottom=e+"%",this.elementProgress.style.height=e+"%")}},{key:"updateBuffer",value:function(e){this.elementBuffer.style.width=e+"%"}},{key:"element",value:function(){return this.el}}])&&ye(t.prototype,i),n&&ye(t,n),e}(),Te=function(e,t){e&&t&&(e.setAttribute("aria-label",t),e.setAttribute("role","button"),e.setAttribute("tabindex","0"))};function Oe(e,t){for(var i=0;i0&&Array.prototype.forEach.call(o,(function(e){"string"==typeof e?a.el.appendChild(d(e)):a.el.appendChild(e)}))}var t,i,n;return t=e,(i=[{key:"addContent",value:function(e){this.content&&this.removeContent(),this.content=e,this.tooltip.appendChild(e)}},{key:"removeContent",value:function(){this.content&&(this.tooltip.removeChild(this.content),this.content=null)}},{key:"hasContent",value:function(){return!!this.content}},{key:"element",value:function(){return this.el}},{key:"openTooltip",value:function(e){this.isOpen||(this.trigger("open-"+this.componentType,e,{isOpen:!0}),this.isOpen=!0,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"closeTooltip",value:function(e){this.isOpen&&(this.trigger("close-"+this.componentType,e,{isOpen:!1}),this.isOpen=!1,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"toggleOpenState",value:function(e){this.isOpen?this.closeTooltip(e):this.openTooltip(e)}}])&&Oe(t.prototype,i),n&&Oe(t,n),e}(),Me=i(22),_e=i(57);function Se(e,t){for(var i=0;i=this.thumbnails.length&&(t=this.thumbnails.length-1);var i=this.thumbnails[t].img;return i.indexOf("://")<0&&(i=this.vttPath?this.vttPath+"/"+i:i),i},loadThumbnail:function(e){var t=this.chooseThumbnail(e),i={margin:"0 auto",backgroundPosition:"0 0"};if(t.indexOf("#xywh")>0)try{var n=/(.+)#xywh=(\d+),(\d+),(\d+),(\d+)/.exec(t);t=n[1],i.backgroundPosition=-1*n[2]+"px "+-1*n[3]+"px",i.width=n[4],this.timeTip.setWidth(+i.width),i.height=n[5]}catch(e){return}else this.individualImage||(this.individualImage=new Image,this.individualImage.onload=Object(w.a)((function(){this.individualImage.onload=null,this.timeTip.image({width:this.individualImage.width,height:this.individualImage.height}),this.timeTip.setWidth(this.individualImage.width)}),this),this.individualImage.src=t);return i.backgroundImage='url("'+t+'")',i},showThumbnail:function(e){this._model.get("containerWidth")<=420||this.thumbnails.length<1||this.timeTip.image(this.loadThumbnail(e))},resetThumbnails:function(){this.timeTip.image({backgroundImage:"",width:0,height:0}),this.thumbnails=[]}};function Le(e,t,i){return(Le="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,i){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=He(e)););return e}(e,t);if(n){var o=Object.getOwnPropertyDescriptor(n,t);return o.get?o.get.call(i):o.value}})(e,t,i||e)}function Be(e){return(Be="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ie(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Re(e,t){for(var i=0;i-1&&(n="Live")}var d=this.timeTip;d.update(n),this.textLength!==n.length&&(this.textLength=n.length,d.resetWidth()),this.showThumbnail(u),Object(l.a)(d.el,"jw-open");var p=d.getWidth(),w=a.width/100,h=o-a.width,f=0;p>h&&(f=(p-h)/(200*w));var g=100*Math.min(1-f,Math.max(f,c)).toFixed(3);Object(ge.d)(d.el,{left:g+"%"})}}},{key:"hideTimeTooltip",value:function(){Object(l.o)(this.timeTip.el,"jw-open")}},{key:"updateCues",value:function(e,t){var i=this;this.resetCues(),t&&t.length&&(t.forEach((function(e){i.addCue(e)})),this.drawCues())}},{key:"updateAriaText",value:function(){var e=this._model;if(!e.get("seeking")){var t=e.get("position"),i=e.get("duration"),n=Object(ve.timeFormat)(t);"DVR"!==this.streamType&&(n+=" of ".concat(Object(ve.timeFormat)(i)));var o=this.el;document.activeElement!==o&&(this.timeUpdateKeeper.textContent=n),Object(l.t)(o,"aria-valuenow",t),Object(l.t)(o,"aria-valuetext",n)}}},{key:"reset",value:function(){this.resetThumbnails(),this.timeTip.resetWidth(),this.textLength=0}}]),t}(xe);Object(w.g)(Ue.prototype,Ae,ze);var We=Ue;function Qe(e,t){for(var i=0;i=75&&!e),Object(l.t)(r,"aria-valuenow",o),Object(l.t)(s,"aria-valuenow",o);var c="Volume ".concat(o,"%");Object(l.t)(r,"aria-valuetext",c),Object(l.t)(s,"aria-valuetext",c),document.activeElement!==r&&document.activeElement!==s&&(this._volumeAnnouncer.textContent=c)}}},{key:"onCastAvailable",value:function(e,t){this.elements.cast.toggle(t)}},{key:"onCastActive",value:function(e,t){this.elements.fullscreen.toggle(!t),this.elements.cast.button&&Object(l.v)(this.elements.cast.button,"jw-off",!t)}},{key:"onElapsed",value:function(e,t){var i,n,o=e.get("duration");if("DVR"===e.get("streamType")){var a=Math.ceil(t),r=this._model.get("dvrSeekLimit");i=n=a>=-r?"":"-"+Object(ve.timeFormat)(-(t+r)),e.set("dvrLive",a>=-r)}else i=Object(ve.timeFormat)(t),n=Object(ve.timeFormat)(o-t);this.elements.elapsed.textContent=i,this.elements.countdown.textContent=n}},{key:"onDuration",value:function(e,t){this.elements.duration.textContent=Object(ve.timeFormat)(Math.abs(t))}},{key:"onAudioMode",value:function(e,t){var i=this.elements.time.element();t?this.elements.buttonContainer.insertBefore(i,this.elements.elapsed):Object(l.m)(this.el,i)}},{key:"element",value:function(){return this.el}},{key:"setAltText",value:function(e,t){this.elements.alt.textContent=t}},{key:"closeMenus",value:function(e){this.menus.forEach((function(t){e&&e.target===t.el||t.closeTooltip(e)}))}},{key:"rewind",value:function(){var e,t=0,i=this._model.get("currentTime");i?e=i-10:(e=this._model.get("position")-10,"DVR"===this._model.get("streamType")&&(t=this._model.get("duration"))),this._api.seek(Math.max(e,t),{reason:"interaction"})}},{key:"onState",value:function(e,t){var i=e.get("localization"),n=i.play;this.setPlayText(n),t===a.pb&&("LIVE"!==e.get("streamType")?(n=i.pause,this.setPlayText(n)):(n=i.stop,this.setPlayText(n))),Object(l.t)(this.elements.play.element(),"aria-label",n)}},{key:"onStreamTypeChange",value:function(e,t){var i="LIVE"===t,n="DVR"===t;this.elements.rewind.toggle(!i),this.elements.live.toggle(i||n),Object(l.t)(this.elements.live.element(),"tabindex",i?"-1":"0"),this.elements.duration.style.display=n?"none":"",this.onDuration(e,e.get("duration")),this.onState(e,e.get("state"))}},{key:"addLogo",value:function(e){var t=this.elements.buttonContainer,i=new me(e.file,this._model.get("localization").logo,(function(){e.link&&Object(l.l)(e.link,"_blank",{rel:"noreferrer"})}),"logo","jw-logo-button");e.link||Object(l.t)(i.element(),"tabindex","-1"),t.insertBefore(i.element(),t.querySelector(".jw-spacer").nextSibling)}},{key:"goToLiveEdge",value:function(){if("DVR"===this._model.get("streamType")){var e=Math.min(this._model.get("position"),-1),t=this._model.get("dvrSeekLimit");this._api.seek(Math.max(-t,e),{reason:"interaction"}),this._api.play({reason:"interaction"})}}},{key:"updateButtons",value:function(e,t,i){if(t){var n,o,a=this.elements.buttonContainer;t!==i&&i?(n=ct(t,i),o=ct(i,t),this.removeButtons(a,o)):n=t;for(var r=n.length-1;r>=0;r--){var s=n[r],l=new me(s.img,s.tooltip,s.callback,s.id,s.btnClass);s.tooltip&&nt(l.element(),s.id,s.tooltip);var c=void 0;"related"===l.id?c=this.elements.settingsButton.element():"share"===l.id?c=a.querySelector('[button="related"]')||this.elements.settingsButton.element():(c=this.elements.spacer.nextSibling)&&"logo"===c.getAttribute("button")&&(c=c.nextSibling),a.insertBefore(l.element(),c)}}}},{key:"removeButtons",value:function(e,t){for(var i=t.length;i--;){var n=e.querySelector('[button="'.concat(t[i].id,'"]'));n&&e.removeChild(n)}}},{key:"toggleCaptionsButtonState",value:function(e){var t=this.elements.captionsButton;t&&Object(l.v)(t.element(),"jw-off",!e)}},{key:"destroy",value:function(){var e=this;this._model.off(null,null,this),Object.keys(this.elements).forEach((function(t){var i=e.elements[t];i&&"function"==typeof i.destroy&&e.elements[t].destroy()})),this.ui.forEach((function(e){e.destroy()})),this.ui=[]}}])&&at(t.prototype,i),n&&at(t,n),e}(),pt=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return'
    ')+'
    ')+"
    "},wt=function(e){return'
    '+pt("rewind",e.rewind)+pt("display",e.playback)+pt("next",e.next)+"
    "};function ht(e,t){for(var i=0;i'.concat(a.playback,"
    ")),Object(l.a)(o.icon,"jw-idle-label"),o.icon.appendChild(s))}return o}var i,n,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&vt(e,t)}(t,e),i=t,(n=[{key:"element",value:function(){return this.el}}])&&jt(i.prototype,n),o&&jt(i,o),t}(r.a);function kt(e,t){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";return'
    '+'
    '.concat(e,"
    ")+'
    '.concat(t,"
    ")+'
    '.concat(i,"
    ")+"
    "+'')+"
    "}());t.querySelector(".jw-nextup-close").appendChild(de("close")),this.addContent(t),this.closeButton=this.content.querySelector(".jw-nextup-close"),this.closeButton.setAttribute("aria-label",this.localization.close),this.tooltip=this.content.querySelector(".jw-nextup-tooltip");var i=this._model,n=i.player;this.enabled=!1,i.on("change:nextUp",this.onNextUp,this),n.change("duration",this.onDuration,this),n.change("position",this.onElapsed,this),n.change("streamType",this.onStreamType,this),n.change("state",(function(e,t){"complete"===t&&this.toggle(!1)}),this),this.closeUi=new u.a(this.closeButton,{directSelect:!0}).on("click tap enter",(function(){this.nextUpSticky=!1,this.toggle(!1)}),this),this.tooltipUi=new u.a(this.tooltip).on("click tap",this.click,this)}},{key:"loadThumbnail",value:function(e){return this.nextUpImage=new Image,this.nextUpImage.onload=function(){this.nextUpImage.onload=null}.bind(this),this.nextUpImage.src=e,{backgroundImage:'url("'+e+'")'}}},{key:"click",value:function(){var e=this.feedShownId;this.reset(),this._api.next({feedShownId:e,reason:"interaction"})}},{key:"toggle",value:function(e,t){if(this.enabled&&(Object(l.v)(this.container,"jw-nextup-sticky",!!this.nextUpSticky),this.shown!==e)){this.shown=e,Object(l.v)(this.container,"jw-nextup-container-visible",e),Object(l.v)(this._playerElement,"jw-flag-nextup",e);var i=this._model.get("nextUp");e&&i?(this.feedShownId=Object(ot.b)(ot.a),this.trigger("nextShown",{mode:i.mode,ui:"nextup",itemsShown:[i],feedData:i.feedData,reason:t,feedShownId:this.feedShownId})):this.feedShownId=""}}},{key:"setNextUpItem",value:function(e){var t=this;setTimeout((function(){if(t.thumbnail=t.content.querySelector(".jw-nextup-thumbnail"),Object(l.v)(t.content,"jw-nextup-thumbnail-visible",!!e.image),e.image){var i=t.loadThumbnail(e.image);Object(ge.d)(t.thumbnail,i)}t.header=t.content.querySelector(".jw-nextup-header"),t.header.textContent=Object(l.e)(t.localization.nextUp).textContent,t.title=t.content.querySelector(".jw-nextup-title");var n=e.title;t.title.textContent=n?Object(l.e)(n).textContent:"";var o=e.duration;o&&(t.duration=t.content.querySelector(".jw-nextup-duration"),t.duration.textContent="number"==typeof o?Object(ve.timeFormat)(o):o)}),500)}},{key:"onNextUp",value:function(e,t){this.reset(),t||(t={showNextUp:!1}),this.enabled=!(!t.title&&!t.image),this.enabled&&(t.showNextUp||(this.nextUpSticky=!1,this.toggle(!1)),this.setNextUpItem(t))}},{key:"onDuration",value:function(e,t){if(t){var i=e.get("nextupoffset"),n=-10;i&&(n=Object(Mt.d)(i,t)),n<0&&(n+=t),Object(Mt.c)(i)&&t-5=this.offset;n&&void 0===i?(this.nextUpSticky=n,this.toggle(n,"time")):!n&&i&&this.reset()}}},{key:"onStreamType",value:function(e,t){"VOD"!==t&&(this.nextUpSticky=!1,this.toggle(!1))}},{key:"element",value:function(){return this.container}},{key:"addContent",value:function(e){this.content&&this.removeContent(),this.content=e,this.container.appendChild(e)}},{key:"removeContent",value:function(){this.content&&(this.container.removeChild(this.content),this.content=null)}},{key:"reset",value:function(){this.nextUpSticky=void 0,this.toggle(!1)}},{key:"destroy",value:function(){this.off(),this._model.off(null,null,this),this.closeUi&&this.closeUi.destroy(),this.tooltipUi&&this.tooltipUi.destroy()}}])&&_t(t.prototype,i),n&&_t(t,n),e}(),Et=function(e,t){var i=e.featured,n=e.showLogo,o=e.type;return e.logo=n?'':"",'
  • ').concat(At[o](e,t),"
  • ")},At={link:function(e){var t=e.link,i=e.title,n=e.logo;return'').concat(n).concat(i||"","")},info:function(e,t){return'")},share:function(e,t){return'")},keyboardShortcuts:function(e,t){return'")}},Pt=i(23),zt=i(6),Lt=i(13);function Bt(e,t){for(var i=0;iJW Player '.concat(e,""),a={items:[{type:"info"},{title:Object(Lt.e)(n)?"".concat(o," ").concat(n):"".concat(n," ").concat(o),type:"link",featured:!0,showLogo:!0,link:"https://jwplayer.com/learn-more?e=".concat(It[i])}]},r=t.get("provider"),s=a.items;if(r&&r.name.indexOf("flash")>=0){var l="Flash Version "+Object(zt.a)();s.push({title:l,type:"link",link:"http://www.adobe.com/software/flash/about/"})}return this.shortcutsTooltip&&s.splice(s.length-1,0,{type:"keyboardShortcuts"}),a}},{key:"rightClick",value:function(e){if(this.lazySetup(),this.mouseOverContext)return!1;this.hideMenu(),this.showMenu(e),this.addHideMenuHandlers()}},{key:"getOffset",value:function(e){var t=Object(l.c)(this.wrapperElement),i=e.pageX-t.left,n=e.pageY-t.top;return this.model.get("touchMode")&&(n-=100),{x:i,y:n}}},{key:"showMenu",value:function(e){var t=this,i=this.getOffset(e);return this.el.style.left=i.x+"px",this.el.style.top=i.y+"px",this.outCount=0,Object(l.a)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.a)(this.el,"jw-open"),clearTimeout(this._menuTimeout),this._menuTimeout=setTimeout((function(){return t.hideMenu()}),3e3),!1}},{key:"hideMenu",value:function(e){e&&this.el&&this.el.contains(e.target)||(Object(l.o)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.o)(this.el,"jw-open"))}},{key:"lazySetup",value:function(){var e,t,i,n,o=this,a=(e=this.buildArray(),t=this.model.get("localization"),i=e.items,n=(void 0===i?[]:i).map((function(e){return Et(e,t)})),'
    '+'
      '.concat(n.join(""),"
    ")+"
    ");if(this.el){if(this.html!==a){this.html=a;var r=Rt(a);Object(l.h)(this.el);for(var s=r.childNodes.length;s--;)this.el.appendChild(r.firstChild)}}else this.html=a,this.el=Rt(this.html),this.wrapperElement.appendChild(this.el),this.hideMenuHandler=function(e){return o.hideMenu(e)},this.overHandler=function(){o.mouseOverContext=!0},this.outHandler=function(e){o.mouseOverContext=!1,e.relatedTarget&&!o.el.contains(e.relatedTarget)&&++o.outCount>1&&o.hideMenu()},this.infoOverlayHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.infoOverlay.open()},this.shortcutsTooltipHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.shortcutsTooltip.open()}}},{key:"setup",value:function(e,t,i){this.wrapperElement=i,this.model=e,this.mouseOverContext=!1,this.playerContainer=t,this.ui=new u.a(i).on("longPress",this.rightClick,this)}},{key:"addHideMenuHandlers",value:function(){this.removeHideMenuHandlers(),this.wrapperElement.addEventListener("touchstart",this.hideMenuHandler),document.addEventListener("touchstart",this.hideMenuHandler),o.OS.mobile||(this.wrapperElement.addEventListener("click",this.hideMenuHandler),document.addEventListener("click",this.hideMenuHandler),this.el.addEventListener("mouseover",this.overHandler),this.el.addEventListener("mouseout",this.outHandler)),this.el.querySelector(".jw-info-overlay-item").addEventListener("click",this.infoOverlayHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").addEventListener("click",this.shortcutsTooltipHandler)}},{key:"removeHideMenuHandlers",value:function(){this.wrapperElement&&(this.wrapperElement.removeEventListener("click",this.hideMenuHandler),this.wrapperElement.removeEventListener("touchstart",this.hideMenuHandler)),this.el&&(this.el.querySelector(".jw-info-overlay-item").removeEventListener("click",this.infoOverlayHandler),this.el.removeEventListener("mouseover",this.overHandler),this.el.removeEventListener("mouseout",this.outHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").removeEventListener("click",this.shortcutsTooltipHandler)),document.removeEventListener("click",this.hideMenuHandler),document.removeEventListener("touchstart",this.hideMenuHandler)}},{key:"destroy",value:function(){clearTimeout(this._menuTimeout),this.removeHideMenuHandlers(),this.el&&(this.hideMenu(),this.hideMenuHandler=null,this.el=null),this.wrapperElement&&(this.wrapperElement.oncontextmenu=null,this.wrapperElement=null),this.model&&(this.model=null),this.ui&&(this.ui.destroy(),this.ui=null)}}])&&Bt(t.prototype,i),n&&Bt(t,n),e}(),Nt=function(e){return'")},Ht=function(e){return'"},Ft=function(e){return'"};function Dt(e){return(Dt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function qt(e,t){return!t||"object"!==Dt(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function Ut(e){return(Ut=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Wt(e,t){return(Wt=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Qt(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Yt(e,t){for(var i=0;i2&&void 0!==arguments[2]?arguments[2]:Nt;Qt(this,e),this.el=Object(l.e)(n(t)),this.ui=new u.a(this.el).on("click tap enter",i,this)}return Xt(e,[{key:"destroy",value:function(){this.ui.destroy()}}]),e}(),Zt=function(e){function t(e,i){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Ft;return Qt(this,t),qt(this,Ut(t).call(this,e,i,n))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Wt(e,t)}(t,e),Xt(t,[{key:"activate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!0),this.el.setAttribute("aria-checked","true"),this.active=!0}},{key:"deactivate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!1),this.el.setAttribute("aria-checked","false"),this.active=!1}}]),t}(Jt),Gt=function(e,t){return e?'':''},$t=function(e,t){var i=e.name,n={captions:"cc-off",audioTracks:"audio-tracks",quality:"quality-100",playbackRates:"playback-rate"}[i];if(n||e.icon){var o=p("jw-settings-".concat(i," jw-submenu-").concat(i),(function(t){e.open(t)}),i,[e.icon&&Object(l.e)(e.icon)||de(n)]),a=o.element();return a.setAttribute("role","menuitemradio"),a.setAttribute("aria-checked","false"),a.setAttribute("aria-label",t),"ontouchstart"in window||(o.tooltip=nt(a,i,t)),o}};function ei(e){return(ei="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ti(e,t){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:Gt;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),a=this,(o=!(r=ii(t).call(this))||"object"!==ei(r)&&"function"!=typeof r?oi(a):r).open=o.open.bind(oi(oi(o))),o.close=o.close.bind(oi(oi(o))),o.toggle=o.toggle.bind(oi(oi(o))),o.onDocumentClick=o.onDocumentClick.bind(oi(oi(o))),o.name=e,o.isSubmenu=!!i,o.el=Object(l.e)(s(o.isSubmenu,e)),o.topbar=o.el.querySelector(".jw-".concat(o.name,"-topbar")),o.buttonContainer=o.el.querySelector(".jw-".concat(o.name,"-topbar-buttons")),o.children={},o.openMenus=[],o.items=[],o.visible=!1,o.parentMenu=i,o.mainMenu=o.parentMenu?o.parentMenu.mainMenu:oi(oi(o)),o.categoryButton=null,o.closeButton=o.parentMenu&&o.parentMenu.closeButton||o.createCloseButton(n),o.isSubmenu?(o.categoryButton=o.parentMenu.categoryButton||o.createCategoryButton(n),o.parentMenu.parentMenu&&!o.mainMenu.backButton&&(o.mainMenu.backButton=o.createBackButton(n)),o.itemsContainer=o.createItemsContainer(),o.parentMenu.appendMenu(oi(oi(o)))):o.ui=ri(oi(oi(o))),o}var i,n,o;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&ni(e,t)}(t,e),i=t,(n=[{key:"createItemsContainer",value:function(){var e,t,i=this,n=this.el.querySelector(".jw-settings-submenu-items"),o=new u.a(n),a=this.categoryButton&&this.categoryButton.element()||this.parentMenu.categoryButton&&this.parentMenu.categoryButton.element()||this.mainMenu.buttonContainer.firstChild;return this.parentMenu.isSubmenu&&(e=this.mainMenu.closeButton.element(),t=this.mainMenu.backButton.element()),o.on("keydown",(function(o){if(o.target.parentNode===n){var r=function(e,t){e?e.focus():void 0!==t&&n.childNodes[t].focus()},s=o.sourceEvent,c=s.target,u=n.firstChild===c,d=n.lastChild===c,p=i.topbar,w=e||Object(l.k)(a),h=t||Object(l.n)(a),f=Object(l.k)(s.target),g=Object(l.n)(s.target),j=s.key.replace(/(Arrow|ape)/,"");switch(j){case"Tab":r(s.shiftKey?h:w);break;case"Left":r(h||Object(l.n)(document.getElementsByClassName("jw-icon-settings")[0]));break;case"Up":p&&u?r(p.firstChild):r(g,n.childNodes.length-1);break;case"Right":r(w);break;case"Down":p&&d?r(p.firstChild):r(f,0)}s.preventDefault(),"Esc"!==j&&s.stopPropagation()}})),o}},{key:"createCloseButton",value:function(e){var t=p("jw-settings-close",this.close,e.close,[de("close")]);return this.topbar.appendChild(t.element()),t.show(),t.ui.on("keydown",(function(e){var t=e.sourceEvent,i=t.key.replace(/(Arrow|ape)/,"");("Enter"===i||"Right"===i||"Tab"===i&&!t.shiftKey)&&this.close(e)}),this),this.buttonContainer.appendChild(t.element()),t}},{key:"createCategoryButton",value:function(e){var t=e[{captions:"cc",audioTracks:"audioTracks",quality:"hd",playbackRates:"playbackRates"}[this.name]];"sharing"===this.name&&(t=e.sharing.heading);var i=$t(this,t);return i.element().setAttribute("name",this.name),i}},{key:"createBackButton",value:function(e){var t=p("jw-settings-back",(function(e){Kt&&Kt.open(e)}),e.close,[de("arrow-left")]);return Object(l.m)(this.mainMenu.topbar,t.element()),t}},{key:"createTopbar",value:function(){var e=Object(l.e)('
    ');return Object(l.m)(this.el,e),e}},{key:"createItems",value:function(e,t){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Zt,a=this.name,r=e.map((function(e,r){var s,l;switch(a){case"quality":s="Auto"===e.label&&0===r?"".concat(n.defaultText,' '):e.label;break;case"captions":s="Off"!==e.label&&"off"!==e.id||0!==r?e.label:n.defaultText;break;case"playbackRates":l=e,s=Object(Lt.e)(n.tooltipText)?"x"+e:e+"x";break;case"audioTracks":s=e.name}s||(s=e,"object"===ei(e)&&(s.options=n));var c=new o(s,function(e){c.active||(t(l||r),c.deactivate&&(i.items.filter((function(e){return!0===e.active})).forEach((function(e){e.deactivate()})),Kt?Kt.open(e):i.mainMenu.close(e)),c.activate&&c.activate())}.bind(i));return c}));return r}},{key:"setMenuItems",value:function(e,t){var i=this;e?(this.items=[],Object(l.h)(this.itemsContainer.el),e.forEach((function(e){i.items.push(e),i.itemsContainer.el.appendChild(e.el)})),t>-1&&e[t].activate(),this.categoryButton.show()):this.removeMenu()}},{key:"appendMenu",value:function(e){if(e){var t=e.el,i=e.name,n=e.categoryButton;if(this.children[i]=e,n){var o=this.mainMenu.buttonContainer,a=o.querySelector(".jw-settings-sharing"),r="quality"===i?o.firstChild:a||this.closeButton.element();o.insertBefore(n.element(),r)}this.mainMenu.el.appendChild(t)}}},{key:"removeMenu",value:function(e){if(!e)return this.parentMenu.removeMenu(this.name);var t=this.children[e];t&&(delete this.children[e],t.destroy())}},{key:"open",value:function(e){if(!this.visible||this.openMenus){var t;if(Kt=null,this.isSubmenu){var i=this.mainMenu,n=this.parentMenu,o=this.categoryButton;if(n.openMenus.length&&n.closeChildren(),o&&o.element().setAttribute("aria-checked","true"),n.isSubmenu){n.el.classList.remove("jw-settings-submenu-active"),i.topbar.classList.add("jw-nested-menu-open");var a=i.topbar.querySelector(".jw-settings-topbar-text");a.setAttribute("name",this.name),a.innerText=this.title||this.name,i.backButton.show(),Kt=this.parentMenu,t=this.topbar?this.topbar.firstChild:e&&"enter"===e.type?this.items[0].el:a}else i.topbar.classList.remove("jw-nested-menu-open"),i.backButton&&i.backButton.hide();this.el.classList.add("jw-settings-submenu-active"),n.openMenus.push(this.name),i.visible||(i.open(e),this.items&&e&&"enter"===e.type?t=this.topbar?this.topbar.firstChild.focus():this.items[0].el:o.tooltip&&(o.tooltip.suppress=!0,t=o.element())),this.openMenus.length&&this.closeChildren(),t&&t.focus(),this.el.scrollTop=0}else this.el.parentNode.classList.add("jw-settings-open"),this.trigger("menuVisibility",{visible:!0,evt:e}),document.addEventListener("click",this.onDocumentClick);this.visible=!0,this.el.setAttribute("aria-expanded","true")}}},{key:"close",value:function(e){var t=this;this.visible&&(this.visible=!1,this.el.setAttribute("aria-expanded","false"),this.isSubmenu?(this.el.classList.remove("jw-settings-submenu-active"),this.categoryButton.element().setAttribute("aria-checked","false"),this.parentMenu.openMenus=this.parentMenu.openMenus.filter((function(e){return e!==t.name})),!this.mainMenu.openMenus.length&&this.mainMenu.visible&&this.mainMenu.close(e)):(this.el.parentNode.classList.remove("jw-settings-open"),this.trigger("menuVisibility",{visible:!1,evt:e}),document.removeEventListener("click",this.onDocumentClick)),this.openMenus.length&&this.closeChildren())}},{key:"closeChildren",value:function(){var e=this;this.openMenus.forEach((function(t){var i=e.children[t];i&&i.close()}))}},{key:"toggle",value:function(e){this.visible?this.close(e):this.open(e)}},{key:"onDocumentClick",value:function(e){/jw-(settings|video|nextup-close|sharing-link|share-item)/.test(e.target.className)||this.close()}},{key:"destroy",value:function(){var e=this;if(document.removeEventListener("click",this.onDocumentClick),Object.keys(this.children).map((function(t){e.children[t].destroy()})),this.isSubmenu){this.parentMenu.name===this.mainMenu.name&&this.categoryButton&&(this.parentMenu.buttonContainer.removeChild(this.categoryButton.element()),this.categoryButton.ui.destroy()),this.itemsContainer&&this.itemsContainer.destroy();var t=this.parentMenu.openMenus,i=t.indexOf(this.name);t.length&&i>-1&&this.openMenus.splice(i,1),delete this.parentMenu}else this.ui.destroy();this.visible=!1,this.el.parentNode&&this.el.parentNode.removeChild(this.el)}},{key:"defaultChild",get:function(){var e=this.children,t=e.quality,i=e.captions,n=e.audioTracks,o=e.sharing,a=e.playbackRates;return t||i||n||o||a}}])&&ti(i.prototype,n),o&&ti(i,o),t}(r.a),ri=function(e){var t=e.closeButton,i=e.el;return new u.a(i).on("keydown",(function(i){var n=i.sourceEvent,o=i.target,a=Object(l.k)(o),r=Object(l.n)(o),s=n.key.replace(/(Arrow|ape)/,""),c=function(t){r?t||r.focus():e.close(i)};switch(s){case"Esc":e.close(i);break;case"Left":c();break;case"Right":a&&t.element()&&o!==t.element()&&a.focus();break;case"Tab":n.shiftKey&&c(!0);break;case"Up":case"Down":!function(){var t=e.children[o.getAttribute("name")];if(!t&&Kt&&(t=Kt.children[Kt.openMenus]),t)return t.open(i),void(t.topbar?t.topbar.firstChild.focus():t.items&&t.items.length&&t.items[0].el.focus());if(i.target.parentNode.classList.contains("jw-submenu-topbar")){var n=i.target.parentNode.parentNode.querySelector(".jw-settings-submenu-items");("Down"===s?n.childNodes[0]:n.childNodes[n.childNodes.length-1]).focus()}}()}if(n.stopPropagation(),/13|32|37|38|39|40/.test(n.keyCode))return n.preventDefault(),!1}))},si=i(59),li=function(e){return wi[e]},ci=function(e){for(var t,i=Object.keys(wi),n=0;n1;i.elements.settingsButton.toggle(c)};t.change("levels",(function(e,t){r(t)}),o);var s=function(e,i,n){var o=t.get("levels");if(o&&"Auto"===o[0].label&&i&&i.items.length){var a=i.items[0].el.querySelector(".jw-auto-label"),r=o[e.index]||{label:""};a.textContent=n?"":r.label}};t.on("change:visualQuality",(function(e,i){var n=o.children.quality;i&&n&&s(i.level,n,t.get("currentLevel"))})),t.on("change:currentLevel",(function(e,i){var n=o.children.quality,a=t.get("visualQuality");a&&n&&s(a.level,n,i)}),o),t.change("captionsList",(function(i,r){var s={defaultText:n.off},l=t.get("captionsIndex");a("captions",r,(function(t){return e.setCurrentCaptions(t)}),l,s);var c=o.children.captions;if(c&&!c.children.captionsSettings){c.topbar=c.topbar||c.createTopbar();var u=new ai("captionsSettings",c,n);u.title="Subtitle Settings";var d=new Jt("Settings",u.open);c.topbar.appendChild(d.el);var p=new Zt("Reset",(function(){t.set("captions",si.a),f()}));p.el.classList.add("jw-settings-reset");var h=t.get("captions"),f=function(){var e=[];pi.forEach((function(i){h&&h[i.propertyName]&&(i.defaultVal=i.getOption(h[i.propertyName]));var o=new ai(i.name,u,n),a=new Jt({label:i.name,value:i.defaultVal},o.open,Ht),r=o.createItems(i.options,(function(e){var n=a.el.querySelector(".jw-settings-content-item-value");!function(e,i){var n=t.get("captions"),o=e.propertyName,a=e.options&&e.options[i],r=e.getTypedValue(a),s=Object(w.g)({},n);s[o]=r,t.set("captions",s)}(i,e),n.innerText=i.options[e]}),null);o.setMenuItems(r,i.options.indexOf(i.defaultVal)||0),e.push(a)})),e.push(p),u.setMenuItems(e)};f()}}));var l=function(e,t){e&&t>-1&&e.items[t].activate()};t.change("captionsIndex",(function(e,t){var n=o.children.captions;n&&l(n,t),i.toggleCaptionsButtonState(!!t)}),o);var c=function(i){if(t.get("supportsPlaybackRate")&&"LIVE"!==t.get("streamType")&&t.get("playbackRateControls")){var r=i.indexOf(t.get("playbackRate")),s={tooltipText:n.playbackRates};a("playbackRates",i,(function(t){return e.setPlaybackRate(t)}),r,s)}else o.children.playbackRates&&o.removeMenu("playbackRates")};t.on("change:playbackRates",(function(e,t){c(t)}),o);var u=function(i){a("audioTracks",i,(function(t){return e.setCurrentAudioTrack(t)}),t.get("currentAudioTrack"))};return t.on("change:audioTracks",(function(e,t){u(t)}),o),t.on("change:playbackRate",(function(e,i){var n=t.get("playbackRates"),a=-1;n&&(a=n.indexOf(i)),l(o.children.playbackRates,a)}),o),t.on("change:currentAudioTrack",(function(e,t){o.children.audioTracks.items[t].activate()}),o),t.on("change:playlistItem",(function(){o.removeMenu("captions"),i.elements.captionsButton.hide(),o.visible&&o.close()}),o),t.on("change:playbackRateControls",(function(){c(t.get("playbackRates"))})),t.on("change:castActive",(function(e,i,n){i!==n&&(i?(o.removeMenu("audioTracks"),o.removeMenu("quality"),o.removeMenu("playbackRates")):(u(t.get("audioTracks")),r(t.get("levels")),c(t.get("playbackRates"))))}),o),t.on("change:streamType",(function(){c(t.get("playbackRates"))}),o),o},fi=i(58),gi=i(35),ji=i(12),bi=function(e,t,i,n){var o=Object(l.e)('
    '),r=!1,s=null,c=!1,u=function(e){/jw-info/.test(e.target.className)||w.close()},d=function(){var n,a,s,c,u,d=p("jw-info-close",(function(){w.close()}),t.get("localization").close,[de("close")]);d.show(),Object(l.m)(o,d.element()),a=o.querySelector(".jw-info-title"),s=o.querySelector(".jw-info-duration"),c=o.querySelector(".jw-info-description"),u=o.querySelector(".jw-info-clientid"),t.change("playlistItem",(function(e,t){var i=t.description,n=t.title;Object(l.q)(c,i||""),Object(l.q)(a,n||"Unknown Title")})),t.change("duration",(function(e,i){var n="";switch(t.get("streamType")){case"LIVE":n="Live";break;case"DVR":n="DVR";break;default:i&&(n=Object(ve.timeFormat)(i))}s.textContent=n}),w),u.textContent=(n=i.getPlugin("jwpsrv"))&&"function"==typeof n.doNotTrackUser&&n.doNotTrackUser()?"":"Client ID: ".concat(function(){try{return window.localStorage.jwplayerLocalId}catch(e){return"none"}}()),e.appendChild(o),r=!0};var w={open:function(){r||d(),document.addEventListener("click",u),c=!0;var e=t.get("state");e===a.pb&&i.pause("infoOverlayInteraction"),s=e,n(!0)},close:function(){document.removeEventListener("click",u),c=!1,t.get("state")===a.ob&&s===a.pb&&i.play("infoOverlayInteraction"),s=null,n(!1)},destroy:function(){this.close(),t.off(null,null,this)}};return Object.defineProperties(w,{visible:{enumerable:!0,get:function(){return c}}}),w};var mi=function(e,t,i){var n,o=!1,r=null,s=i.get("localization").shortcuts,c=Object(l.e)(function(e,t){var i=e.map((function(e){return'
    '+''.concat(e.description,"")+''.concat(e.key,"")+"
    "})).join("");return'
    ')+'Press shift question mark to access a list of keyboard shortcuts
    '+''.concat(t,"")+'
    '+"".concat(i)+"
    "}(function(e){var t=e.playPause,i=e.volumeToggle,n=e.fullscreenToggle,o=e.seekPercent,a=e.increaseVolume,r=e.decreaseVolume,s=e.seekForward,l=e.seekBackward;return[{key:e.spacebar,description:t},{key:"↑",description:a},{key:"↓",description:r},{key:"→",description:s},{key:"←",description:l},{key:"c",description:e.captionsToggle},{key:"f",description:n},{key:"m",description:i},{key:"0-9",description:o}]}(s),s.keyboardShortcuts)),d={reason:"settingsInteraction"},w=new u.a(c.querySelector(".jw-switch")),h=function(){w.el.setAttribute("aria-checked",i.get("enableShortcuts")),Object(l.a)(c,"jw-open"),r=i.get("state"),c.querySelector(".jw-shortcuts-close").focus(),document.addEventListener("click",g),o=!0,t.pause(d)},f=function(){Object(l.o)(c,"jw-open"),document.removeEventListener("click",g),e.focus(),o=!1,r===a.pb&&t.play(d)},g=function(e){/jw-shortcuts|jw-switch/.test(e.target.className)||f()},j=function(e){var t=e.currentTarget,n="true"!==t.getAttribute("aria-checked");t.setAttribute("aria-checked",n),i.set("enableShortcuts",n)};return n=p("jw-shortcuts-close",f,i.get("localization").close,[de("close")]),Object(l.m)(c,n.element()),n.show(),e.appendChild(c),w.on("click tap enter",j),{el:c,open:h,close:f,destroy:function(){f(),w.destroy()},toggleVisibility:function(){o?f():h()}}},vi=function(e){return'
    ')+"
    "};function yi(e){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ki(e,t){for(var i=0;i16?n.activeTimeout=setTimeout(n.userInactiveTimeout,e):n.playerContainer.querySelector(".jw-tab-focus")?n.resetActiveTimeout():n.userInactive()},n}var i,n,r;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&Ai(e,t)}(t,e),i=t,(n=[{key:"resetActiveTimeout",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.inactiveTime=0}},{key:"enable",value:function(e,t){var i=this,n=this.context.createElement("div");n.className="jw-controls jw-reset",this.div=n;var r=this.context.createElement("div");r.className="jw-controls-backdrop jw-reset",this.backdrop=r,this.logo=this.playerContainer.querySelector(".jw-logo");var c=t.get("touchMode"),u=function(){(t.get("isFloating")?i.wrapperElement:i.playerContainer).focus()};if(!this.displayContainer){var d=new Ot(t,e);d.buttons.display.on("click tap enter",(function(){i.trigger(a.p),i.userActive(1e3),e.playToggle(Li()),u()})),this.div.appendChild(d.element()),this.displayContainer=d}this.infoOverlay=new bi(n,t,e,(function(e){Object(l.v)(i.div,"jw-info-open",e),e&&i.div.querySelector(".jw-info-close").focus()})),o.OS.mobile||(this.shortcutsTooltip=new mi(this.wrapperElement,e,t)),this.rightClickMenu=new Vt(this.infoOverlay,this.shortcutsTooltip),c?(Object(l.a)(this.playerContainer,"jw-flag-touch"),this.rightClickMenu.setup(t,this.playerContainer,this.wrapperElement)):t.change("flashBlocked",(function(e,t){t?i.rightClickMenu.destroy():i.rightClickMenu.setup(e,i.playerContainer,i.wrapperElement)}),this);var w=t.get("floating");if(w){var h=new Ci(n,t.get("localization").close);h.on(a.sb,(function(){return i.trigger("dismissFloating",{doNotForward:!0})})),!1!==w.dismissible&&Object(l.a)(this.playerContainer,"jw-floating-dismissible")}var f=this.controlbar=new dt(e,t,this.playerContainer.querySelector(".jw-hidden-accessibility"));if(f.on(a.sb,(function(){return i.userActive()})),f.on("nextShown",(function(e){this.trigger("nextShown",e)}),this),f.on("adjustVolume",k,this),t.get("nextUpDisplay")&&!f.nextUpToolTip){var g=new St(t,e,this.playerContainer);g.on("all",this.trigger,this),g.setup(this.context),f.nextUpToolTip=g,this.div.appendChild(g.element())}this.div.appendChild(f.element());var j=t.get("localization"),b=this.settingsMenu=hi(e,t.player,this.controlbar,j),m=null;this.controlbar.on("menuVisibility",(function(n){var o=n.visible,r=n.evt,s=t.get("state"),l={reason:"settingsInteraction"},c=i.controlbar.elements.settingsButton,d="keydown"===(r&&r.sourceEvent||r||{}).type,p=o||d?0:Pi;i.userActive(p),m=s,Object(fi.a)(t.get("containerWidth"))<2&&(o&&s===a.pb?e.pause(l):o||s!==a.ob||m!==a.pb||e.play(l)),!o&&d&&c?c.element().focus():r&&u()})),b.on("menuVisibility",(function(e){return i.controlbar.trigger("menuVisibility",e)})),this.controlbar.on("settingsInteraction",(function(e,t,i){if(t)return b.defaultChild.toggle(i);b.children[e].toggle(i)})),o.OS.mobile?this.div.appendChild(b.el):(this.playerContainer.setAttribute("aria-describedby","jw-shortcuts-tooltip-explanation"),this.div.insertBefore(b.el,f.element()));var v=function(t){if(t.get("autostartMuted")){var n=function(){return i.unmuteAutoplay(e,t)},a=function(e,t){t||n()};o.OS.mobile&&(i.mute=p("jw-autostart-mute jw-off",n,t.get("localization").unmute,[de("volume-0")]),i.mute.show(),i.div.appendChild(i.mute.element())),f.renderVolume(!0,t.get("volume")),Object(l.a)(i.playerContainer,"jw-flag-autostart"),t.on("change:autostartFailed",n,i),t.on("change:autostartMuted change:mute",a,i),i.muteChangeCallback=a,i.unmuteCallback=n}};function y(i){var n=0,o=t.get("duration"),a=t.get("position");if("DVR"===t.get("streamType")){var r=t.get("dvrSeekLimit");n=o,o=Math.max(a,-r)}var l=Object(s.a)(a+i,n,o);e.seek(l,Li())}function k(i){var n=Object(s.a)(t.get("volume")+i,0,100);e.setVolume(n)}t.once("change:autostartMuted",v),v(t);var x=function(n){if(n.ctrlKey||n.metaKey)return!0;var o=!i.settingsMenu.visible,a=!0===t.get("enableShortcuts"),r=i.instreamState;if(a||-1!==zi.indexOf(n.keyCode)){switch(n.keyCode){case 27:if(t.get("fullscreen"))e.setFullscreen(!1),i.playerContainer.blur(),i.userInactive();else{var s=e.getPlugin("related");s&&s.close({type:"escape"})}i.rightClickMenu.el&&i.rightClickMenu.hideMenuHandler(),i.infoOverlay.visible&&i.infoOverlay.close(),i.shortcutsTooltip&&i.shortcutsTooltip.close();break;case 13:case 32:if(document.activeElement.classList.contains("jw-switch")&&13===n.keyCode)return!0;e.playToggle(Li());break;case 37:!r&&o&&y(-5);break;case 39:!r&&o&&y(5);break;case 38:o&&k(10);break;case 40:o&&k(-10);break;case 67:var l=e.getCaptionsList().length;if(l){var c=(e.getCurrentCaptions()+1)%l;e.setCurrentCaptions(c)}break;case 77:e.setMute();break;case 70:e.setFullscreen();break;case 191:i.shortcutsTooltip&&i.shortcutsTooltip.toggleVisibility();break;default:if(n.keyCode>=48&&n.keyCode<=59){var u=(n.keyCode-48)/10*t.get("duration");e.seek(u,Li())}}return/13|32|37|38|39|40/.test(n.keyCode)?(n.preventDefault(),!1):void 0}};this.playerContainer.addEventListener("keydown",x),this.keydownCallback=x;var T=function(e){switch(e.keyCode){case 9:var t=i.playerContainer.contains(e.target)?0:Pi;i.userActive(t);break;case 32:e.preventDefault()}};this.playerContainer.addEventListener("keyup",T),this.keyupCallback=T;var O=function(e){var t=e.relatedTarget||document.querySelector(":focus");t&&(i.playerContainer.contains(t)||i.userInactive())};this.playerContainer.addEventListener("blur",O,!0),this.blurCallback=O;var C=function e(){"jw-shortcuts-tooltip-explanation"===i.playerContainer.getAttribute("aria-describedby")&&i.playerContainer.removeAttribute("aria-describedby"),i.playerContainer.removeEventListener("blur",e,!0)};this.shortcutsTooltip&&(this.playerContainer.addEventListener("blur",C,!0),this.onRemoveShortcutsDescription=C),this.userActive(),this.addControls(),this.addBackdrop(),t.set("controlsEnabled",!0)}},{key:"addControls",value:function(){this.wrapperElement.appendChild(this.div)}},{key:"disable",value:function(e){var t=this.nextUpToolTip,i=this.settingsMenu,n=this.infoOverlay,o=this.controlbar,a=this.rightClickMenu,r=this.shortcutsTooltip,s=this.playerContainer,c=this.div;clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.off(),e.off(null,null,this),e.set("controlsEnabled",!1),c.parentNode&&(Object(l.o)(s,"jw-flag-touch"),c.parentNode.removeChild(c)),o&&o.destroy(),a&&a.destroy(),this.keydownCallback&&s.removeEventListener("keydown",this.keydownCallback),this.keyupCallback&&s.removeEventListener("keyup",this.keyupCallback),this.blurCallback&&s.removeEventListener("blur",this.blurCallback),this.onRemoveShortcutsDescription&&s.removeEventListener("blur",this.onRemoveShortcutsDescription),this.displayContainer&&this.displayContainer.destroy(),t&&t.destroy(),i&&i.destroy(),n&&n.destroy(),r&&r.destroy(),this.removeBackdrop()}},{key:"controlbarHeight",value:function(){return this.dimensions.cbHeight||(this.dimensions.cbHeight=this.controlbar.element().clientHeight),this.dimensions.cbHeight}},{key:"element",value:function(){return this.div}},{key:"resize",value:function(){this.dimensions={}}},{key:"unmuteAutoplay",value:function(e,t){var i=!t.get("autostartFailed"),n=t.get("mute");i?n=!1:t.set("playOnViewable",!1),this.muteChangeCallback&&(t.off("change:autostartMuted change:mute",this.muteChangeCallback),this.muteChangeCallback=null),this.unmuteCallback&&(t.off("change:autostartFailed",this.unmuteCallback),this.unmuteCallback=null),t.set("autostartFailed",void 0),t.set("autostartMuted",void 0),e.setMute(n),this.controlbar.renderVolume(n,t.get("volume")),this.mute&&this.mute.hide(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.userActive()}},{key:"mouseMove",value:function(e){var t=this.controlbar.element().contains(e.target),i=this.controlbar.nextUpToolTip&&this.controlbar.nextUpToolTip.element().contains(e.target),n=this.logo&&this.logo.contains(e.target),o=t||i||n?0:Pi;this.userActive(o)}},{key:"userActive",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Pi;e>0?(this.inactiveTime=Object(c.a)()+e,-1===this.activeTimeout&&(this.activeTimeout=setTimeout(this.userInactiveTimeout,e))):this.resetActiveTimeout(),this.showing||(Object(l.o)(this.playerContainer,"jw-flag-user-inactive"),this.showing=!0,this.trigger("userActive"))}},{key:"userInactive",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.settingsMenu.visible||(this.inactiveTime=0,this.showing=!1,Object(l.a)(this.playerContainer,"jw-flag-user-inactive"),this.trigger("userInactive"))}},{key:"addBackdrop",value:function(){var e=this.instreamState?this.div:this.wrapperElement.querySelector(".jw-captions");this.wrapperElement.insertBefore(this.backdrop,e)}},{key:"removeBackdrop",value:function(){var e=this.backdrop.parentNode;e&&e.removeChild(this.backdrop)}},{key:"setupInstream",value:function(){this.instreamState=!0,this.userActive(),this.addBackdrop(),this.settingsMenu&&this.settingsMenu.close(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","-1")}},{key:"destroyInstream",value:function(e){this.instreamState=null,this.addBackdrop(),e.get("autostartMuted")&&Object(l.a)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","0")}}])&&_i(i.prototype,n),r&&_i(i,r),t}(r.a)},function(e,t,i){"use strict";i.r(t);var n=i(0),o=i(12),a=i(50),r=i(36);var s=i(44),l=i(51),c=i(26),u=i(25),d=i(3),p=i(46),w=i(2),h=i(7),f=i(34);function g(e){var t=!1;return{async:function(){var i=this,n=arguments;return Promise.resolve().then((function(){if(!t)return e.apply(i,n)}))},cancel:function(){t=!0},cancelled:function(){return t}}}var j=i(1);function b(e){return function(t,i){var o=e.mediaModel,a=Object(n.g)({},i,{type:t});switch(t){case d.T:if(o.get(d.T)===i.mediaType)return;o.set(d.T,i.mediaType);break;case d.U:return void o.set(d.U,Object(n.g)({},i));case d.M:if(i[t]===e.model.getMute())return;break;case d.bb:i.newstate===d.mb&&(e.thenPlayPromise.cancel(),o.srcReset());var r=o.attributes.mediaState;o.attributes.mediaState=i.newstate,o.trigger("change:mediaState",o,i.newstate,r);break;case d.F:return e.beforeComplete=!0,e.trigger(d.B,a),void(e.attached&&!e.background&&e._playbackComplete());case d.G:o.get("setup")?(e.thenPlayPromise.cancel(),o.srcReset()):(t=d.tb,a.code+=1e5);break;case d.K:a.metadataType||(a.metadataType="unknown");var s=i.duration;Object(n.u)(s)&&(o.set("seekRange",i.seekRange),o.set("duration",s));break;case d.D:o.set("buffer",i.bufferPercent);case d.S:o.set("seekRange",i.seekRange),o.set("position",i.position),o.set("currentTime",i.currentTime);var l=i.duration;Object(n.u)(l)&&o.set("duration",l),t===d.S&&Object(n.r)(e.item.starttime)&&delete e.item.starttime;break;case d.R:var c=e.mediaElement;c&&c.paused&&o.set("mediaState","paused");break;case d.I:o.set(d.I,i.levels);case d.J:var u=i.currentQuality,p=i.levels;u>-1&&p.length>1&&o.set("currentLevel",parseInt(u));break;case d.f:o.set(d.f,i.tracks);case d.g:var w=i.currentTrack,h=i.tracks;w>-1&&h.length>0&&w=Math.max(l,p.a)&&(e.preloadNextItem(),v=!0)}function P(e){var t={};b.tag&&(t.tag=b.tag),this.trigger(d.F,t),z.call(this,e)}function z(e){g={},a&&f+10?e:null,h&&h.model.set("skipOffset",s)}};Object(n.g)(le.prototype,h.a);var ce=le,ue=i(66),de=i(63),pe=function(e){var t=this,i=[],n={},o=0,a=0;function r(e){if(e.data=e.data||[],e.name=e.label||e.name||e.language,e._id=Object(de.a)(e,i.length),!e.name){var t=Object(de.b)(e,o);e.name=t.label,o=t.unknownCount}n[e._id]=e,i.push(e)}function s(){for(var e=[{id:"off",label:"Off"}],t=0;t')+'
    '},fe=i(35),ge=44,je=function(e){var t=e.get("height");if(e.get("aspectratio"))return!1;if("string"==typeof t&&t.indexOf("%")>-1)return!1;var i=1*t||NaN;return!!(i=isNaN(i)?e.get("containerHeight"):i)&&(i&&i<=ge)},be=i(54);function me(e,t){if(e.get("fullscreen"))return 1;if(!e.get("activeTab"))return 0;if(e.get("isFloating"))return 1;var i=e.get("intersectionRatio");return void 0===i&&(i=function(e){var t=document.documentElement,i=document.body,n={top:0,left:0,right:t.clientWidth||i.clientWidth,width:t.clientWidth||i.clientWidth,bottom:t.clientHeight||i.clientHeight,height:t.clientHeight||i.clientHeight};if(!i.contains(e))return 0;if("none"===window.getComputedStyle(e).display)return 0;var o=ve(e);if(!o)return 0;var a=o,r=e.parentNode,s=!1;for(;!s;){var l=null;if(r===i||r===t||1!==r.nodeType?(s=!0,l=n):"visible"!==window.getComputedStyle(r).overflow&&(l=ve(r)),l&&(c=l,u=a,d=void 0,p=void 0,w=void 0,h=void 0,f=void 0,g=void 0,d=Math.max(c.top,u.top),p=Math.min(c.bottom,u.bottom),w=Math.max(c.left,u.left),h=Math.min(c.right,u.right),g=p-d,!(a=(f=h-w)>=0&&g>=0&&{top:d,bottom:p,left:w,right:h,width:f,height:g})))return 0;r=r.parentNode}var c,u,d,p,w,h,f,g;var j=o.width*o.height,b=a.width*a.height;return j?b/j:0}(t),window.top!==window.self&&i)?0:i}function ve(e){try{return e.getBoundingClientRect()}catch(e){}}var ye=i(49),ke=i(42),xe=i(58),Te=i(10);var Oe=i(32),Ce=i(5),Me=i(6),_e=["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],Se=function(e,t,i){for(var n=e.requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen,o=t.exitFullscreen||t.webkitExitFullscreen||t.webkitCancelFullScreen||t.mozCancelFullScreen||t.msExitFullscreen,a=!(!n||!o),r=_e.length;r--;)t.addEventListener(_e[r],i);return{events:_e,supportsDomFullscreen:function(){return a},requestFullscreen:function(){n.call(e,{navigationUI:"hide"})},exitFullscreen:function(){null!==this.fullscreenElement()&&o.apply(t)},fullscreenElement:function(){var e=t.fullscreenElement,i=t.webkitCurrentFullScreenElement,n=t.mozFullScreenElement,o=t.msFullscreenElement;return null===e?e:e||i||n||o},destroy:function(){for(var e=_e.length;e--;)t.removeEventListener(_e[e],i)}}},Ee=i(40);function Ae(e,t){for(var i=0;i')},Be={linktarget:"_blank",margin:8,hide:!1,position:"top-right"};function Ie(e){var t,i;Object(n.g)(this,h.a);var o=new Image;this.setup=function(){(i=Object(n.g)({},Be,e.get("logo"))).position=i.position||Be.position,i.hide="true"===i.hide.toString(),i.file&&"control-bar"!==i.position&&(t||(t=Object(Ce.e)(Le(i.position,i.hide))),e.set("logo",i),o.onload=function(){var n=this.height,o=this.width,a={backgroundImage:'url("'+this.src+'")'};if(i.margin!==Be.margin){var r=/(\w+)-(\w+)/.exec(i.position);3===r.length&&(a["margin-"+r[1]]=i.margin,a["margin-"+r[2]]=i.margin)}var s=.15*e.get("containerHeight"),l=.15*e.get("containerWidth");if(n>s||o>l){var c=o/n;l/s>c?(n=s,o=s*c):(o=l,n=l/c)}a.width=Math.round(o),a.height=Math.round(n),Object(Te.d)(t,a),e.set("logoWidth",a.width)},o.src=i.file,i.link&&(t.setAttribute("tabindex","0"),t.setAttribute("aria-label",e.get("localization").logo)),this.ui=new Ee.a(t).on("click tap enter",(function(e){e&&e.stopPropagation&&e.stopPropagation(),this.trigger(d.A,{link:i.link,linktarget:i.linktarget})}),this))},this.setContainer=function(e){t&&e.appendChild(t)},this.element=function(){return t},this.position=function(){return i.position},this.destroy=function(){o.onload=null,this.ui&&this.ui.destroy()}}var Re=function(e){this.model=e,this.image=null};Object(n.g)(Re.prototype,{setup:function(e){this.el=e},setImage:function(e){var t=this.image;t&&(t.onload=null),this.image=null;var i="";"string"==typeof e&&(i='url("'+e+'")',(t=this.image=new Image).src=e),Object(Te.d)(this.el,{backgroundImage:i})},resize:function(e,t,i){if("uniform"===i){if(e&&(this.playerAspectRatio=e/t),!this.playerAspectRatio||!this.image||"complete"!==(s=this.model.get("state"))&&"idle"!==s&&"error"!==s&&"buffering"!==s)return;var n=this.image,o=null;if(n){if(0===n.width){var a=this;return void(n.onload=function(){a.resize(e,t,i)})}var r=n.width/n.height;Math.abs(this.playerAspectRatio-r)<.09&&(o="cover")}Object(Te.d)(this.el,{backgroundSize:o})}var s},element:function(){return this.el}});var Ve=Re,Ne=function(e){this.model=e.player};Object(n.g)(Ne.prototype,{hide:function(){Object(Te.d)(this.el,{display:"none"})},show:function(){Object(Te.d)(this.el,{display:""})},setup:function(e){this.el=e;var t=this.el.getElementsByTagName("div");this.title=t[0],this.description=t[1],this.model.on("change:logoWidth",this.update,this),this.model.change("playlistItem",this.playlistItem,this)},update:function(e){var t={},i=e.get("logo");if(i){var n=1*(""+i.margin).replace("px",""),o=e.get("logoWidth")+(isNaN(n)?0:n+10);"top-left"===i.position?t.paddingLeft=o:"top-right"===i.position&&(t.paddingRight=o)}Object(Te.d)(this.el,t)},playlistItem:function(e,t){if(t)if(e.get("displaytitle")||e.get("displaydescription")){var i="",n="";t.title&&e.get("displaytitle")&&(i=t.title),t.description&&e.get("displaydescription")&&(n=t.description),this.updateText(i,n)}else this.hide()},updateText:function(e,t){Object(Ce.q)(this.title,e),Object(Ce.q)(this.description,t),this.title.firstChild||this.description.firstChild?this.show():this.hide()},element:function(){return this.el}});var He=Ne;function Fe(e,t){for(var i=0;ie)}if(t.get("controls")){var r=je(t);Object(Ce.v)(u,"jw-flag-audio-player",r),t.set("audioMode",r)}}function I(){t.set("visibility",me(t,u))}this.updateBounds=function(){Object(ke.a)(k);var e=t.get("isFloating")?p:u,i=document.body.contains(e),n=Object(Ce.c)(e),r=Math.round(n.width),s=Math.round(n.height);if(S=Object(Ce.c)(u),r===o&&s===a)return o&&a||z(),void t.set("inDom",i);r&&s||o&&a||z(),(r||s||i)&&(t.set("containerWidth",r),t.set("containerHeight",s)),t.set("inDom",i),i&&be.a.observe(u)},this.updateStyles=function(){var e=t.get("containerWidth"),i=t.get("containerHeight");B(e,i),A&&A.resize(e,i),$(e,i),v.resize(),T&&F()},this.checkResized=function(){var e=t.get("containerWidth"),i=t.get("containerHeight"),n=t.get("isFloating");if(e!==o||i!==a){this.resizeListener||(this.resizeListener=new Ue.a(p,this,t)),o=e,a=i,l.trigger(d.hb,{width:e,height:i});var s=Object(xe.a)(e);E!==s&&(E=s,l.trigger(d.j,{breakpoint:E}))}n!==r&&(r=n,l.trigger(d.x,{floating:n}),I())},this.responsiveListener=z,this.setup=function(){j.setup(u.querySelector(".jw-preview")),b.setup(u.querySelector(".jw-title")),(i=new Ie(t)).setup(),i.setContainer(p),i.on(d.A,J),v.setup(u.id,t.get("captions")),b.element().parentNode.insertBefore(v.element(),b.element()),O=function(e,t,i){var n=new Pe(t,i),o=t.get("controls");n.on({click:function(){l.trigger(d.p),A&&(ce()?A.settingsMenu.close():ue()?A.infoOverlay.close():e.playToggle({reason:"interaction"}))},tap:function(){l.trigger(d.p),ce()&&A.settingsMenu.close(),ue()&&A.infoOverlay.close();var i=t.get("state");if(o&&(i===d.mb||i===d.kb||t.get("instream")&&i===d.ob)&&e.playToggle({reason:"interaction"}),o&&i===d.ob){if(t.get("instream")||t.get("castActive")||"audio"===t.get("mediaType"))return;Object(Ce.v)(u,"jw-flag-controls-hidden"),l.dismissible&&Object(Ce.v)(u,"jw-floating-dismissible",Object(Ce.i)(u,"jw-flag-controls-hidden")),v.renderCues(!0)}else A&&(A.showing?A.userInactive():A.userActive())},doubleClick:function(){return A&&e.setFullscreen()}}),We||(u.addEventListener("mousemove",W),u.addEventListener("mouseover",Q),u.addEventListener("mouseout",Y));return n}(e,t,f),M=new Ee.a(u).on("click",(function(){})),C=Se(u,document,te),t.on("change:hideAdsControls",(function(e,t){Object(Ce.v)(u,"jw-flag-ads-hide-controls",t)})),t.on("change:scrubbing",(function(e,t){Object(Ce.v)(u,"jw-flag-dragging",t)})),t.on("change:playRejected",(function(e,t){Object(Ce.v)(u,"jw-flag-play-rejected",t)})),t.on(d.X,ee),t.on("change:".concat(d.U),(function(){$(),v.resize()})),t.player.on("change:errorEvent",ae),t.change("stretching",X);var n=t.get("width"),o=t.get("height"),a=G(n,o);Object(Te.d)(u,a),t.change("aspectratio",K),B(n,o),t.get("controls")||(Object(Ce.a)(u,"jw-flag-controls-hidden"),Object(Ce.o)(u,"jw-floating-dismissible")),Qe&&Object(Ce.a)(u,"jw-ie");var r=t.get("skin")||{};r.name&&Object(Ce.p)(u,/jw-skin-\S+/,"jw-skin-"+r.name);var s=function(e){e||(e={});var t=e.active,i=e.inactive,n=e.background,o={};return o.controlbar=function(e){if(e||t||i||n){var o={};return e=e||{},o.iconsActive=e.iconsActive||t,o.icons=e.icons||i,o.text=e.text||i,o.background=e.background||n,o}}(e.controlbar),o.timeslider=function(e){if(e||t){var i={};return e=e||{},i.progress=e.progress||t,i.rail=e.rail,i}}(e.timeslider),o.menus=function(e){if(e||t||i||n){var o={};return e=e||{},o.text=e.text||i,o.textActive=e.textActive||t,o.background=e.background||n,o}}(e.menus),o.tooltips=function(e){if(e||i||n){var t={};return e=e||{},t.text=e.text||i,t.background=e.background||n,t}}(e.tooltips),o}(r);!function(e,t){var i;function n(t,i,n,o){if(n){t=Object(w.f)(t,"#"+e+(o?"":" "));var a={};a[i]=n,Object(Te.b)(t.join(", "),a,e)}}t&&(t.controlbar&&function(t){n([".jw-controlbar .jw-icon-inline.jw-text",".jw-title-primary",".jw-title-secondary"],"color",t.text),t.icons&&(n([".jw-button-color:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:not(.jw-icon-cast)"],"color",t.icons),n([".jw-display-icon-container .jw-button-color"],"color",t.icons),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.icons,"}"),e));t.iconsActive&&(n([".jw-display-icon-container .jw-button-color:hover",".jw-display-icon-container .jw-button-color:focus"],"color",t.iconsActive),n([".jw-button-color.jw-toggle:not(.jw-icon-cast)",".jw-button-color:hover:not(.jw-icon-cast)",".jw-button-color:focus:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:hover:not(.jw-icon-cast)"],"color",t.iconsActive),n([".jw-svg-icon-buffer"],"fill",t.icons),Object(Te.b)("#".concat(e," .jw-icon-cast:hover google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:focus google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher.jw-off:focus"),"{--disconnected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast google-cast-launcher:focus"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:hover google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e),Object(Te.b)("#".concat(e," .jw-icon-cast:focus google-cast-launcher"),"{--connected-color: ".concat(t.iconsActive,"}"),e));n([" .jw-settings-topbar",":not(.jw-state-idle) .jw-controlbar",".jw-flag-audio-player .jw-controlbar"],"background",t.background,!0)}(t.controlbar),t.timeslider&&function(e){var t=e.progress;"none"!==t&&(n([".jw-progress",".jw-knob"],"background-color",t),n([".jw-buffer"],"background-color",Object(Te.c)(t,50)));n([".jw-rail"],"background-color",e.rail),n([".jw-background-color.jw-slider-time",".jw-slider-time .jw-cue"],"background-color",e.background)}(t.timeslider),t.menus&&(n([".jw-option",".jw-toggle.jw-off",".jw-skip .jw-skip-icon",".jw-nextup-tooltip",".jw-nextup-close",".jw-settings-content-item",".jw-related-title"],"color",(i=t.menus).text),n([".jw-option.jw-active-option",".jw-option:not(.jw-active-option):hover",".jw-option:not(.jw-active-option):focus",".jw-settings-content-item:hover",".jw-nextup-tooltip:hover",".jw-nextup-tooltip:focus",".jw-nextup-close:hover"],"color",i.textActive),n([".jw-nextup",".jw-settings-menu"],"background",i.background)),t.tooltips&&function(e){n([".jw-skip",".jw-tooltip .jw-text",".jw-time-tip .jw-text"],"background-color",e.background),n([".jw-time-tip",".jw-tooltip"],"color",e.background),n([".jw-skip"],"border","none"),n([".jw-skip .jw-text",".jw-skip .jw-icon",".jw-time-tip .jw-text",".jw-tooltip .jw-text"],"color",e.text)}(t.tooltips),t.menus&&function(t){if(t.textActive){var i={color:t.textActive,borderColor:t.textActive,stroke:t.textActive};Object(Te.b)("#".concat(e," .jw-color-active"),i,e),Object(Te.b)("#".concat(e," .jw-color-active-hover:hover"),i,e)}if(t.text){var n={color:t.text,borderColor:t.text,stroke:t.text};Object(Te.b)("#".concat(e," .jw-color-inactive"),n,e),Object(Te.b)("#".concat(e," .jw-color-inactive-hover:hover"),n,e)}}(t.menus))}(t.get("id"),s),t.set("mediaContainer",f),t.set("iFrame",m.Features.iframe),t.set("activeTab",Object(ye.a)()),t.set("touchMode",We&&("string"==typeof o||o>=ge)),be.a.add(this),t.get("enableGradient")&&!Qe&&Object(Ce.a)(u,"jw-ab-drop-shadow"),this.isSetup=!0,t.trigger("viewSetup",u);var c=document.body.contains(u);c&&be.a.observe(u),t.set("inDom",c)},this.init=function(){this.updateBounds(),t.on("change:fullscreen",Z),t.on("change:activeTab",I),t.on("change:fullscreen",I),t.on("change:intersectionRatio",I),t.on("change:visibility",U),t.on("instreamMode",(function(e){e?de():pe()})),I(),1!==be.a.size()||t.get("visibility")||U(t,1,0);var e=t.player;t.change("state",re),e.change("controls",D),t.change("streamType",ne),t.change("mediaType",oe),e.change("playlistItem",(function(e,t){le(e,t)})),o=a=null,T&&We&&be.a.addScrollHandler(F),this.checkResized()};var R,V=62,N=!0;function H(){var e=t.get("isFloating"),i=S.top0&&void 0!==arguments[0])||arguments[0],t={x:0,y:0,width:o||0,height:a||0};return A&&e&&(t.height-=A.controlbarHeight()),t},this.setCaptions=function(e){v.clear(),v.setup(t.get("id"),e),v.resize()},this.setIntersection=function(e){var i=Math.round(100*e.intersectionRatio)/100;t.set("intersectionRatio",i),T&&!P()&&(_=_||i>=.5)&&we(i)},this.stopFloating=function(e,i){if(e&&(T=null,be.a.removeScrollHandler(F)),Ye===u){Ye=null,t.set("isFloating",!1);var n=function(){Object(Ce.o)(u,"jw-flag-floating"),K(t,t.get("aspectratio")),Object(Te.d)(u,{backgroundImage:null}),Object(Te.d)(p,{maxWidth:null,width:null,height:null,left:null,right:null,top:null,bottom:null,margin:null,transform:null,transition:null,"transition-timing-function":null})};i?(Object(Te.d)(p,{transform:"translateY(-".concat(V-S.top,"px)"),"transition-timing-function":"ease-out"}),setTimeout(n,150)):n(),g.disable(),z()}},this.destroy=function(){t.destroy(),be.a.unobserve(u),be.a.remove(this),this.isSetup=!1,this.off(),Object(ke.a)(k),clearTimeout(y),Ye===u&&(Ye=null),M&&(M.destroy(),M=null),C&&(C.destroy(),C=null),A&&A.disable(t),O&&(O.destroy(),u.removeEventListener("mousemove",W),u.removeEventListener("mouseout",Y),u.removeEventListener("mouseover",Q),O=null),v.destroy(),i&&(i.destroy(),i=null),Object(Te.a)(t.get("id")),this.resizeListener&&(this.resizeListener.destroy(),delete this.resizeListener),T&&We&&be.a.removeScrollHandler(F)}};function Ke(e,t,i){return(Ke="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,i){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=tt(e)););return e}(e,t);if(n){var o=Object.getOwnPropertyDescriptor(n,t);return o.get?o.get.call(i):o.value}})(e,t,i||e)}function Je(e){return(Je="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ze(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Ge(e,t){for(var i=0;it&&e(),t=n}};function Ot(e,t){t.off(d.N,e._onPlayAttempt),t.off(d.fb,e._triggerFirstFrame),t.off(d.S,e._onTime),e.off("change:activeTab",e._onTabVisible)}var Ct=function(e,t){e.change("mediaModel",(function(e,i,n){e._qoeItem&&n&&e._qoeItem.end(n.get("mediaState")),e._qoeItem=new yt.a,e._qoeItem.getFirstFrame=function(){var e=this.between(d.N,d.H),t=this.between(xt,d.H);return t>0&&t0&&re(t,e.tracks)}),O).on(d.F,(function(){Promise.resolve().then(ae)}),O).on(d.G,O.triggerError,O),Ct(C,R),C.on(d.w,O.triggerError,O),C.on("change:state",(function(e,t,i){X()||K.call(T,e,t,i)}),this),C.on("change:castState",(function(e,t){O.trigger(d.m,t)})),C.on("change:fullscreen",(function(e,t){O.trigger(d.y,{fullscreen:t}),t&&e.set("playOnViewable",!1)})),C.on("change:volume",(function(e,t){O.trigger(d.V,{volume:t})})),C.on("change:mute",(function(e){O.trigger(d.M,{mute:e.getMute()})})),C.on("change:playbackRate",(function(e,t){O.trigger(d.ab,{playbackRate:t,position:e.get("position")})}));var V=function e(t,i){"clickthrough"!==i&&"interaction"!==i&&"external"!==i||(C.set("playOnViewable",!1),C.off("change:playReason change:pauseReason",e))};function N(e,t){Object(n.t)(t)||C.set("viewable",Math.round(t))}function H(){de&&(!0!==C.get("autostart")||C.get("playOnViewable")||$("autostart"),de.flush())}function F(e,t){O.trigger("viewable",{viewable:t}),D()}function D(){if((o.a[0]===t||1===C.get("viewable"))&&"idle"===C.get("state")&&!1===C.get("autostart"))if(!b.primed()&&m.OS.android){var e=b.getTestElement(),i=O.getMute();Promise.resolve().then((function(){return ht(e,{muted:i})})).then((function(){"idle"===C.get("state")&&R.preloadVideo()})).catch(St)}else R.preloadVideo()}function q(e){O._instreamAdapter.noResume=!e,e||te({reason:"viewable"})}function U(e){e||(O.pause({reason:"viewable"}),C.set("playOnViewable",!e))}function W(e,t){var i=X();if(e.get("playOnViewable")){if(t){var n=e.get("autoPause").pauseAds,o=e.get("pauseReason");J()===d.mb?$("viewable"):i&&!n||"interaction"===o||Z({reason:"viewable"})}else m.OS.mobile&&!i&&(O.pause({reason:"autostart"}),C.set("playOnViewable",!0));m.OS.mobile&&i&&q(t)}}function Q(e,t){var i=e.get("state"),n=X(),o=e.get("playReason");n?e.get("autoPause").pauseAds?U(t):q(t):i===d.pb||i===d.jb?U(t):i===d.mb&&"playlist"===o&&e.once("change:state",(function(){U(t)}))}function X(){var e=O._instreamAdapter;return!!e&&e.getState()}function J(){var e=X();return e||C.get("state")}function Z(e){if(E.cancel(),_=!1,C.get("state")===d.lb)return Promise.resolve();var i=G(e);return C.set("playReason",i),X()?(t.pauseAd(!1,e),Promise.resolve()):(C.get("state")===d.kb&&(ee(!0),O.setItemIndex(0)),!M&&(M=!0,O.trigger(d.C,{playReason:i,startTime:e&&e.startTime?e.startTime:C.get("playlistItem").starttime}),M=!1,vt()&&!b.primed()&&b.prime(),"playlist"===i&&C.get("autoPause").viewability&&Q(C,C.get("viewable")),x)?(vt()&&!B&&C.get("mediaElement").load(),x=!1,k=null,Promise.resolve()):R.playVideo(i).then(b.played))}function G(e){return e&&e.reason?e.reason:"unknown"}function $(e){if(J()===d.mb){E=g(H);var t=C.get("advertising");(function(e,t){var i=t.cancelable,n=t.muted,o=void 0!==n&&n,a=t.allowMuted,r=void 0!==a&&a,s=t.timeout,l=void 0===s?1e4:s,c=e.getTestElement(),u=o?"muted":"".concat(r);bt[u]||(bt[u]=ht(c,{muted:o}).catch((function(e){if(!i.cancelled()&&!1===o&&r)return ht(c,{muted:o=!0});throw e})).then((function(){return o?(bt[u]=null,gt):ft})).catch((function(e){throw clearTimeout(d),bt[u]=null,e.reason=jt,e})));var d,p=bt[u].then((function(e){if(clearTimeout(d),i.cancelled()){var t=new Error("Autoplay test was cancelled");throw t.reason="cancelled",t}return e})),w=new Promise((function(e,t){d=setTimeout((function(){bt[u]=null;var e=new Error("Autoplay test timed out");e.reason="timeout",t(e)}),l)}));return Promise.race([p,w])})(b,{cancelable:E,muted:O.getMute(),allowMuted:!t||t.autoplayadsmuted}).then((function(t){return C.set("canAutoplay",t),t!==gt||O.getMute()||(C.set("autostartMuted",!0),ue(),C.once("change:autostartMuted",(function(e){e.off("change:viewable",W),O.trigger(d.M,{mute:C.getMute()})}))),O.getMute()&&C.get("enableDefaultCaptions")&&y.selectDefaultIndex(1),Z({reason:e}).catch((function(){O._instreamAdapter||C.set("autostartFailed",!0),k=null}))})).catch((function(e){if(C.set("canAutoplay",jt),C.set("autostart",!1),!E.cancelled()){var t=Object(j.w)(e);O.trigger(d.h,{reason:e.reason,code:t,error:e})}}))}}function ee(e){if(E.cancel(),de.empty(),X()){var t=O._instreamAdapter;return t&&(t.noResume=!0),void(k=function(){return R.stopVideo()})}k=null,!e&&(_=!0),M&&(x=!0),C.set("errorEvent",void 0),R.stopVideo()}function te(e){var t=G(e);C.set("pauseReason",t),C.set("playOnViewable","viewable"===t)}function ie(e){k=null,E.cancel();var i=X();if(i&&i!==d.ob)return te(e),void t.pauseAd(!0,e);switch(C.get("state")){case d.lb:return;case d.pb:case d.jb:te(e),R.pause();break;default:M&&(x=!0)}}function ne(e,t){ee(!0),O.setItemIndex(e),O.play(t)}function oe(e){ne(C.get("item")+1,e)}function ae(){O.completeCancelled()||(k=O.completeHandler,O.shouldAutoAdvance()?O.nextItem():C.get("repeat")?oe({reason:"repeat"}):(m.OS.iOS&&le(!1),C.set("playOnViewable",!1),C.set("state",d.kb),O.trigger(d.cb,{})))}function re(e,t){e=parseInt(e,10)||0,C.persistVideoSubtitleTrack(e,t),R.subtitles=e,O.trigger(d.k,{tracks:se(),track:e})}function se(){return y.getCaptionsList()}function le(e){Object(n.n)(e)||(e=!C.get("fullscreen")),C.set("fullscreen",e),O._instreamAdapter&&O._instreamAdapter._adModel&&O._instreamAdapter._adModel.set("fullscreen",e)}function ue(){R.mute=C.getMute(),R.volume=C.get("volume")}C.on("change:playReason change:pauseReason",V),O.on(d.c,(function(e){return V(0,e.playReason)})),O.on(d.b,(function(e){return V(0,e.pauseReason)})),C.on("change:scrubbing",(function(e,t){t?(S=C.get("state")!==d.ob,ie()):S&&Z({reason:"interaction"})})),C.on("change:captionsList",(function(e,t){O.trigger(d.l,{tracks:t,track:C.get("captionsIndex")||0})})),C.on("change:mediaModel",(function(e,t){var i=this;e.set("errorEvent",void 0),t.change("mediaState",(function(t,i){var n;e.get("errorEvent")||e.set(d.bb,(n=i)===d.nb||n===d.qb?d.jb:n)}),this),t.change("duration",(function(t,i){if(0!==i){var n=e.get("minDvrWindow"),o=Object(mt.b)(i,n);e.setStreamType(o)}}),this);var n=e.get("item")+1,o="autoplay"===(e.get("related")||{}).oncomplete,a=e.get("playlist")[n];if((a||o)&&B){t.on("change:position",(function e(n,r){var s=a&&!a.daiSetting,l=t.get("duration");s&&r&&l>0&&r>=l-p.b?(t.off("change:position",e,i),R.backgroundLoad(a)):o&&(a=C.get("nextUp"))}),this)}})),(y=new we(C)).on("all",L,O),I.on("viewSetup",(function(e){Object(a.b)(T,e)})),this.playerReady=function(){v.once(d.hb,(function(){try{!function(){C.change("visibility",N),P.off(),O.trigger(d.gb,{setupTime:0}),C.change("playlist",(function(e,t){if(t.length){var i={playlist:t},o=C.get("feedData");o&&(i.feedData=Object(n.g)({},o)),O.trigger(d.eb,i)}})),C.change("playlistItem",(function(e,t){if(t){var i=t.title,n=t.image;if("mediaSession"in navigator&&window.MediaMetadata&&(i||n))try{navigator.mediaSession.metadata=new window.MediaMetadata({title:i,artist:window.location.hostname,artwork:[{src:n||""}]})}catch(e){}e.set("cues",[]),O.trigger(d.db,{index:C.get("item"),item:t})}})),P.flush(),P.destroy(),P=null,C.change("viewable",F),C.change("viewable",W),C.get("autoPause").viewability?C.change("viewable",Q):C.once("change:autostartFailed change:mute",(function(e){e.off("change:viewable",W)}));H(),C.on("change:itemReady",(function(e,t){t&&de.flush()}))}()}catch(e){O.triggerError(Object(j.v)(j.m,j.a,e))}})),v.init()},this.preload=D,this.load=function(e,t){var i,n=O._instreamAdapter;switch(n&&(n.noResume=!0),O.trigger("destroyPlugin",{}),ee(!0),E.cancel(),E=g(H),A.cancel(),vt()&&b.prime(),Mt(e)){case"string":C.attributes.item=0,C.attributes.itemReady=!1,A=g((function(e){if(e)return O.updatePlaylist(Object(c.a)(e.playlist),e)})),i=function(e){var t=this;return new Promise((function(i,n){var o=new l.a;o.on(d.eb,(function(e){i(e)})),o.on(d.w,n,t),o.load(e)}))}(e).then(A.async);break;case"object":C.attributes.item=0,i=O.updatePlaylist(Object(c.a)(e),t||{});break;case"number":i=O.setItemIndex(e);break;default:return}i.catch((function(e){O.triggerError(Object(j.u)(e,j.c))})),i.then(E.async).catch(St)},this.play=function(e){return Z(e).catch(St)},this.pause=ie,this.seek=function(e,t){var i=C.get("state");if(i!==d.lb){R.position=e;var n=i===d.mb;C.get("scrubbing")||!n&&i!==d.kb||(n&&((t=t||{}).startTime=e),this.play(t))}},this.stop=ee,this.playlistItem=ne,this.playlistNext=oe,this.playlistPrev=function(e){ne(C.get("item")-1,e)},this.setCurrentCaptions=re,this.setCurrentQuality=function(e){R.quality=e},this.setFullscreen=le,this.getCurrentQuality=function(){return R.quality},this.getQualityLevels=function(){return R.qualities},this.setCurrentAudioTrack=function(e){R.audioTrack=e},this.getCurrentAudioTrack=function(){return R.audioTrack},this.getAudioTracks=function(){return R.audioTracks},this.getCurrentCaptions=function(){return y.getCurrentIndex()},this.getCaptionsList=se,this.getVisualQuality=function(){var e=this._model.get("mediaModel");return e?e.get(d.U):null},this.getConfig=function(){return this._model?this._model.getConfiguration():void 0},this.getState=J,this.next=St,this.completeHandler=ae,this.completeCancelled=function(){return(e=C.get("state"))!==d.mb&&e!==d.kb&&e!==d.lb||!!_&&(_=!1,!0);var e},this.shouldAutoAdvance=function(){return C.get("item")!==C.get("playlist").length-1},this.nextItem=function(){oe({reason:"playlist"})},this.setConfig=function(e){!function(e,t){var i=e._model,n=i.attributes;t.height&&(t.height=Object(r.b)(t.height),t.width=t.width||n.width),t.width&&(t.width=Object(r.b)(t.width),t.aspectratio?(n.width=t.width,delete t.width):t.height=n.height),t.width&&t.height&&!t.aspectratio&&e._view.resize(t.width,t.height),Object.keys(t).forEach((function(o){var a=t[o];if(void 0!==a)switch(o){case"aspectratio":i.set(o,Object(r.a)(a,n.width));break;case"autostart":!function(e,t,i){e.setAutoStart(i),"idle"===e.get("state")&&!0===i&&t.play({reason:"autostart"})}(i,e,a);break;case"mute":e.setMute(a);break;case"volume":e.setVolume(a);break;case"playbackRateControls":case"playbackRates":case"repeat":case"stretching":i.set(o,a)}}))}(O,e)},this.setItemIndex=function(e){R.stopVideo();var t=C.get("playlist").length;return(e=(parseInt(e,10)||0)%t)<0&&(e+=t),R.setActiveItem(e).catch((function(e){e.code>=151&&e.code<=162&&(e=Object(j.u)(e,j.e)),T.triggerError(Object(j.v)(j.k,j.d,e))}))},this.detachMedia=function(){if(M&&(x=!0),C.get("autoPause").viewability&&Q(C,C.get("viewable")),!B)return R.setAttached(!1);R.backgroundActiveMedia()},this.attachMedia=function(){B?R.restoreBackgroundMedia():R.setAttached(!0),"function"==typeof k&&k()},this.routeEvents=function(e){return R.routeEvents(e)},this.forwardEvents=function(){return R.forwardEvents()},this.playVideo=function(e){return R.playVideo(e)},this.stopVideo=function(){return R.stopVideo()},this.castVideo=function(e,t){return R.castVideo(e,t)},this.stopCast=function(){return R.stopCast()},this.backgroundActiveMedia=function(){return R.backgroundActiveMedia()},this.restoreBackgroundMedia=function(){return R.restoreBackgroundMedia()},this.preloadNextItem=function(){R.background.currentMedia&&R.preloadVideo()},this.isBeforeComplete=function(){return R.beforeComplete},this.setVolume=function(e){C.setVolume(e),ue()},this.setMute=function(e){C.setMute(e),ue()},this.setPlaybackRate=function(e){C.setPlaybackRate(e)},this.getProvider=function(){return C.get("provider")},this.getWidth=function(){return C.get("containerWidth")},this.getHeight=function(){return C.get("containerHeight")},this.getItemQoe=function(){return C._qoeItem},this.addButton=function(e,t,i,n,o){var a=C.get("customButtons")||[],r=!1,s={img:e,tooltip:t,callback:i,id:n,btnClass:o};a=a.reduce((function(e,t){return t.id===n?(r=!0,e.push(s)):e.push(t),e}),[]),r||a.unshift(s),C.set("customButtons",a)},this.removeButton=function(e){var t=C.get("customButtons")||[];t=t.filter((function(t){return t.id!==e})),C.set("customButtons",t)},this.resize=v.resize,this.getSafeRegion=v.getSafeRegion,this.setCaptions=v.setCaptions,this.checkBeforePlay=function(){return M},this.setControls=function(e){Object(n.n)(e)||(e=!C.get("controls")),C.set("controls",e),R.controls=e},this.addCues=function(e){this.setCues(C.get("cues").concat(e))},this.setCues=function(e){C.set("cues",e)},this.updatePlaylist=function(e,t){try{var i=Object(c.b)(e,C,t);Object(c.e)(i);var o=Object(n.g)({},t);delete o.playlist,C.set("feedData",o),C.set("playlist",i)}catch(e){return Promise.reject(e)}return this.setItemIndex(C.get("item"))},this.setPlaylistItem=function(e,t){(t=Object(c.d)(C,new u.a(t),t.feedData||{}))&&(C.get("playlist")[e]=t,e===C.get("item")&&"idle"===C.get("state")&&this.setItemIndex(e))},this.playerDestroy=function(){this.off(),this.stop(),Object(a.b)(this,this.originalContainer),v&&v.destroy(),C&&C.destroy(),de&&de.destroy(),y&&y.destroy(),R&&R.destroy(),this.instreamDestroy()},this.isBeforePlay=this.checkBeforePlay,this.createInstream=function(){return this.instreamDestroy(),this._instreamAdapter=new ce(this,C,v,b),this._instreamAdapter},this.instreamDestroy=function(){O._instreamAdapter&&(O._instreamAdapter.destroy(),O._instreamAdapter=null)};var de=new s.a(this,["play","pause","setCurrentAudioTrack","setCurrentCaptions","setCurrentQuality","setFullscreen"],(function(){return!T._model.get("itemReady")||P}));de.queue.push.apply(de.queue,f),v.setup()},get:function(e){if(e in y.a){var t=this._model.get("mediaModel");return t?t.get(e):y.a[e]}return this._model.get(e)},getContainer:function(){return this.currentContainer||this.originalContainer},getMute:function(){return this._model.getMute()},triggerError:function(e){var t=this._model;e.message=t.get("localization").errors[e.key],delete e.key,t.set("errorEvent",e),t.set("state",d.lb),t.once("change:state",(function(){this.set("errorEvent",void 0)}),t),this.trigger(d.w,e)}});t.default=_t},,,,,,,,,,,,,function(e,t,i){"use strict";i.r(t);var n=i(0);var o=i(8),a=i(52),r=i(3),s=i(43),l={canplay:function(){this.trigger(r.E)},play:function(){this.stallTime=-1,this.video.paused||this.state===r.pb||this.setState(r.nb)},loadedmetadata:function(){var e={metadataType:"media",duration:this.getDuration(),height:this.video.videoHeight,width:this.video.videoWidth,seekRange:this.getSeekRange()},t=this.drmUsed;t&&(e.drm=t),this.trigger(r.K,e)},timeupdate:function(){var e=this.getVideoCurrentTime(),t=this.getCurrentTime(),i=this.getDuration();if(!isNaN(i)){this.seeking||this.video.paused||this.state!==r.qb&&this.state!==r.nb||this.stallTime===e||(this.stallTime=-1,this.setState(r.pb),this.trigger(r.fb));var n={position:t,duration:i,currentTime:e,seekRange:this.getSeekRange(),metadata:{currentTime:e}};if(this.getPtsOffset){var o=this.getPtsOffset();o>=0&&(n.metadata.mpegts=o+t)}var a=this.getLiveLatency();null!==a&&(n.latency=a),(this.state===r.pb||this.seeking)&&this.trigger(r.S,n)}},click:function(e){this.trigger(r.n,e)},volumechange:function(){var e=this.video;this.trigger(r.V,{volume:Math.round(100*e.volume)}),this.trigger(r.M,{mute:e.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(r.R))},playing:function(){-1===this.stallTime&&this.setState(r.pb),this.trigger(r.fb)},pause:function(){this.state!==r.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(r.ob))},progress:function(){var e=this.getDuration();if(!(e<=0||e===1/0)){var t=this.video.buffered;if(t&&0!==t.length){var i=Object(s.a)(t.end(t.length-1)/e,0,1);this.trigger(r.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:e,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(r.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==r.mb&&this.state!==r.kb&&this.trigger(r.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},c=i(10);function u(e){return e&&e.length?e.end(e.length-1):0}var d={container:null,volume:function(e){this.video.volume=Math.min(Math.max(0,e/100),1)},mute:function(e){this.video.muted=!!e,this.video.muted||this.video.removeAttribute("muted")},resize:function(e,t,i){var n=this.video,a=n.videoWidth,r=n.videoHeight;if(e&&t&&a&&r){var s={objectFit:"",width:"",height:""};if("uniform"===i){var l=e/t,u=a/r,d=Math.abs(l-u);d<.09&&d>.0025&&(s.objectFit="fill",i="exactfit")}if(o.Browser.ie||o.OS.iOS&&o.OS.version.major<9||o.Browser.androidNative)if("uniform"!==i){s.objectFit="contain";var p=e/t,w=a/r,h=1,f=1;"none"===i?h=f=p>w?Math.ceil(100*r/t)/100:Math.ceil(100*a/e)/100:"fill"===i?h=f=p>w?p/w:w/p:"exactfit"===i&&(p>w?(h=p/w,f=1):(h=1,f=w/p)),Object(c.e)(n,"matrix(".concat(h.toFixed(2),", 0, 0, ").concat(f.toFixed(2),", 0, 0)"))}else s.top=s.left=s.margin="",Object(c.e)(n,"");Object(c.d)(n,s)}},getContainer:function(){return this.container},setContainer:function(e){this.container=e,this.video.parentNode!==e&&e.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var e=this.container;e&&e===this.video.parentNode&&e.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return u(this.video.buffered)-this.video.currentTime<=2}},p={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},w=i(65),h=i(5),f=i(53),g=i(7),j=i(66),b=i(63),m={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function v(e,t){for(var i,n,o,a=e.length,r="",s=t||0;s>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:r+=String.fromCharCode(i);break;case 12:case 13:n=e[s++],r+=String.fromCharCode((31&i)<<6|63&n);break;case 14:n=e[s++],o=e[s++],r+=String.fromCharCode((15&i)<<12|(63&n)<<6|(63&o)<<0)}return r}function y(e){var t=function(e){for(var t="0x",i=0;i>1|(8323072&t)>>2|(2130706432&t)>>3}function k(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(e,t){if(!("value"in t)&&"data"in t&&t.data instanceof ArrayBuffer){var i=new Uint8Array(t.data),n=i.length;t={value:{key:"",data:""}};for(var o=10;o<14&&o0){var c=v(i.subarray(a,a+=s),0);if("PRIV"===t.value.key){if("com.apple.streaming.transportStreamTimestamp"===c){var u=1&y(i.subarray(a,a+=4)),d=y(i.subarray(a,a+=4))+(u?4294967296:0);t.value.data=d}else t.value.data=v(i,a+1);t.value.info=c}else t.value.info=c,t.value.data=v(i,a+1)}else{var p=i[a];t.value.data=1===p||2===p?function(e,t){for(var i=e.length-1,n="",o=t||0;o=0&&o[a].startTime>t.startTime;a--)i.unshift(o[a]),e.removeCue(o[a]);try{e.addCue(t),i.forEach((function(t){return e.addCue(t)}))}catch(e){console.error(e)}e.mode=n}(t,n)}else try{t.addCue(i)}catch(e){console.error(e)}}function _(e,t){t&&t.length&&Object(n.f)(t,(function(t){if(!(o.Browser.ie&&e&&/^(native|subtitle|cc)/.test(t._id))){o.Browser.ie&&"disabled"===t.mode||(t.mode="disabled",t.mode="hidden");for(var i=t.cues.length;i--;)t.removeCue(t.cues[i]);t.embedded||(t.mode="disabled"),t.inuse=!1}}))}function S(e){return"subtitles"===e||"captions"===e}function E(e){var t,i=Object(b.b)(e,this._unknownCount),o=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===e.kind){var a=this.video.textTracks;(t=Object(n.j)(a,{label:o}))||(t=this.video.addTextTrack(e.kind,o,e.language||"")),t.default=e.default,t.mode="disabled",t.inuse=!0}else(t=e).data=t.data||[];return t._id||(t._id=Object(b.a)(e,this._textTracks.length)),t}function A(e){this._textTracks.push(e),this._tracksById[e._id]=e}function P(){if(this._textTracks){var e=this._textTracks.filter((function(e){return e.embedded||"subs"===e.groupid}));this._initTextTracks(),e.forEach((function(e){this._tracksById[e._id]=e})),this._textTracks=e}}function z(e){this.triggerActiveCues(e.currentTarget.activeCues)}function L(e,t,i){var n=e.kind;this._cachedVTTCues[e._id]||(this._cachedVTTCues[e._id]={});var o,a=this._cachedVTTCues[e._id];switch(n){case"captions":case"subtitles":o=i||Math.floor(20*t.startTime);var r="_"+t.line,s=Math.floor(20*t.endTime),l=a[o+r]||a[o+1+r]||a[o-1+r];return!(l&&Math.abs(l-s)<=1)&&(a[o+r]=s,!0);case"metadata":var c=t.data?new Uint8Array(t.data).join(""):t.text;return!a[o=i||t.startTime+c]&&(a[o]=t.endTime,!0);default:return!1}}function B(e){if(e.length>this._textTracks.length)return!0;for(var t=0;t=0&&(f.retries=0);var e=f.getVideoCurrentTime();f.currentTime=e,_&&C!==e&&$(e),l.timeupdate.call(f),he(),o.Browser.ie&&G()},resize:G,ended:function(){M=-1,fe(),l.ended.call(f)},loadedmetadata:function(){var e=f.getDuration();B&&e===1/0&&(e=0);var t={metadataType:"media",duration:e,height:v.videoHeight,width:v.videoWidth,seekRange:f.getSeekRange()};f.trigger(r.K,t),G()},durationchange:function(){B||l.progress.call(f)},loadeddata:function(){var e;!function(){if(v.getStartDate){var e=v.getStartDate(),t=e.getTime?e.getTime():NaN;if(t!==f.startDateTime&&!isNaN(t)){f.startDateTime=t;var i=e.toISOString(),n=f.getSeekRange(),o=n.start,a=n.end,s={metadataType:"program-date-time",programDateTime:i,start:o,end:a},l=f.createCue(o,a,JSON.stringify(s));f.addVTTCue({type:"metadata",cue:l}),delete s.metadataType,f.trigger(r.L,{metadataType:"program-date-time",metadata:s})}}}(),l.loadeddata.call(f),function(e){if(E=null,!e)return;if(e.length){for(var t=0;t0&&(t=e.map((function(e,t){return{label:e.label||t}}))),t}function ie(e){f.currentTime=-1,j=e.minDvrWindow,m=e.sources,M=function(e){var i=Math.max(0,M),n=t.qualityLabel;if(e)for(var o=0;o0&&(T=-1,f.seek(e)),e>0&&f.getVideoCurrentTime()!==e&&f.seek(e);var n=te(m);n&&f.trigger(r.I,{levels:n,currentQuality:M}),m.length&&"hls"!==m[0].type&&we()}function ae(e){E=null,A=-1,y.reason||(y.reason="initial choice",y.level={}),x=!1;var t=document.createElement("source");t.src=e.file,v.src!==t.src&&(v.src=e.file)}function re(){v&&(f.disableTextTrack(),v.removeAttribute("preload"),v.removeAttribute("src"),Object(h.h)(v),Object(c.d)(v,{objectFit:""}),M=-1,!o.Browser.msie&&"load"in v&&v.load())}function se(){var e=1/0;return["buffered","seekable"].forEach((function(t){for(var i=v[t],o=i?i.length:0;o--;){var a=Math.min(e,i.start(o));Object(n.o)(a)&&(e=a)}})),e}function le(){var e=0;return["buffered","seekable"].forEach((function(t){for(var i=v[t],o=i?i.length:0;o--;){var a=Math.max(e,i.end(o));Object(n.o)(a)&&(e=a)}})),e}function ce(){for(var e=-1,t=0;t-1&&e1)&&function(e){X=e.end,J=Math.min(0,f.getVideoCurrentTime()-X),Z=Object(V.a)()}(t),Object(w.a)(t.end-t.start,j))return J}return e}(f.getVideoCurrentTime())},f.getDuration=function(){if(t.getDurationHook)return t.getDurationHook();var e=v.duration;if(B&&e===1/0&&0===f.getVideoCurrentTime()||isNaN(e))return 0;var i=le();if(v.duration===1/0&&i){var n=i-se();Object(w.a)(n,j)&&(e=-n)}return e},f.getSeekRange=function(){var e={start:0,end:f.getDuration()};return v.seekable.length&&(e.end=le(),e.start=se()),e},f.getLiveLatency=function(){var e=null,t=le();return f.isLive()&&t&&(e=t+(Object(V.a)()-Z)/1e3-f.getVideoCurrentTime()),e},this.stop=function(){fe(),re(),this.clearTracks(),o.Browser.ie&&v.pause(),this.setState(r.mb)},this.destroy=function(){S=Q,Y(b,v),this.removeTracksListener(v.audioTracks,"change",ce),this.removeTracksListener(v.textTracks,"change",f.textTrackChangeHandler),this.off()},this.init=function(e){f.retries=0,f.maxRetries=e.adType?0:3,ie(e);var t=m[M];(B=Object(a.a)(t))&&(f.supportsPlaybackRate=!1,b.waiting=Q),f.eventsOn_(),m.length&&"hls"!==m[0].type&&this.sendMediaType(m),y.reason=""},this.preload=function(e){ie(e);var t=m[M],i=t.preload||"metadata";"none"!==i&&(v.setAttribute("preload",i),ae(t))},this.load=function(e){ie(e),oe(e.starttime),this.setupSideloadedTracks(e.tracks)},this.play=function(){return S(),ne()},this.pause=function(){fe(),S=function(){if(v.paused&&f.getVideoCurrentTime()&&f.isLive()){var e=le(),t=e-se(),i=!Object(w.a)(t,j),o=e-f.getVideoCurrentTime();if(i&&e&&(o>15||o<0)){if(O=Math.max(e-10,e-t),!Object(n.o)(O))return;$(f.getVideoCurrentTime()),v.currentTime=O}}},v.pause()},this.seek=function(e){if(!t.seekHook||!t.seekHook(e,v)){var i=f.getSeekRange(),n=e;if(e<0&&(n+=i.end),x||(x=!!le()),x){T=0;try{if(f.seeking=!0,f.isLive()&&Object(w.a)(i.end-i.start,j))if(J=Math.min(0,n-X),e<0)n+=Math.min(12,(Object(V.a)()-Z)/1e3);O=n,$(f.getVideoCurrentTime()),v.currentTime=n}catch(e){f.seeking=!1,T=n}}else T=n,o.Browser.firefox&&v.paused&&ne()}},this.setVisibility=function(e){(e=!!e)||o.OS.android?Object(c.d)(f.container,{visibility:"visible",opacity:1}):Object(c.d)(f.container,{visibility:"",opacity:0})},this.setFullscreen=function(e){if(e=!!e){try{var t=v.webkitEnterFullscreen||v.webkitEnterFullScreen;t&&t.apply(v)}catch(e){return!1}return f.getFullScreen()}var i=v.webkitExitFullscreen||v.webkitExitFullScreen;return i&&i.apply(v),e},f.getFullScreen=function(){return _||!!v.webkitDisplayingFullscreen},this.setCurrentQuality=function(e){M!==e&&e>=0&&m&&m.length>e&&(M=e,y.reason="api",y.level={},this.trigger(r.J,{currentQuality:e,levels:te(m)}),t.qualityLabel=m[e].label,oe(f.getVideoCurrentTime()||0),ne())},this.setPlaybackRate=function(e){v.playbackRate=v.defaultPlaybackRate=e},this.getPlaybackRate=function(){return v.playbackRate},this.getCurrentQuality=function(){return M},this.getQualityLevels=function(){return Array.isArray(m)?m.map((function(e){return function(e){return{bitrate:e.bitrate,label:e.label,width:e.width,height:e.height}}(e)})):[]},this.getName=function(){return{name:W}},this.setCurrentAudioTrack=de,this.getAudioTracks=function(){return E||[]},this.getCurrentAudioTrack=function(){return A}}Object(n.g)(X.prototype,f.a),X.getName=function(){return{name:"html5"}};t.default=X;var K=220001},,,,,,,,,,,,,,,,,,,,,,,,,,function(e,t,i){"use strict";i.d(t,"a",(function(){return o}));var n=i(2);function o(e){var t=[],i=(e=Object(n.i)(e)).split("\r\n\r\n");1===i.length&&(i=e.split("\n\n"));for(var o=0;o0&&(o=0),i.length>o+1&&i[o+1]){var a=i[o],r=a.indexOf(" --\x3e ");r>0&&(t.begin=Object(n.g)(a.substr(0,r)),t.end=Object(n.g)(a.substr(r+5)),t.text=i.slice(o+1).join("\r\n"))}return t}},function(e,t,i){"use strict";i.d(t,"a",(function(){return o})),i.d(t,"b",(function(){return a}));var n=i(5);function o(e){var t=-1;return e>=1280?t=7:e>=960?t=6:e>=800?t=5:e>=640?t=4:e>=540?t=3:e>=420?t=2:e>=320?t=1:e>=250&&(t=0),t}function a(e,t){var i="jw-breakpoint-"+t;Object(n.p)(e,/jw-breakpoint--?\d+/,i)}},function(e,t,i){"use strict";i.d(t,"a",(function(){return d}));var n,o=i(0),a=i(8),r=i(16),s=i(7),l=i(3),c=i(10),u=i(5),d={back:!0,backgroundOpacity:50,edgeStyle:null,fontSize:14,fontOpacity:100,fontScale:.05,preprocessor:o.k,windowOpacity:0},p=function(e){var t,s,p,w,h,f,g,j,b,m=this,v=e.player;function y(){Object(o.o)(t.fontSize)&&(v.get("containerHeight")?j=d.fontScale*(t.userFontScale||1)*t.fontSize/d.fontSize:v.once("change:containerHeight",y,this))}function k(){var e=v.get("containerHeight");if(e){var t;if(v.get("fullscreen")&&a.OS.iOS)t=null;else{var i=e*j;t=Math.round(10*function(e){var t=v.get("mediaElement");if(t&&t.videoHeight){var i=t.videoWidth,n=t.videoHeight,o=i/n,r=v.get("containerHeight"),s=v.get("containerWidth");if(v.get("fullscreen")&&a.OS.mobile){var l=window.screen;l.orientation&&(r=l.availHeight,s=l.availWidth)}if(s&&r&&i&&n)return(s/r>o?r:n*s/i)*j}return e}(i))/10}v.get("renderCaptionsNatively")?function(e,t){var i="#".concat(e," .jw-video::-webkit-media-text-track-display");t&&(t+="px",a.OS.iOS&&Object(c.b)(i,{fontSize:"inherit"},e,!0));b.fontSize=t,Object(c.b)(i,b,e,!0)}(v.get("id"),t):Object(c.d)(h,{fontSize:t})}}function x(e,t,i){var n=Object(c.c)("#000000",i);"dropshadow"===e?t.textShadow="0 2px 1px "+n:"raised"===e?t.textShadow="0 0 5px "+n+", 0 1px 5px "+n+", 0 2px 5px "+n:"depressed"===e?t.textShadow="0 -2px 1px "+n:"uniform"===e&&(t.textShadow="-2px 0 1px "+n+",2px 0 1px "+n+",0 -2px 1px "+n+",0 2px 1px "+n+",-1px 1px 1px "+n+",1px 1px 1px "+n+",1px -1px 1px "+n+",1px 1px 1px "+n)}(h=document.createElement("div")).className="jw-captions jw-reset",this.show=function(){Object(u.a)(h,"jw-captions-enabled")},this.hide=function(){Object(u.o)(h,"jw-captions-enabled")},this.populate=function(e){v.get("renderCaptionsNatively")||(p=[],s=e,e?this.selectCues(e,w):this.renderCues())},this.resize=function(){k(),this.renderCues(!0)},this.renderCues=function(e){e=!!e,n&&n.processCues(window,p,h,e)},this.selectCues=function(e,t){if(e&&e.data&&t&&!v.get("renderCaptionsNatively")){var i=this.getAlignmentPosition(e,t);!1!==i&&(p=this.getCurrentCues(e.data,i),this.renderCues(!0))}},this.getCurrentCues=function(e,t){return Object(o.h)(e,(function(e){return t>=e.startTime&&(!e.endTime||t<=e.endTime)}))},this.getAlignmentPosition=function(e,t){var i=e.source,n=t.metadata,a=t.currentTime;return i&&n&&Object(o.r)(n[i])&&(a=n[i]),a},this.clear=function(){Object(u.g)(h)},this.setup=function(e,i){f=document.createElement("div"),g=document.createElement("span"),f.className="jw-captions-window jw-reset",g.className="jw-captions-text jw-reset",t=Object(o.g)({},d,i),j=d.fontScale;var n=function(){if(!v.get("renderCaptionsNatively")){y(t.fontSize);var i=t.windowColor,n=t.windowOpacity,o=t.edgeStyle;b={};var r={};!function(e,t){var i=t.color,n=t.fontOpacity;(i||n!==d.fontOpacity)&&(e.color=Object(c.c)(i||"#ffffff",n));if(t.back){var o=t.backgroundColor,a=t.backgroundOpacity;o===d.backgroundColor&&a===d.backgroundOpacity||(e.backgroundColor=Object(c.c)(o,a))}else e.background="transparent";t.fontFamily&&(e.fontFamily=t.fontFamily);t.fontStyle&&(e.fontStyle=t.fontStyle);t.fontWeight&&(e.fontWeight=t.fontWeight);t.textDecoration&&(e.textDecoration=t.textDecoration)}(r,t),(i||n!==d.windowOpacity)&&(b.backgroundColor=Object(c.c)(i||"#000000",n)),x(o,r,t.fontOpacity),t.back||null!==o||x("uniform",r),Object(c.d)(f,b),Object(c.d)(g,r),function(e,t){k(),function(e,t){a.Browser.safari&&Object(c.b)("#"+e+" .jw-video::-webkit-media-text-track-display-backdrop",{backgroundColor:t.backgroundColor},e,!0);Object(c.b)("#"+e+" .jw-video::-webkit-media-text-track-display",b,e,!0),Object(c.b)("#"+e+" .jw-video::cue",t,e,!0)}(e,t),function(e,t){Object(c.b)("#"+e+" .jw-text-track-display",b,e),Object(c.b)("#"+e+" .jw-text-track-cue",t,e)}(e,t)}(e,r)}};n(),f.appendChild(g),h.appendChild(f),v.change("captionsTrack",(function(e,t){this.populate(t)}),this),v.set("captions",t),v.on("change:captions",(function(e,i){t=i,n()}))},this.element=function(){return h},this.destroy=function(){v.off(null,null,this),this.off()};var T=function(e){w=e,m.selectCues(s,w)};v.on("change:playlistItem",(function(){w=null,p=[]}),this),v.on(l.Q,(function(e){p=[],T(e)}),this),v.on(l.S,T,this),v.on("subtitlesTrackData",(function(){this.selectCues(s,w)}),this),v.on("change:captionsList",(function e(t,o){var a=this;1!==o.length&&(t.get("renderCaptionsNatively")||n||(i.e(8).then(function(e){n=i(68).default}.bind(null,i)).catch(Object(r.c)(301121)).catch((function(e){a.trigger(l.tb,e)})),t.off("change:captionsList",e,this)))}),this)};Object(o.g)(p.prototype,s.a),t.b=p},function(e,t,i){"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var i=function(e,t){var i=e[1]||"",n=e[3];if(!n)return i;if(t&&"function"==typeof btoa){var o=(r=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */"),a=n.sources.map((function(e){return"/*# sourceURL="+n.sourceRoot+e+" */"}));return[i].concat(a).concat([o]).join("\n")}var r;return[i].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+i+"}":i})).join("")},t.i=function(e,i){"string"==typeof e&&(e=[[null,e,""]]);for(var n={},o=0;o'},function(e,t,i){"use strict";function n(e,t){var i=e.kind||"cc";return e.default||e.defaulttrack?"default":e._id||e.file||i+t}function o(e,t){var i=e.label||e.name||e.language;return i||(i="Unknown CC",(t+=1)>1&&(i+=" ["+t+"]")),{label:i,unknownCount:t}}i.d(t,"a",(function(){return n})),i.d(t,"b",(function(){return o}))},function(e,t,i){"use strict";function n(e){return new Promise((function(t,i){if(e.paused)return i(o("NotAllowedError",0,"play() failed."));var n=function(){e.removeEventListener("play",a),e.removeEventListener("playing",r),e.removeEventListener("pause",r),e.removeEventListener("abort",r),e.removeEventListener("error",r)},a=function(){e.addEventListener("playing",r),e.addEventListener("abort",r),e.addEventListener("error",r),e.addEventListener("pause",r)},r=function(e){if(n(),"playing"===e.type)t();else{var a='The play() request was interrupted by a "'.concat(e.type,'" event.');"error"===e.type?i(o("NotSupportedError",9,a)):i(o("AbortError",20,a))}};e.addEventListener("play",a)}))}function o(e,t,i){var n=new Error(i);return n.name=e,n.code=t,n}i.d(t,"a",(function(){return n}))},function(e,t,i){"use strict";function n(e,t){return e!==1/0&&Math.abs(e)>=Math.max(a(t),0)}function o(e,t){var i="VOD";return e===1/0?i="LIVE":e<0&&(i=n(e,a(t))?"DVR":"LIVE"),i}function a(e){return void 0===e?120:Math.max(e,0)}i.d(t,"a",(function(){return n})),i.d(t,"b",(function(){return o}))},function(e,t,i){"use strict";var n=i(67),o=i(16),a=i(22),r=i(4),s=i(57),l=i(2),c=i(1);function u(e){throw new c.n(null,e)}function d(e,t,n){e.xhr=Object(a.a)(e.file,(function(a){!function(e,t,n,a){var d,p,h=e.responseXML?e.responseXML.firstChild:null;if(h)for("xml"===Object(r.b)(h)&&(h=h.nextSibling);h.nodeType===h.COMMENT_NODE;)h=h.nextSibling;try{if(h&&"tt"===Object(r.b)(h))d=function(e){e||u(306007);var t=[],i=e.getElementsByTagName("p"),n=30,o=e.getElementsByTagName("tt");if(o&&o[0]){var a=parseFloat(o[0].getAttribute("ttp:frameRate"));isNaN(a)||(n=a)}i||u(306005),i.length||(i=e.getElementsByTagName("tt:p")).length||(i=e.getElementsByTagName("tts:p"));for(var r=0;r\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(h){var f=s.getAttribute("begin"),g=s.getAttribute("dur"),j=s.getAttribute("end"),b={begin:Object(l.g)(f,n),text:h};j?b.end=Object(l.g)(j,n):g&&(b.end=b.begin+Object(l.g)(g,n)),t.push(b)}}return t.length||u(306005),t}(e.responseXML),p=w(d),delete t.xhr,n(p);else{var f=e.responseText;f.indexOf("WEBVTT")>=0?i.e(10).then(function(e){return i(97).default}.bind(null,i)).catch(Object(o.c)(301131)).then((function(e){var i=new e(window);p=[],i.oncue=function(e){p.push(e)},i.onflush=function(){delete t.xhr,n(p)},i.parse(f)})).catch((function(e){delete t.xhr,a(Object(c.v)(null,c.b,e))})):(d=Object(s.a)(f),p=w(d),delete t.xhr,n(p))}}catch(e){delete t.xhr,a(Object(c.v)(null,c.b,e))}}(a,e,t,n)}),(function(e,t,i,o){n(Object(c.u)(o,c.b))}))}function p(e){e&&e.forEach((function(e){var t=e.xhr;t&&(t.onload=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()),delete e.xhr}))}function w(e){return e.map((function(e){return new n.a(e.begin,e.end,e.text)}))}i.d(t,"c",(function(){return d})),i.d(t,"a",(function(){return p})),i.d(t,"b",(function(){return w}))},function(e,t,i){"use strict";var n=window.VTTCue;function o(e){if("string"!=typeof e)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[e.toLowerCase()]&&e.toLowerCase()}if(!n){(n=function(e,t,i){var n=this;n.hasBeenReset=!1;var a="",r=!1,s=e,l=t,c=i,u=null,d="",p=!0,w="auto",h="start",f="auto",g=100,j="middle";Object.defineProperty(n,"id",{enumerable:!0,get:function(){return a},set:function(e){a=""+e}}),Object.defineProperty(n,"pauseOnExit",{enumerable:!0,get:function(){return r},set:function(e){r=!!e}}),Object.defineProperty(n,"startTime",{enumerable:!0,get:function(){return s},set:function(e){if("number"!=typeof e)throw new TypeError("Start time must be set to a number.");s=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"endTime",{enumerable:!0,get:function(){return l},set:function(e){if("number"!=typeof e)throw new TypeError("End time must be set to a number.");l=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"text",{enumerable:!0,get:function(){return c},set:function(e){c=""+e,this.hasBeenReset=!0}}),Object.defineProperty(n,"region",{enumerable:!0,get:function(){return u},set:function(e){u=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"vertical",{enumerable:!0,get:function(){return d},set:function(e){var t=function(e){return"string"==typeof e&&(!!{"":!0,lr:!0,rl:!0}[e.toLowerCase()]&&e.toLowerCase())}(e);if(!1===t)throw new SyntaxError("An invalid or illegal string was specified.");d=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"snapToLines",{enumerable:!0,get:function(){return p},set:function(e){p=!!e,this.hasBeenReset=!0}}),Object.defineProperty(n,"line",{enumerable:!0,get:function(){return w},set:function(e){if("number"!=typeof e&&"auto"!==e)throw new SyntaxError("An invalid number or illegal string was specified.");w=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"lineAlign",{enumerable:!0,get:function(){return h},set:function(e){var t=o(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");h=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"position",{enumerable:!0,get:function(){return f},set:function(e){if(e<0||e>100)throw new Error("Position must be between 0 and 100.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"size",{enumerable:!0,get:function(){return g},set:function(e){if(e<0||e>100)throw new Error("Size must be between 0 and 100.");g=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"align",{enumerable:!0,get:function(){return j},set:function(e){var t=o(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");j=t,this.hasBeenReset=!0}}),n.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}t.a=n},,function(e,t,i){var n=i(70);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(e.exports=n.locals)},function(e,t,i){(e.exports=i(60)(!1)).push([e.i,'.jw-reset{text-align:left;direction:ltr}.jw-reset-text,.jw-reset{color:inherit;background-color:transparent;padding:0;margin:0;float:none;font-family:Arial,Helvetica,sans-serif;font-size:1em;line-height:1em;list-style:none;text-transform:none;vertical-align:baseline;border:0;font-variant:inherit;font-stretch:inherit;-webkit-tap-highlight-color:rgba(255,255,255,0)}body .jw-error,body .jwplayer.jw-state-error{height:100%;width:100%}.jw-title{position:absolute;top:0}.jw-background-color{background:rgba(0,0,0,0.4)}.jw-text{color:rgba(255,255,255,0.8)}.jw-knob{color:rgba(255,255,255,0.8);background-color:#fff}.jw-button-color{color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):focus,:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):hover{color:#fff}.jw-toggle{color:#fff}.jw-toggle.jw-off{color:rgba(255,255,255,0.8)}.jw-toggle.jw-off:focus{color:#fff}.jw-toggle:focus{outline:none}:not(.jw-flag-touch) .jw-toggle.jw-off:hover{color:#fff}.jw-rail{background:rgba(255,255,255,0.3)}.jw-buffer{background:rgba(255,255,255,0.3)}.jw-progress{background:#f2f2f2}.jw-time-tip,.jw-volume-tip{border:0}.jw-slider-volume.jw-volume-tip.jw-background-color.jw-slider-vertical{background:none}.jw-skip{padding:.5em;outline:none}.jw-skip .jw-skiptext,.jw-skip .jw-skip-icon{color:rgba(255,255,255,0.8)}.jw-skip.jw-skippable:hover .jw-skip-icon,.jw-skip.jw-skippable:focus .jw-skip-icon{color:#fff}.jw-icon-cast google-cast-launcher{--connected-color:#fff;--disconnected-color:rgba(255,255,255,0.8)}.jw-icon-cast google-cast-launcher:focus{outline:none}.jw-icon-cast google-cast-launcher.jw-off{--connected-color:rgba(255,255,255,0.8)}.jw-icon-cast:focus google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-icon-cast:hover google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-nextup-container{bottom:2.5em;padding:5px .5em}.jw-nextup{border-radius:0}.jw-color-active{color:#fff;stroke:#fff;border-color:#fff}:not(.jw-flag-touch) .jw-color-active-hover:hover,:not(.jw-flag-touch) .jw-color-active-hover:focus{color:#fff;stroke:#fff;border-color:#fff}.jw-color-inactive{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-color-inactive-hover:hover{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}.jw-option{color:rgba(255,255,255,0.8)}.jw-option.jw-active-option{color:#fff;background-color:rgba(255,255,255,0.1)}:not(.jw-flag-touch) .jw-option:hover{color:#fff}.jwplayer{width:100%;font-size:16px;position:relative;display:block;min-height:0;overflow:hidden;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:none}.jwplayer *{box-sizing:inherit}.jwplayer.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jwplayer.jw-flag-aspect-mode{height:auto !important}.jwplayer.jw-flag-aspect-mode .jw-aspect{display:block}.jwplayer .jw-aspect{display:none}.jwplayer .jw-swf{outline:none}.jw-media,.jw-preview{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}.jw-media{overflow:hidden;cursor:pointer}.jw-plugin{position:absolute;bottom:66px}.jw-breakpoint-7 .jw-plugin{bottom:132px}.jw-plugin .jw-banner{max-width:100%;opacity:0;cursor:pointer;position:absolute;margin:auto auto 0;left:0;right:0;bottom:0;display:block}.jw-preview,.jw-captions,.jw-title{pointer-events:none}.jw-media,.jw-logo{pointer-events:all}.jw-wrapper{background-color:#000;position:absolute;top:0;left:0;right:0;bottom:0}.jw-hidden-accessibility{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.jw-contract-trigger::before{content:"";overflow:hidden;width:200%;height:200%;display:block;position:absolute;top:0;left:0}.jwplayer .jw-media video{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;margin:auto;background:transparent}.jwplayer .jw-media video::-webkit-media-controls-start-playback-button{display:none}.jwplayer.jw-stretch-uniform .jw-media video{object-fit:contain}.jwplayer.jw-stretch-none .jw-media video{object-fit:none}.jwplayer.jw-stretch-fill .jw-media video{object-fit:cover}.jwplayer.jw-stretch-exactfit .jw-media video{object-fit:fill}.jw-preview{position:absolute;display:none;opacity:1;visibility:visible;width:100%;height:100%;background:#000 no-repeat 50% 50%}.jwplayer .jw-preview,.jw-error .jw-preview{background-size:contain}.jw-stretch-none .jw-preview{background-size:auto auto}.jw-stretch-fill .jw-preview{background-size:cover}.jw-stretch-exactfit .jw-preview{background-size:100% 100%}.jw-title{display:none;padding-top:20px;width:100%;z-index:1}.jw-title-primary,.jw-title-secondary{color:#fff;padding-left:20px;padding-right:20px;padding-bottom:.5em;overflow:hidden;text-overflow:ellipsis;direction:unset;white-space:nowrap;width:100%}.jw-title-primary{font-size:1.625em}.jw-breakpoint-2 .jw-title-primary,.jw-breakpoint-3 .jw-title-primary{font-size:1.5em}.jw-flag-small-player .jw-title-primary{font-size:1.25em}.jw-flag-small-player .jw-title-secondary,.jw-title-secondary:empty{display:none}.jw-captions{position:absolute;width:100%;height:100%;text-align:center;display:none;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-decoration:none;pointer-events:none;overflow:hidden;top:0}.jw-captions.jw-captions-enabled{display:block}.jw-captions-window{display:none;padding:.25em;border-radius:.25em}.jw-captions-window.jw-captions-window-active{display:inline-block}.jw-captions-text{display:inline-block;color:#fff;background-color:#000;word-wrap:normal;word-break:normal;white-space:pre-line;font-style:normal;font-weight:normal;text-align:center;text-decoration:none}.jw-text-track-display{font-size:inherit;line-height:1.5}.jw-text-track-cue{background-color:rgba(0,0,0,0.5);color:#fff;padding:.1em .3em}.jwplayer video::-webkit-media-controls{display:none;justify-content:flex-start}.jwplayer video::-webkit-media-text-track-display{min-width:-webkit-min-content}.jwplayer video::cue{background-color:rgba(0,0,0,0.5)}.jwplayer video::-webkit-media-controls-panel-container{display:none}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing) .jw-captions,.jwplayer.jw-flag-media-audio.jw-state-playing .jw-captions,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden) .jw-captions{max-height:calc(100% - 60px)}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-flag-media-audio.jw-state-playing:not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container{max-height:calc(100% - 60px)}.jw-logo{position:absolute;margin:20px;cursor:pointer;pointer-events:all;background-repeat:no-repeat;background-size:contain;top:auto;right:auto;left:auto;bottom:auto;outline:none}.jw-logo.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-flag-audio-player .jw-logo{display:none}.jw-logo-top-right{top:0;right:0}.jw-logo-top-left{top:0;left:0}.jw-logo-bottom-left{left:0}.jw-logo-bottom-right{right:0}.jw-logo-bottom-left,.jw-logo-bottom-right{bottom:44px;transition:bottom 150ms cubic-bezier(0, .25, .25, 1)}.jw-state-idle .jw-logo{z-index:1}.jw-state-setup .jw-wrapper{background-color:inherit}.jw-state-setup .jw-logo,.jw-state-setup .jw-controls,.jw-state-setup .jw-controls-backdrop{visibility:hidden}span.jw-break{display:block}body .jw-error,body .jwplayer.jw-state-error{background-color:#333;color:#fff;font-size:16px;display:table;opacity:1;position:relative}body .jw-error .jw-display,body .jwplayer.jw-state-error .jw-display{display:none}body .jw-error .jw-media,body .jwplayer.jw-state-error .jw-media{cursor:default}body .jw-error .jw-preview,body .jwplayer.jw-state-error .jw-preview{background-color:#333}body .jw-error .jw-error-msg,body .jwplayer.jw-state-error .jw-error-msg{background-color:#000;border-radius:2px;display:flex;flex-direction:row;align-items:stretch;padding:20px}body .jw-error .jw-error-msg .jw-icon,body .jwplayer.jw-state-error .jw-error-msg .jw-icon{height:30px;width:30px;margin-right:20px;flex:0 0 auto;align-self:center}body .jw-error .jw-error-msg .jw-icon:empty,body .jwplayer.jw-state-error .jw-error-msg .jw-icon:empty{display:none}body .jw-error .jw-error-msg .jw-info-container,body .jwplayer.jw-state-error .jw-error-msg .jw-info-container{margin:0;padding:0}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg{flex-direction:column}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text{text-align:center}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon{flex:.5 0 auto;margin-right:0;margin-bottom:20px}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break{display:inline}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break:before{content:" "}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg{height:100%;width:100%;top:0;position:absolute;left:0;background:#000;-webkit-transform:none;transform:none;padding:4px 16px;z-index:1}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg.jw-info-overlay{max-width:none;max-height:none}body .jwplayer.jw-state-error .jw-title,.jw-state-idle .jw-title,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-title{display:block}body .jwplayer.jw-state-error .jw-preview,.jw-state-idle .jw-preview,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-preview{display:block}.jw-state-idle .jw-captions,.jwplayer.jw-state-complete .jw-captions,body .jwplayer.jw-state-error .jw-captions{display:none}.jw-state-idle video::-webkit-media-text-track-container,.jwplayer.jw-state-complete video::-webkit-media-text-track-container,body .jwplayer.jw-state-error video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-fullscreen{width:100% !important;height:100% !important;top:0;right:0;bottom:0;left:0;z-index:1000;margin:0;position:fixed}body .jwplayer.jw-flag-flash-blocked .jw-title{display:block}.jwplayer.jw-flag-controls-hidden .jw-media{cursor:default}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:45px}.jw-flag-floating{background-size:cover;background-color:#000}.jw-flag-floating .jw-wrapper{position:fixed;z-index:2147483647;-webkit-animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;top:auto;bottom:1rem;left:auto;right:1rem;max-width:400px;max-height:400px;margin:0 auto}@media screen and (max-width:480px){.jw-flag-floating .jw-wrapper{width:100%;left:0;right:0}}.jw-flag-floating .jw-wrapper .jw-media{touch-action:none}@media screen and (max-device-width:480px) and (orientation:portrait){.jw-flag-touch.jw-flag-floating .jw-wrapper{-webkit-animation:none;animation:none;top:62px;bottom:auto;left:0;right:0;max-width:none;max-height:none}}.jw-flag-floating .jw-float-icon{pointer-events:all;cursor:pointer;display:none}.jw-flag-floating .jw-float-icon .jw-svg-icon{-webkit-filter:drop-shadow(0 0 1px #000);filter:drop-shadow(0 0 1px #000)}.jw-flag-floating.jw-floating-dismissible .jw-dismiss-icon{display:none}.jw-flag-floating.jw-floating-dismissible.jw-flag-ads .jw-float-icon{display:flex}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-logo,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-logo{display:none}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-float-icon,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-float-icon{display:flex}.jw-float-icon{display:none;position:absolute;top:3px;right:5px;align-items:center;justify-content:center}@-webkit-keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.jw-flag-top{margin-top:2em;overflow:visible}.jw-top{height:2em;line-height:2;pointer-events:none;text-align:center;opacity:.8;position:absolute;top:-2em;width:100%}.jw-top .jw-icon{cursor:pointer;pointer-events:all;height:auto;width:auto}.jw-top .jw-text{color:#555}',""])},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t){e.exports=''},function(e,t,i){var n=i(96);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(e.exports=n.locals)},function(e,t,i){(e.exports=i(60)(!1)).push([e.i,'.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-flag-small-player .jw-settings-menu,.jw-settings-submenu{height:100%;width:100%}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;right:0}.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-settings-item-active::before{top:0;position:absolute;left:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;bottom:0;left:0}.jw-nextup-close{position:absolute;top:0;right:0}.jw-overlays,.jw-controls,.jw-flag-small-player .jw-settings-menu{position:absolute;bottom:0;right:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-time-tip::after,.jw-settings-menu .jw-icon.jw-button-color::after,.jw-text-live::before,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{content:"";display:block}.jw-svg-icon{height:24px;width:24px;fill:currentColor;pointer-events:none}.jw-icon{height:44px;width:44px;background-color:transparent;outline:none}.jw-icon.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-icon-airplay .jw-svg-icon-airplay-off{display:none}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-off{display:block}.jw-icon-airplay .jw-svg-icon-airplay-on{display:block}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-on{display:none}.jw-icon-cc .jw-svg-icon-cc-off{display:none}.jw-off.jw-icon-cc .jw-svg-icon-cc-off{display:block}.jw-icon-cc .jw-svg-icon-cc-on{display:block}.jw-off.jw-icon-cc .jw-svg-icon-cc-on{display:none}.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:none}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:block}.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:block}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:none}.jw-icon-volume .jw-svg-icon-volume-0{display:none}.jw-off.jw-icon-volume .jw-svg-icon-volume-0{display:block}.jw-icon-volume .jw-svg-icon-volume-100{display:none}.jw-full.jw-icon-volume .jw-svg-icon-volume-100{display:block}.jw-icon-volume .jw-svg-icon-volume-50{display:block}.jw-off.jw-icon-volume .jw-svg-icon-volume-50,.jw-full.jw-icon-volume .jw-svg-icon-volume-50{display:none}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon[aria-checked="true"]::after,.jw-settings-open .jw-icon-settings::after,.jw-icon-volume.jw-open::after{opacity:1}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-cc,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-settings,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-audio-tracks,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-hd,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-settings-sharing,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-fullscreen,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-airplay,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-cast{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-text-live{bottom:6px}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume::after{display:none}.jw-overlays,.jw-controls{pointer-events:none}.jw-controls-backdrop{display:block;background:linear-gradient(to bottom, transparent, rgba(0,0,0,0.4) 77%, rgba(0,0,0,0.4) 100%) 100% 100% / 100% 240px no-repeat transparent;transition:opacity 250ms cubic-bezier(0, .25, .25, 1),background-size 250ms cubic-bezier(0, .25, .25, 1);pointer-events:none}.jw-overlays{cursor:auto}.jw-controls{overflow:hidden}.jw-flag-small-player .jw-controls{text-align:center}.jw-text{height:1em;font-family:Arial,Helvetica,sans-serif;font-size:.75em;font-style:normal;font-weight:normal;color:#fff;text-align:center;font-variant:normal;font-stretch:normal}.jw-controlbar,.jw-skip,.jw-display-icon-container .jw-icon,.jw-nextup-container,.jw-autostart-mute,.jw-overlays .jw-plugin{pointer-events:all}.jwplayer .jw-display-icon-container,.jw-error .jw-display-icon-container{width:auto;height:auto;box-sizing:content-box}.jw-display{display:table;height:100%;padding:57px 0;position:relative;width:100%}.jw-flag-dragging .jw-display{display:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-display-container{display:table-cell;height:100%;text-align:center;vertical-align:middle}.jw-display-controls{display:inline-block}.jwplayer .jw-display-icon-container{float:left}.jw-display-icon-container{display:inline-block;padding:5.5px;margin:0 22px}.jw-display-icon-container .jw-icon{height:75px;width:75px;cursor:pointer;display:flex;justify-content:center;align-items:center}.jw-display-icon-container .jw-icon .jw-svg-icon{height:33px;width:33px;padding:0;position:relative}.jw-display-icon-container .jw-icon .jw-svg-icon-rewind{padding:.2em .05em}.jw-breakpoint--1 .jw-nextup-container{display:none}.jw-breakpoint-0 .jw-display-icon-next,.jw-breakpoint--1 .jw-display-icon-next,.jw-breakpoint-0 .jw-display-icon-rewind,.jw-breakpoint--1 .jw-display-icon-rewind{display:none}.jw-breakpoint-0 .jw-display .jw-icon,.jw-breakpoint--1 .jw-display .jw-icon,.jw-breakpoint-0 .jw-display .jw-svg-icon,.jw-breakpoint--1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-0 .jw-display .jw-icon:before,.jw-breakpoint--1 .jw-display .jw-icon:before,.jw-breakpoint-0 .jw-display .jw-svg-icon:before,.jw-breakpoint--1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon,.jw-breakpoint-1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-1 .jw-display .jw-icon:before,.jw-breakpoint-1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon.jw-icon-rewind:before{width:33px;height:33px}.jw-breakpoint-2 .jw-display .jw-icon,.jw-breakpoint-3 .jw-display .jw-icon,.jw-breakpoint-2 .jw-display .jw-svg-icon,.jw-breakpoint-3 .jw-display .jw-svg-icon{width:77px;height:77px;line-height:77px}.jw-breakpoint-2 .jw-display .jw-icon:before,.jw-breakpoint-3 .jw-display .jw-icon:before,.jw-breakpoint-2 .jw-display .jw-svg-icon:before,.jw-breakpoint-3 .jw-display .jw-svg-icon:before{width:38.5px;height:38.5px}.jw-breakpoint-4 .jw-display .jw-icon,.jw-breakpoint-5 .jw-display .jw-icon,.jw-breakpoint-6 .jw-display .jw-icon,.jw-breakpoint-7 .jw-display .jw-icon,.jw-breakpoint-4 .jw-display .jw-svg-icon,.jw-breakpoint-5 .jw-display .jw-svg-icon,.jw-breakpoint-6 .jw-display .jw-svg-icon,.jw-breakpoint-7 .jw-display .jw-svg-icon{width:88px;height:88px;line-height:88px}.jw-breakpoint-4 .jw-display .jw-icon:before,.jw-breakpoint-5 .jw-display .jw-icon:before,.jw-breakpoint-6 .jw-display .jw-icon:before,.jw-breakpoint-7 .jw-display .jw-icon:before,.jw-breakpoint-4 .jw-display .jw-svg-icon:before,.jw-breakpoint-5 .jw-display .jw-svg-icon:before,.jw-breakpoint-6 .jw-display .jw-svg-icon:before,.jw-breakpoint-7 .jw-display .jw-svg-icon:before{width:44px;height:44px}.jw-controlbar{display:flex;flex-flow:row wrap;align-items:center;justify-content:center;position:absolute;left:0;bottom:0;width:100%;border:none;border-radius:0;background-size:auto;box-shadow:none;max-height:72px;transition:250ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s}.jw-breakpoint-7 .jw-controlbar{max-height:140px}.jw-breakpoint-7 .jw-controlbar .jw-button-container{padding:0 48px 20px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-tooltip{margin-bottom:-7px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-overlay{padding-bottom:40%}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text{font-size:1em}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text.jw-text-elapsed{justify-content:flex-end}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume{height:60px;width:60px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline .jw-svg-icon,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time{padding:0 60px;height:34px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time .jw-slider-container{height:10px}.jw-controlbar .jw-button-image{background:no-repeat 50% 50%;background-size:contain;max-height:24px}.jw-controlbar .jw-spacer{flex:1 1 auto;align-self:stretch}.jw-controlbar .jw-icon.jw-button-color:hover{color:#fff}.jw-button-container{display:flex;flex-flow:row nowrap;flex:1 1 auto;align-items:center;justify-content:center;width:100%;padding:0 12px}.jw-slider-horizontal{background-color:transparent}.jw-icon-inline{position:relative}.jw-icon-inline,.jw-icon-tooltip{height:44px;width:44px;align-items:center;display:flex;justify-content:center}.jw-icon-inline:not(.jw-text),.jw-icon-tooltip,.jw-slider-horizontal{cursor:pointer}.jw-text-elapsed,.jw-text-duration{justify-content:flex-start;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.jw-icon-tooltip{position:relative}.jw-knob:hover,.jw-icon-inline:hover,.jw-icon-tooltip:hover,.jw-icon-display:hover,.jw-option:before:hover{color:#fff}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{pointer-events:none}.jw-icon-cast{display:none;margin:0;padding:0}.jw-icon-cast google-cast-launcher{background-color:transparent;border:none;padding:0;width:24px;height:24px;cursor:pointer}.jw-icon-inline.jw-icon-volume{display:none}.jwplayer .jw-text-countdown{display:none}.jw-flag-small-player .jw-display{padding-top:0;padding-bottom:0}.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-rewind,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-next,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-playback{display:none}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop{opacity:0}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-countdown{display:flex}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-duration,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-duration{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-text-countdown,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-related-btn,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-slider-volume{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-controlbar{flex-direction:column-reverse}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-button-container{height:30px}.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-volume,.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-fullscreen{display:none}.jwplayer:not(.jw-breakpoint-0) .jw-text-duration:before,.jwplayer:not(.jw-breakpoint--1) .jw-text-duration:before{content:"/";padding-right:1ch;padding-left:1ch}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar{will-change:transform}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar .jw-text{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.jw-slider-container{display:flex;align-items:center;position:relative;touch-action:none}.jw-rail,.jw-buffer,.jw-progress{position:absolute;cursor:pointer}.jw-progress{background-color:#f2f2f2}.jw-rail{background-color:rgba(255,255,255,0.3)}.jw-buffer{background-color:rgba(255,255,255,0.3)}.jw-knob{height:13px;width:13px;background-color:#fff;border-radius:50%;box-shadow:0 0 10px rgba(0,0,0,0.4);opacity:1;pointer-events:none;position:absolute;-webkit-transform:translate(-50%, -50%) scale(0);transform:translate(-50%, -50%) scale(0);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform}.jw-flag-dragging .jw-slider-time .jw-knob,.jw-icon-volume:active .jw-slider-volume .jw-knob{box-shadow:0 0 26px rgba(0,0,0,0.2),0 0 10px rgba(0,0,0,0.4),0 0 0 6px rgba(255,255,255,0.2)}.jw-slider-horizontal,.jw-slider-vertical{display:flex}.jw-slider-horizontal .jw-slider-container{height:5px;width:100%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue,.jw-slider-horizontal .jw-knob{top:50%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue{-webkit-transform:translate(0, -50%);transform:translate(0, -50%)}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress{height:5px}.jw-slider-horizontal .jw-rail{width:100%}.jw-slider-vertical{align-items:center;flex-direction:column}.jw-slider-vertical .jw-slider-container{height:88px;width:5px}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress,.jw-slider-vertical .jw-knob{left:50%}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress{height:100%;width:5px;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out;bottom:0}.jw-slider-vertical .jw-knob{-webkit-transform:translate(-50%, 50%);transform:translate(-50%, 50%)}.jw-slider-time.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-slider-time,.jw-flag-audio-player .jw-slider-volume{height:17px;width:100%;align-items:center;background:transparent none;padding:0 12px}.jw-slider-time .jw-cue{background-color:rgba(33,33,33,0.8);cursor:pointer;position:absolute;width:6px}.jw-slider-time,.jw-horizontal-volume-container{z-index:1;outline:none}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail,.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer,.jw-slider-time .jw-progress,.jw-horizontal-volume-container .jw-progress,.jw-slider-time .jw-cue,.jw-horizontal-volume-container .jw-cue{-webkit-backface-visibility:hidden;backface-visibility:hidden;height:100%;-webkit-transform:translate(0, -50%) scale(1, .6);transform:translate(0, -50%) scale(1, .6);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out}.jw-slider-time:hover .jw-rail,.jw-horizontal-volume-container:hover .jw-rail,.jw-slider-time:focus .jw-rail,.jw-horizontal-volume-container:focus .jw-rail,.jw-flag-dragging .jw-slider-time .jw-rail,.jw-flag-dragging .jw-horizontal-volume-container .jw-rail,.jw-flag-touch .jw-slider-time .jw-rail,.jw-flag-touch .jw-horizontal-volume-container .jw-rail,.jw-slider-time:hover .jw-buffer,.jw-horizontal-volume-container:hover .jw-buffer,.jw-slider-time:focus .jw-buffer,.jw-horizontal-volume-container:focus .jw-buffer,.jw-flag-dragging .jw-slider-time .jw-buffer,.jw-flag-dragging .jw-horizontal-volume-container .jw-buffer,.jw-flag-touch .jw-slider-time .jw-buffer,.jw-flag-touch .jw-horizontal-volume-container .jw-buffer,.jw-slider-time:hover .jw-progress,.jw-horizontal-volume-container:hover .jw-progress,.jw-slider-time:focus .jw-progress,.jw-horizontal-volume-container:focus .jw-progress,.jw-flag-dragging .jw-slider-time .jw-progress,.jw-flag-dragging .jw-horizontal-volume-container .jw-progress,.jw-flag-touch .jw-slider-time .jw-progress,.jw-flag-touch .jw-horizontal-volume-container .jw-progress,.jw-slider-time:hover .jw-cue,.jw-horizontal-volume-container:hover .jw-cue,.jw-slider-time:focus .jw-cue,.jw-horizontal-volume-container:focus .jw-cue,.jw-flag-dragging .jw-slider-time .jw-cue,.jw-flag-dragging .jw-horizontal-volume-container .jw-cue,.jw-flag-touch .jw-slider-time .jw-cue,.jw-flag-touch .jw-horizontal-volume-container .jw-cue{-webkit-transform:translate(0, -50%) scale(1, 1);transform:translate(0, -50%) scale(1, 1)}.jw-slider-time:hover .jw-knob,.jw-horizontal-volume-container:hover .jw-knob,.jw-slider-time:focus .jw-knob,.jw-horizontal-volume-container:focus .jw-knob{-webkit-transform:translate(-50%, -50%) scale(1);transform:translate(-50%, -50%) scale(1)}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail{background-color:rgba(255,255,255,0.2)}.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer{background-color:rgba(255,255,255,0.4)}.jw-flag-touch .jw-slider-time::before,.jw-flag-touch .jw-horizontal-volume-container::before{height:44px;width:100%;content:"";position:absolute;display:block;bottom:calc(100% - 17px);left:0}.jw-slider-time.jw-tab-focus:focus .jw-rail,.jw-horizontal-volume-container.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time{height:17px;padding:0}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-slider-container{height:10px}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-knob{border-radius:0;border:1px solid rgba(0,0,0,0.75);height:12px;width:10px}.jw-modal{width:284px}.jw-breakpoint-7 .jw-modal,.jw-breakpoint-6 .jw-modal,.jw-breakpoint-5 .jw-modal{height:232px}.jw-breakpoint-4 .jw-modal,.jw-breakpoint-3 .jw-modal{height:192px}.jw-breakpoint-2 .jw-modal,.jw-flag-small-player .jw-modal{bottom:0;right:0;height:100%;width:100%;max-height:none;max-width:none;z-index:2}.jwplayer .jw-rightclick{display:none;position:absolute;white-space:nowrap}.jwplayer .jw-rightclick.jw-open{display:block}.jwplayer .jw-rightclick .jw-rightclick-list{border-radius:1px;list-style:none;margin:0;padding:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item{background-color:rgba(0,0,0,0.8);border-bottom:1px solid #444;margin:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo{color:#fff;display:inline-flex;padding:0 10px 0 0;vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo .jw-svg-icon{height:20px;width:20px}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-link{border:none;color:#fff;display:block;font-size:11px;line-height:1em;padding:15px 23px;text-align:start;text-decoration:none;width:100%}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:last-child{border-bottom:none}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:hover{cursor:pointer}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured{vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link{color:#fff}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link span{color:#fff}.jwplayer .jw-rightclick .jw-info-overlay-item,.jwplayer .jw-rightclick .jw-share-item,.jwplayer .jw-rightclick .jw-shortcuts-item{border:none;background-color:transparent;outline:none;cursor:pointer}.jw-icon-tooltip.jw-open .jw-overlay{opacity:1;pointer-events:auto;transition-delay:0s}.jw-icon-tooltip.jw-open .jw-overlay:focus{outline:none}.jw-icon-tooltip.jw-open .jw-overlay:focus.jw-tab-focus{outline:solid 2px #4d90fe}.jw-slider-time .jw-overlay:before{height:1em;top:auto}.jw-slider-time .jw-icon-tooltip.jw-open .jw-overlay{pointer-events:none}.jw-volume-tip{padding:13px 0 26px}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{height:auto;width:100%;box-shadow:0 0 10px rgba(0,0,0,0.4);color:#fff;display:block;margin:0 0 14px;pointer-events:none;position:relative;z-index:0}.jw-time-tip::after,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{top:100%;position:absolute;left:50%;height:14px;width:14px;border-radius:1px;background-color:currentColor;-webkit-transform-origin:75% 50%;transform-origin:75% 50%;-webkit-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg);z-index:-1}.jw-time-tip .jw-text,.jw-controlbar .jw-tooltip .jw-text,.jw-settings-menu .jw-tooltip .jw-text{background-color:#fff;border-radius:1px;color:#000;font-size:10px;height:auto;line-height:1;padding:7px 10px;display:inline-block;min-width:100%;vertical-align:middle}.jw-controlbar .jw-overlay{position:absolute;bottom:100%;left:50%;margin:0;min-height:44px;min-width:44px;opacity:0;pointer-events:none;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s, 150ms;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);width:100%;z-index:1}.jw-controlbar .jw-overlay .jw-contents{position:relative}.jw-controlbar .jw-option{position:relative;white-space:nowrap;cursor:pointer;list-style:none;height:1.5em;font-family:inherit;line-height:1.5em;padding:0 .5em;font-size:.8em;margin:0}.jw-controlbar .jw-option::before{padding-right:.125em}.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{position:absolute;bottom:100%;left:50%;opacity:0;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:100ms 0s cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility, -webkit-transform;transition-property:opacity, transform, visibility;transition-property:opacity, transform, visibility, -webkit-transform;visibility:hidden;white-space:nowrap;width:auto;z-index:1}.jw-controlbar .jw-tooltip.jw-open,.jw-settings-menu .jw-tooltip.jw-open{opacity:1;-webkit-transform:translate(-50%, -10px);transform:translate(-50%, -10px);transition-duration:150ms;transition-delay:500ms,0s,500ms;visibility:visible}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen{left:auto;right:0;-webkit-transform:translate(0, 0);transform:translate(0, 0)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen.jw-open,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen.jw-open{-webkit-transform:translate(0, -10px);transform:translate(0, -10px)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen::after,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen::after{left:auto;right:9px}.jw-tooltip-time{height:auto;width:0;bottom:100%;line-height:normal;padding:0;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jw-tooltip-time .jw-overlay{bottom:0;min-height:0;width:auto}.jw-tooltip{bottom:57px;display:none;position:absolute}.jw-tooltip .jw-text{height:100%;white-space:nowrap;text-overflow:ellipsis;direction:unset;max-width:246px;overflow:hidden}.jw-flag-audio-player .jw-tooltip{display:none}.jw-flag-small-player .jw-time-thumb{display:none}.jwplayer .jw-shortcuts-tooltip{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column;z-index:1}.jwplayer .jw-shortcuts-tooltip.jw-open{display:flex}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-close{flex:0 0 auto;margin:5px 5px 5px auto}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container{display:flex;flex:1 1 auto;flex-flow:column;font-size:12px;margin:0 20px 20px;overflow-y:auto;padding:5px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar{background-color:transparent;width:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-title{font-weight:bold}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list{display:flex;max-width:340px;margin:0 10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-tooltip-descriptions{width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row{display:flex;align-items:center;justify-content:space-between;margin:10px 0;width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-description{margin-right:10px;max-width:70%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-key{background:#fefefe;color:#333;overflow:hidden;padding:7px 10px;text-overflow:ellipsis;white-space:nowrap}.jw-skip{color:rgba(255,255,255,0.8);cursor:default;position:absolute;display:flex;right:.75em;bottom:56px;padding:.5em;border:1px solid #333;background-color:#000;align-items:center;height:2em}.jw-skip.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-skip.jw-skippable{cursor:pointer;padding:.25em .75em}.jw-skip.jw-skippable:hover{cursor:pointer;color:#fff}.jw-skip.jw-skippable .jw-skip-icon{display:inline;height:24px;width:24px;margin:0}.jw-breakpoint-7 .jw-skip{padding:1.35em 1em;bottom:130px}.jw-breakpoint-7 .jw-skip .jw-text{font-size:1em;font-weight:normal}.jw-breakpoint-7 .jw-skip .jw-icon-inline{height:30px;width:30px}.jw-breakpoint-7 .jw-skip .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-skip .jw-skip-icon{display:none;margin-left:-0.75em;padding:0 .5em;pointer-events:none}.jw-skip .jw-skip-icon .jw-svg-icon-next{display:block;padding:0}.jw-skip .jw-text,.jw-skip .jw-skip-icon{vertical-align:middle;font-size:.7em}.jw-skip .jw-text{font-weight:bold}.jw-cast{background-size:cover;display:none;height:100%;position:relative;width:100%}.jw-cast-container{background:linear-gradient(180deg, rgba(25,25,25,0.75), rgba(25,25,25,0.25), rgba(25,25,25,0));left:0;padding:20px 20px 80px;position:absolute;top:0;width:100%}.jw-cast-text{color:#fff;font-size:1.6em}.jw-breakpoint--1 .jw-cast-text,.jw-breakpoint-0 .jw-cast-text{font-size:1.15em}.jw-breakpoint-1 .jw-cast-text,.jw-breakpoint-2 .jw-cast-text,.jw-breakpoint-3 .jw-cast-text{font-size:1.3em}.jw-nextup-container{position:absolute;bottom:66px;left:0;background-color:transparent;cursor:pointer;margin:0 auto;padding:12px;pointer-events:none;right:0;text-align:right;visibility:hidden;width:100%}.jw-settings-open .jw-nextup-container,.jw-info-open .jw-nextup-container{display:none}.jw-breakpoint-7 .jw-nextup-container{padding:60px}.jw-flag-small-player .jw-nextup-container{padding:0 12px 0 0}.jw-flag-small-player .jw-nextup-container .jw-nextup-title,.jw-flag-small-player .jw-nextup-container .jw-nextup-duration,.jw-flag-small-player .jw-nextup-container .jw-nextup-close{display:none}.jw-flag-small-player .jw-nextup-container .jw-nextup-tooltip{height:30px}.jw-flag-small-player .jw-nextup-container .jw-nextup-header{font-size:12px}.jw-flag-small-player .jw-nextup-container .jw-nextup-body{justify-content:center;align-items:center;padding:.75em .3em}.jw-flag-small-player .jw-nextup-container .jw-nextup-thumbnail{width:50%}.jw-flag-small-player .jw-nextup-container .jw-nextup{max-width:65px}.jw-flag-small-player .jw-nextup-container .jw-nextup.jw-nextup-thumbnail-visible{max-width:120px}.jw-nextup{background:#333;border-radius:0;box-shadow:0 0 10px rgba(0,0,0,0.5);color:rgba(255,255,255,0.8);display:inline-block;max-width:280px;overflow:hidden;opacity:0;position:relative;width:64%;pointer-events:all;-webkit-transform:translate(0, -5px);transform:translate(0, -5px);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform;transition-delay:0s}.jw-nextup:hover .jw-nextup-tooltip{color:#fff}.jw-nextup.jw-nextup-thumbnail-visible{max-width:400px}.jw-nextup.jw-nextup-thumbnail-visible .jw-nextup-thumbnail{display:block}.jw-nextup-container-visible{visibility:visible}.jw-nextup-container-visible .jw-nextup{opacity:1;-webkit-transform:translate(0, 0);transform:translate(0, 0);transition-delay:0s, 0s, 150ms}.jw-nextup-tooltip{display:flex;height:80px}.jw-nextup-thumbnail{width:120px;background-position:center;background-size:cover;flex:0 0 auto;display:none}.jw-nextup-body{flex:1 1 auto;overflow:hidden;padding:.75em .875em;display:flex;flex-flow:column wrap;justify-content:space-between}.jw-nextup-header,.jw-nextup-title{font-size:14px;line-height:1.35}.jw-nextup-header{font-weight:bold}.jw-nextup-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.jw-nextup-duration{align-self:flex-end;text-align:right;font-size:12px}.jw-nextup-close{height:24px;width:24px;border:none;color:rgba(255,255,255,0.8);cursor:pointer;margin:6px;visibility:hidden}.jw-nextup-close:hover{color:#fff}.jw-nextup-sticky .jw-nextup-close{visibility:visible}.jw-autostart-mute{position:absolute;bottom:0;right:12px;height:44px;width:44px;background-color:rgba(33,33,33,0.4);padding:5px 4px 5px 6px;display:none}.jwplayer.jw-flag-autostart:not(.jw-flag-media-audio) .jw-nextup{display:none}.jw-settings-menu{position:absolute;bottom:57px;right:12px;align-items:flex-start;background-color:#333;display:none;flex-flow:column nowrap;max-width:284px;pointer-events:auto}.jw-settings-open .jw-settings-menu{display:flex}.jw-breakpoint-7 .jw-settings-menu{bottom:130px;right:60px;max-height:none;max-width:none;height:35%;width:25%}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline{height:60px;width:60px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-tooltip .jw-text{font-size:1em}.jw-breakpoint-7 .jw-settings-menu .jw-settings-back{min-width:60px}.jw-breakpoint-6 .jw-settings-menu,.jw-breakpoint-5 .jw-settings-menu{height:232px;width:284px;max-height:232px}.jw-breakpoint-4 .jw-settings-menu,.jw-breakpoint-3 .jw-settings-menu{height:192px;width:284px;max-height:192px}.jw-breakpoint-2 .jw-settings-menu{height:179px;width:284px;max-height:179px}.jw-flag-small-player .jw-settings-menu{max-width:none}.jw-settings-menu .jw-icon.jw-button-color::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon.jw-button-color[aria-checked="true"]::after{opacity:1}.jw-settings-menu .jw-settings-reset{text-decoration:underline}.jw-settings-topbar{align-items:center;background-color:rgba(0,0,0,0.4);display:flex;flex:0 0 auto;padding:3px 5px 0;width:100%}.jw-settings-topbar.jw-nested-menu-open{padding:0}.jw-settings-topbar.jw-nested-menu-open .jw-icon:not(.jw-settings-close):not(.jw-settings-back){display:none}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-close{width:20px}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-arrow-left{height:12px}.jw-settings-topbar.jw-nested-menu-open .jw-settings-topbar-text{display:block;outline:none}.jw-settings-topbar .jw-settings-back{min-width:44px}.jw-settings-topbar .jw-settings-topbar-buttons{display:inherit;width:100%;height:100%}.jw-settings-topbar .jw-settings-topbar-text{display:none;color:#fff;font-size:13px;width:100%}.jw-settings-topbar .jw-settings-close{margin-left:auto}.jw-settings-submenu{display:none;flex:1 1 auto;overflow-y:auto;padding:8px 20px 0 5px}.jw-settings-submenu::-webkit-scrollbar{background-color:transparent;width:6px}.jw-settings-submenu::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-settings-submenu.jw-settings-submenu-active{display:block}.jw-settings-submenu .jw-submenu-topbar{box-shadow:0 2px 9px 0 #1d1d1d;background-color:#2f2d2d;margin:-8px -20px 0 -5px}.jw-settings-submenu .jw-submenu-topbar .jw-settings-content-item{cursor:pointer;text-align:right;padding-right:15px;text-decoration:underline}.jw-settings-submenu .jw-settings-value-wrapper{float:right;display:flex;align-items:center}.jw-settings-submenu .jw-settings-value-wrapper .jw-settings-content-item-arrow{display:flex}.jw-settings-submenu .jw-settings-value-wrapper .jw-svg-icon-arrow-right{width:8px;margin-left:5px;height:12px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item{font-size:1em;padding:11px 15px 11px 30px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-settings-item-active::before{justify-content:flex-end}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-auto-label{font-size:.85em;padding-left:10px}.jw-flag-touch .jw-settings-submenu{overflow-y:scroll;-webkit-overflow-scrolling:touch}.jw-auto-label{font-size:10px;font-weight:initial;opacity:.75;padding-left:5px}.jw-settings-content-item{position:relative;color:rgba(255,255,255,0.8);cursor:pointer;font-size:12px;line-height:1;padding:7px 0 7px 15px;width:100%;text-align:left;outline:none}.jw-settings-content-item:hover{color:#fff}.jw-settings-content-item:focus{font-weight:bold}.jw-flag-small-player .jw-settings-content-item{line-height:1.75}.jw-settings-content-item.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-settings-item-active{font-weight:bold;position:relative}.jw-settings-item-active::before{height:100%;width:1em;align-items:center;content:"\\2022";display:inline-flex;justify-content:center}.jw-breakpoint-2 .jw-settings-open .jw-display-container,.jw-flag-small-player .jw-settings-open .jw-display-container,.jw-flag-touch .jw-settings-open .jw-display-container{display:none}.jw-breakpoint-2 .jw-settings-open.jw-controls,.jw-flag-small-player .jw-settings-open.jw-controls,.jw-flag-touch .jw-settings-open.jw-controls{z-index:1}.jw-flag-small-player .jw-settings-open .jw-controlbar{display:none}.jw-settings-open .jw-icon-settings::after{opacity:1}.jw-settings-open .jw-tooltip-settings{display:none}.jw-sharing-link{cursor:pointer}.jw-shortcuts-container .jw-switch{position:relative;display:inline-block;transition:ease-out .15s;transition-property:opacity, background;border-radius:18px;width:80px;height:20px;padding:10px;background:rgba(80,80,80,0.8);cursor:pointer;font-size:inherit;vertical-align:middle}.jw-shortcuts-container .jw-switch.jw-tab-focus{outline:solid 2px #4d90fe}.jw-shortcuts-container .jw-switch .jw-switch-knob{position:absolute;top:2px;left:1px;transition:ease-out .15s;box-shadow:0 0 10px rgba(0,0,0,0.4);border-radius:13px;width:15px;height:15px;background:#fefefe}.jw-shortcuts-container .jw-switch:before,.jw-shortcuts-container .jw-switch:after{position:absolute;top:3px;transition:inherit;color:#fefefe}.jw-shortcuts-container .jw-switch:before{content:attr(data-jw-switch-disabled);right:8px}.jw-shortcuts-container .jw-switch:after{content:attr(data-jw-switch-enabled);left:8px;opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]{background:#475470}.jw-shortcuts-container .jw-switch[aria-checked="true"]:before{opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]:after{opacity:1}.jw-shortcuts-container .jw-switch[aria-checked="true"] .jw-switch-knob{left:60px}.jw-idle-icon-text{display:none;line-height:1;position:absolute;text-align:center;text-indent:.35em;top:100%;white-space:nowrap;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.jw-idle-label{border-radius:50%;color:#fff;-webkit-filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));font:normal 16px/1 Arial,Helvetica,sans-serif;position:relative;transition:background-color 150ms cubic-bezier(0, .25, .25, 1);transition-property:background-color,-webkit-filter;transition-property:background-color,filter;transition-property:background-color,filter,-webkit-filter;-webkit-font-smoothing:antialiased}.jw-state-idle .jw-icon-display.jw-idle-label .jw-idle-icon-text{display:block}.jw-state-idle .jw-icon-display.jw-idle-label .jw-svg-icon-play{-webkit-transform:scale(.7, .7);transform:scale(.7, .7)}.jw-breakpoint-0.jw-state-idle .jw-icon-display.jw-idle-label,.jw-breakpoint--1.jw-state-idle .jw-icon-display.jw-idle-label{font-size:12px}.jw-info-overlay{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column}.jw-info-overlay .jw-info-close{flex:0 0 auto;margin:5px 5px 5px auto}.jw-info-open .jw-info-overlay{display:flex}.jw-info-container{display:flex;flex:1 1 auto;flex-flow:column;margin:0 20px 20px;overflow-y:auto;padding:5px}.jw-info-container [class*="jw-info"]:not(:first-of-type){color:rgba(255,255,255,0.8);padding-top:10px;font-size:12px}.jw-info-container .jw-info-description{margin-bottom:30px;text-align:start}.jw-info-container .jw-info-description:empty{display:none}.jw-info-container .jw-info-duration{text-align:start}.jw-info-container .jw-info-title{text-align:start;font-size:12px;font-weight:bold}.jw-info-container::-webkit-scrollbar{background-color:transparent;width:6px}.jw-info-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-info-clientid{align-self:flex-end;font-size:12px;color:rgba(255,255,255,0.8);margin:0 20px 20px 44px;text-align:right}.jw-flag-touch .jw-info-open .jw-display-container{display:none}@supports ((-webkit-filter: drop-shadow(0 0 3px #000)) or (filter: drop-shadow(0 0 3px #000))){.jwplayer.jw-ab-drop-shadow .jw-controls .jw-svg-icon,.jwplayer.jw-ab-drop-shadow .jw-controls .jw-icon.jw-text,.jwplayer.jw-ab-drop-shadow .jw-slider-container .jw-rail,.jwplayer.jw-ab-drop-shadow .jw-title{text-shadow:none;box-shadow:none;-webkit-filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3));filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3))}.jwplayer.jw-ab-drop-shadow .jw-button-color{opacity:.8;transition-property:color, opacity}.jwplayer.jw-ab-drop-shadow .jw-button-color:not(:hover){color:#fff;opacity:.8}.jwplayer.jw-ab-drop-shadow .jw-button-color:hover{opacity:1}.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 0.00787) 10.79%, hsla(0, 0%, 0%, 0.02963) 21.99%, hsla(0, 0%, 0%, 0.0625) 33.34%, hsla(0, 0%, 0%, 0.1037) 44.59%, hsla(0, 0%, 0%, 0.15046) 55.48%, hsla(0, 0%, 0%, 0.2) 65.75%, hsla(0, 0%, 0%, 0.24954) 75.14%, hsla(0, 0%, 0%, 0.2963) 83.41%, hsla(0, 0%, 0%, 0.3375) 90.28%, hsla(0, 0%, 0%, 0.37037) 95.51%, hsla(0, 0%, 0%, 0.39213) 98.83%, hsla(0, 0%, 0%, 0.4));mix-blend-mode:multiply;transition-property:opacity}.jw-state-idle.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0.2), hsla(0, 0%, 0%, 0.19606) 1.17%, hsla(0, 0%, 0%, 0.18519) 4.49%, hsla(0, 0%, 0%, 0.16875) 9.72%, hsla(0, 0%, 0%, 0.14815) 16.59%, hsla(0, 0%, 0%, 0.12477) 24.86%, hsla(0, 0%, 0%, 0.1) 34.25%, hsla(0, 0%, 0%, 0.07523) 44.52%, hsla(0, 0%, 0%, 0.05185) 55.41%, hsla(0, 0%, 0%, 0.03125) 66.66%, hsla(0, 0%, 0%, 0.01481) 78.01%, hsla(0, 0%, 0%, 0.00394) 89.21%, hsla(0, 0%, 0%, 0));background-size:100% 7rem;background-position:50% 0}.jwplayer.jw-ab-drop-shadow.jw-state-idle .jw-controls{background-color:transparent}}.jw-video-thumbnail-container{position:relative;overflow:hidden}.jw-video-thumbnail-container:not(.jw-related-shelf-item-image){height:100%;width:100%}.jw-video-thumbnail-container.jw-video-thumbnail-generated{position:absolute;top:0;left:0}.jw-video-thumbnail-container:hover,.jw-related-item-content:hover .jw-video-thumbnail-container,.jw-related-shelf-item:hover .jw-video-thumbnail-container{cursor:pointer}.jw-video-thumbnail-container:hover .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-item-content:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-shelf-item:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail{position:absolute;top:50%;left:50%;bottom:unset;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:100%;height:auto;min-width:100%;min-height:100%;opacity:0;transition:opacity .3s ease;object-fit:cover;background:#000}.jw-related-item-next-up .jw-video-thumbnail-container .jw-video-thumbnail{height:100%;width:auto}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-visible:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-completed{opacity:0}.jw-video-thumbnail-container .jw-video-thumbnail~.jw-svg-icon-play{display:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-shelf-item-aspect{pointer-events:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-item-poster-content{pointer-events:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-state-idle .jw-controls{background:rgba(0,0,0,0.4)}.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay),.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay){display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon:focus{border:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon .jw-svg-icon-buffer{-webkit-animation:jw-spin 2s linear infinite;animation:jw-spin 2s linear infinite;display:block}@-webkit-keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jwplayer.jw-state-buffering .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-pause{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-pause{display:block}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-controls-backdrop{opacity:0}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-logo-bottom-left,.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio):not(.jw-flag-autostart) .jw-logo-bottom-right{bottom:0}.jwplayer .jw-icon-playback .jw-svg-icon-stop{display:none}.jwplayer.jw-state-paused .jw-svg-icon-pause,.jwplayer.jw-state-idle .jw-svg-icon-pause,.jwplayer.jw-state-error .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-svg-icon-pause{display:none}.jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-complete .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-play{display:none}.jwplayer:not(.jw-state-buffering) .jw-svg-icon-buffer{display:none}.jwplayer:not(.jw-state-complete) .jw-svg-icon-replay{display:none}.jwplayer:not(.jw-state-error) .jw-svg-icon-error{display:none}.jwplayer.jw-state-complete .jw-display .jw-icon-display .jw-svg-icon-replay{display:block}.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-state-complete .jw-controls{background:rgba(0,0,0,0.4);height:100%}.jw-state-idle .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-state-idle .jw-display-icon-rewind,.jwplayer.jw-state-buffering .jw-display-icon-rewind,.jwplayer.jw-state-complete .jw-display-icon-rewind,body .jw-error .jw-display-icon-rewind,body .jwplayer.jw-state-error .jw-display-icon-rewind,.jw-state-idle .jw-display-icon-next,.jwplayer.jw-state-buffering .jw-display-icon-next,.jwplayer.jw-state-complete .jw-display-icon-next,body .jw-error .jw-display-icon-next,body .jwplayer.jw-state-error .jw-display-icon-next{display:none}body .jw-error .jw-icon-display,body .jwplayer.jw-state-error .jw-icon-display{cursor:default}body .jw-error .jw-icon-display .jw-svg-icon-error,body .jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-error{display:block}body .jw-error .jw-icon-container{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-preview{display:none}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title{padding-top:4px}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-primary{width:auto;display:inline-block;padding-right:.5ch}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-secondary{width:auto;display:inline-block;padding-left:0}body .jwplayer.jw-state-error .jw-controlbar,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-controlbar{display:none}body .jwplayer.jw-state-error .jw-settings-menu,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-settings-menu{height:100%;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}body .jwplayer.jw-state-error .jw-display,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-display{padding:0}body .jwplayer.jw-state-error .jw-logo-bottom-left,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-left,body .jwplayer.jw-state-error .jw-logo-bottom-right,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-right{bottom:0}.jwplayer.jw-state-playing.jw-flag-user-inactive .jw-display{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-state-playing:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display,.jwplayer.jw-state-paused:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting):not(.jw-flag-play-rejected) .jw-display{display:none}.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-rewind,.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-next{display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-text,.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-flag-casting:not(.jw-flag-audio-player) .jw-cast{display:block}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-display-icon-container{display:none}.jwplayer.jw-flag-casting .jw-icon-hd,.jwplayer.jw-flag-casting .jw-captions,.jwplayer.jw-flag-casting .jw-icon-fullscreen,.jwplayer.jw-flag-casting .jw-icon-audio-tracks{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-volume{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-airplay{color:#fff}.jw-state-playing.jw-flag-casting:not(.jw-flag-audio-player) .jw-display,.jw-state-paused.jw-flag-casting:not(.jw-flag-audio-player) .jw-display{display:table}.jwplayer.jw-flag-cast-available .jw-icon-cast,.jwplayer.jw-flag-cast-available .jw-icon-airplay{display:flex}.jwplayer.jw-flag-cardboard-available .jw-icon-cardboard{display:flex}.jwplayer.jw-flag-live .jw-display-icon-rewind{visibility:hidden}.jwplayer.jw-flag-live .jw-controlbar .jw-text-elapsed,.jwplayer.jw-flag-live .jw-controlbar .jw-text-duration,.jwplayer.jw-flag-live .jw-controlbar .jw-text-countdown,.jwplayer.jw-flag-live .jw-controlbar .jw-slider-time{display:none}.jwplayer.jw-flag-live .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-live .jw-controlbar .jw-overlay:after{display:none}.jwplayer.jw-flag-live .jw-nextup-container{bottom:44px}.jwplayer.jw-flag-live .jw-text-elapsed,.jwplayer.jw-flag-live .jw-text-duration{display:none}.jwplayer.jw-flag-live .jw-text-live{cursor:default}.jwplayer.jw-flag-live .jw-text-live:hover{color:rgba(255,255,255,0.8)}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-stop,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-stop{display:block}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-text-live{height:24px;width:auto;align-items:center;border-radius:1px;color:rgba(255,255,255,0.8);display:flex;font-size:12px;font-weight:bold;margin-right:10px;padding:0 1ch;text-rendering:geometricPrecision;text-transform:uppercase;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:box-shadow,color}.jw-text-live::before{height:8px;width:8px;background-color:currentColor;border-radius:50%;margin-right:6px;opacity:1;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-text-live.jw-dvr-live{box-shadow:inset 0 0 0 2px currentColor}.jw-text-live.jw-dvr-live::before{opacity:.5}.jw-text-live.jw-dvr-live:hover{color:#fff}.jwplayer.jw-flag-controls-hidden .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-controls-hidden:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-controls-hidden .jw-plugin{bottom:.5em}.jwplayer.jw-flag-controls-hidden .jw-nextup-container{bottom:0}.jw-flag-controls-hidden .jw-controlbar,.jw-flag-controls-hidden .jw-display{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-controls-hidden .jw-controls-backdrop{opacity:0}.jw-flag-controls-hidden .jw-logo{visibility:visible}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-plugin{bottom:.5em}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-nextup-container{bottom:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-controls-hidden) .jw-media{cursor:none;-webkit-cursor-visibility:auto-hide}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing.jw-flag-casting .jw-display{display:table}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-ads) .jw-autostart-mute{display:flex}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting .jw-nextup-container{bottom:66px}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting.jw-state-idle .jw-nextup-container{display:none}.jw-flag-media-audio .jw-preview{display:block}.jwplayer.jw-flag-ads .jw-preview,.jwplayer.jw-flag-ads .jw-logo,.jwplayer.jw-flag-ads .jw-captions.jw-captions-enabled,.jwplayer.jw-flag-ads .jw-nextup-container,.jwplayer.jw-flag-ads .jw-text-duration,.jwplayer.jw-flag-ads .jw-text-elapsed{display:none}.jwplayer.jw-flag-ads video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-rewind,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-next,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-display{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player.jw-state-buffering .jw-display-icon-display{display:inline-block}.jwplayer.jw-flag-ads .jw-controlbar{flex-wrap:wrap-reverse}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time{height:auto;padding:0;pointer-events:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-slider-container{height:5px}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-rail,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-knob,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-buffer,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-cue,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-icon-settings{display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-progress{-webkit-transform:none;transform:none;top:auto}.jwplayer.jw-flag-ads .jw-controlbar .jw-tooltip,.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-tooltip:not(.jw-icon-volume),.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-inline:not(.jw-icon-playback):not(.jw-icon-fullscreen):not(.jw-icon-volume){display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-volume-tip{padding:13px 0}.jwplayer.jw-flag-ads .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid) .jw-controls .jw-controlbar,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart .jw-controls .jw-controlbar{display:flex;pointer-events:all;visibility:visible;opacity:1}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-user-inactive .jw-controls-backdrop,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart.jw-flag-user-inactive .jw-controls-backdrop{opacity:1;background-size:100% 60px}.jwplayer.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-ads-vpaid .jw-skip,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-skip{display:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls{background:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls::after{content:none}.jwplayer.jw-flag-ads-hide-controls .jw-controls-backdrop,.jwplayer.jw-flag-ads-hide-controls .jw-controls{display:none !important}.jw-flag-overlay-open-related .jw-controls,.jw-flag-overlay-open-related .jw-title,.jw-flag-overlay-open-related .jw-logo{display:none}.jwplayer.jw-flag-rightclick-open{overflow:visible}.jwplayer.jw-flag-rightclick-open .jw-rightclick{z-index:16777215}body .jwplayer.jw-flag-flash-blocked .jw-controls,body .jwplayer.jw-flag-flash-blocked .jw-overlays,body .jwplayer.jw-flag-flash-blocked .jw-controls-backdrop,body .jwplayer.jw-flag-flash-blocked .jw-preview{display:none}body .jwplayer.jw-flag-flash-blocked .jw-error-msg{top:25%}.jw-flag-touch.jw-breakpoint-7 .jw-captions,.jw-flag-touch.jw-breakpoint-6 .jw-captions,.jw-flag-touch.jw-breakpoint-5 .jw-captions,.jw-flag-touch.jw-breakpoint-4 .jw-captions,.jw-flag-touch.jw-breakpoint-7 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-6 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-5 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-4 .jw-nextup-container{bottom:4.25em}.jw-flag-touch .jw-controlbar .jw-icon-volume{display:flex}.jw-flag-touch .jw-display,.jw-flag-touch .jw-display-container,.jw-flag-touch .jw-display-controls{pointer-events:none}.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-rewind,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-rewind{display:none}.jw-flag-touch.jw-state-paused.jw-flag-dragging .jw-display{display:none}.jw-flag-audio-player{background-color:#000}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:44px}.jw-flag-audio-player:not(.jw-flag-live) .jw-spacer{display:none}.jw-flag-audio-player .jw-preview,.jw-flag-audio-player .jw-display,.jw-flag-audio-player .jw-title,.jw-flag-audio-player .jw-nextup-container{display:none}.jw-flag-audio-player .jw-controlbar{position:relative}.jw-flag-audio-player .jw-controlbar .jw-button-container{padding-right:3px;padding-left:0}.jw-flag-audio-player .jw-controlbar .jw-icon-tooltip,.jw-flag-audio-player .jw-controlbar .jw-icon-inline{display:none}.jw-flag-audio-player .jw-controlbar .jw-icon-volume,.jw-flag-audio-player .jw-controlbar .jw-icon-playback,.jw-flag-audio-player .jw-controlbar .jw-icon-next,.jw-flag-audio-player .jw-controlbar .jw-icon-rewind,.jw-flag-audio-player .jw-controlbar .jw-icon-cast,.jw-flag-audio-player .jw-controlbar .jw-text-live,.jw-flag-audio-player .jw-controlbar .jw-icon-airplay,.jw-flag-audio-player .jw-controlbar .jw-logo-button,.jw-flag-audio-player .jw-controlbar .jw-text-elapsed,.jw-flag-audio-player .jw-controlbar .jw-text-duration{display:flex;flex:0 0 auto}.jw-flag-audio-player .jw-controlbar .jw-text-duration,.jw-flag-audio-player .jw-controlbar .jw-text-countdown{padding-right:10px}.jw-flag-audio-player .jw-controlbar .jw-slider-time{flex:0 1 auto;align-items:center;display:flex;order:1}.jw-flag-audio-player .jw-controlbar .jw-icon-volume{margin-right:0;transition:margin-right 150ms cubic-bezier(0, .25, .25, 1)}.jw-flag-audio-player .jw-controlbar .jw-icon-volume .jw-overlay{display:none}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container{transition:width 300ms cubic-bezier(0, .25, .25, 1);width:0}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open{width:140px}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open .jw-slider-volume{padding-right:24px;transition:opacity 300ms;opacity:1}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open~.jw-slider-time{flex:1 1 auto;width:auto;transition:opacity 300ms, width 300ms}.jw-flag-audio-player .jw-controlbar .jw-slider-volume{opacity:0}.jw-flag-audio-player .jw-controlbar .jw-slider-volume .jw-knob{-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.jw-flag-audio-player .jw-controlbar .jw-slider-volume~.jw-icon-volume{margin-right:140px}.jw-flag-audio-player.jw-breakpoint-1 .jw-horizontal-volume-container.jw-open~.jw-slider-time,.jw-flag-audio-player.jw-breakpoint-2 .jw-horizontal-volume-container.jw-open~.jw-slider-time{opacity:0}.jw-flag-audio-player.jw-flag-small-player .jw-text-elapsed,.jw-flag-audio-player.jw-flag-small-player .jw-text-duration{display:none}.jw-flag-audio-player.jw-flag-ads .jw-slider-time{display:none}.jw-hidden{display:none}',""])}]]); \ No newline at end of file diff --git a/ui/v2.5/public/jwplayer/jwplayer.core.controls.js b/ui/v2.5/public/jwplayer/jwplayer.core.controls.js index 974fc20af..2730fb918 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.core.controls.js +++ b/ui/v2.5/public/jwplayer/jwplayer.core.controls.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * diff --git a/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.html5.js b/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.html5.js index c82790981..c87c9fd6a 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.html5.js +++ b/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.html5.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * @@ -92,4 +92,4 @@ COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQ The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders. */ -(window.webpackJsonpjwplayer=window.webpackJsonpjwplayer||[]).push([[6,1,2,3,4,5,7,9],[,,,,,,,,,,,,,,,,,function(t,e,i){"use strict";i.r(e);var n,o=i(8),a=i(3),r=i(7),s=i(43),l=i(5),c=i(15),u=i(40);function d(t){return n||(n=new DOMParser),Object(l.r)(Object(l.s)(n.parseFromString(t,"image/svg+xml").documentElement))}var p=function(t,e,i,n){var o=document.createElement("div");o.className="jw-icon jw-icon-inline jw-button-color jw-reset "+t,o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i&&o.setAttribute("aria-label",i),o.style.display="none";var a=new u.a(o).on("click tap enter",e||function(){});return n&&Array.prototype.forEach.call(n,(function(t){"string"==typeof t?o.appendChild(d(t)):o.appendChild(t)})),{ui:a,element:function(){return o},toggle:function(t){t?this.show():this.hide()},show:function(){o.style.display=""},hide:function(){o.style.display="none"}}},h=i(0),f=i(71),w=i.n(f),g=i(72),j=i.n(g),b=i(73),m=i.n(b),v=i(74),y=i.n(v),k=i(75),x=i.n(k),T=i(76),O=i.n(T),C=i(77),_=i.n(C),M=i(78),S=i.n(M),E=i(79),I=i.n(E),L=i(80),A=i.n(L),P=i(81),R=i.n(P),z=i(82),B=i.n(z),V=i(83),N=i.n(V),H=i(84),F=i.n(H),D=i(85),q=i.n(D),U=i(86),W=i.n(U),Q=i(62),Y=i.n(Q),X=i(87),K=i.n(X),J=i(88),Z=i.n(J),G=i(89),$=i.n(G),tt=i(90),et=i.n(tt),it=i(91),nt=i.n(it),ot=i(92),at=i.n(ot),rt=i(93),st=i.n(rt),lt=i(94),ct=i.n(lt),ut=null;function dt(t){var e=wt().querySelector(ht(t));if(e)return ft(e);throw new Error("Icon not found "+t)}function pt(t){var e=wt().querySelectorAll(t.split(",").map(ht).join(","));if(!e.length)throw new Error("Icons not found "+t);return Array.prototype.map.call(e,(function(t){return ft(t)}))}function ht(t){return".jw-svg-icon-".concat(t)}function ft(t){return t.cloneNode(!0)}function wt(){return ut||(ut=d(""+w.a+j.a+m.a+y.a+x.a+O.a+_.a+S.a+I.a+A.a+R.a+B.a+N.a+F.a+q.a+W.a+Y.a+K.a+Z.a+$.a+et.a+nt.a+at.a+st.a+ct.a+"")),ut}var gt=i(10);function jt(t,e){for(var i=0;i10&&delete bt[e[0]];var i=d(t);bt[t]=i}return bt[t].cloneNode(!0)}(e):((r=document.createElement("div")).className="jw-icon jw-button-image jw-button-color jw-reset",e&&Object(gt.d)(r,{backgroundImage:"url(".concat(e,")")})),s.appendChild(r),new u.a(s).on("click tap enter",n,this),s.addEventListener("mousedown",(function(t){t.preventDefault()})),this.id=o,this.buttonElement=s}var e,i,n;return e=t,(i=[{key:"element",value:function(){return this.buttonElement}},{key:"toggle",value:function(t){t?this.show():this.hide()}},{key:"show",value:function(){this.buttonElement.style.display=""}},{key:"hide",value:function(){this.buttonElement.style.display="none"}}])&&jt(e.prototype,i),n&&jt(e,n),t}(),vt=i(11);function yt(t,e){for(var i=0;i=0&&(e.left-=i,e.right-=i),e},xt=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),Object(h.g)(this,r.a),this.className=e+" jw-background-color jw-reset",this.orientation=i}var e,i,n;return e=t,(i=[{key:"setup",value:function(){this.el=Object(l.e)(function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return''}(this.className,"jw-slider-"+this.orientation)),this.elementRail=this.el.getElementsByClassName("jw-slider-container")[0],this.elementBuffer=this.el.getElementsByClassName("jw-buffer")[0],this.elementProgress=this.el.getElementsByClassName("jw-progress")[0],this.elementThumb=this.el.getElementsByClassName("jw-knob")[0],this.ui=new u.a(this.element(),{preventScrolling:!0}).on("dragStart",this.dragStart,this).on("drag",this.dragMove,this).on("dragEnd",this.dragEnd,this).on("click tap",this.tap,this)}},{key:"dragStart",value:function(){this.trigger("dragStart"),this.railBounds=kt(this.elementRail)}},{key:"dragEnd",value:function(t){this.dragMove(t),this.trigger("dragEnd")}},{key:"dragMove",value:function(t){var e,i,n=this.railBounds=this.railBounds?this.railBounds:kt(this.elementRail);return i="horizontal"===this.orientation?(e=t.pageX)n.right?100:100*Object(s.a)((e-n.left)/n.width,0,1):(e=t.pageY)>=n.bottom?0:e<=n.top?100:100*Object(s.a)((n.height-(e-n.top))/n.height,0,1),this.render(i),this.update(i),!1}},{key:"tap",value:function(t){this.railBounds=kt(this.elementRail),this.dragMove(t)}},{key:"limit",value:function(t){return t}},{key:"update",value:function(t){this.trigger("update",{percentage:t})}},{key:"render",value:function(t){t=Math.max(0,Math.min(t,100)),"horizontal"===this.orientation?(this.elementThumb.style.left=t+"%",this.elementProgress.style.width=t+"%"):(this.elementThumb.style.bottom=t+"%",this.elementProgress.style.height=t+"%")}},{key:"updateBuffer",value:function(t){this.elementBuffer.style.width=t+"%"}},{key:"element",value:function(){return this.el}}])&&yt(e.prototype,i),n&&yt(e,n),t}(),Tt=function(t,e){t&&e&&(t.setAttribute("aria-label",e),t.setAttribute("role","button"),t.setAttribute("tabindex","0"))};function Ot(t,e){for(var i=0;i0&&Array.prototype.forEach.call(o,(function(t){"string"==typeof t?a.el.appendChild(d(t)):a.el.appendChild(t)}))}var e,i,n;return e=t,(i=[{key:"addContent",value:function(t){this.content&&this.removeContent(),this.content=t,this.tooltip.appendChild(t)}},{key:"removeContent",value:function(){this.content&&(this.tooltip.removeChild(this.content),this.content=null)}},{key:"hasContent",value:function(){return!!this.content}},{key:"element",value:function(){return this.el}},{key:"openTooltip",value:function(t){this.isOpen||(this.trigger("open-"+this.componentType,t,{isOpen:!0}),this.isOpen=!0,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"closeTooltip",value:function(t){this.isOpen&&(this.trigger("close-"+this.componentType,t,{isOpen:!1}),this.isOpen=!1,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"toggleOpenState",value:function(t){this.isOpen?this.closeTooltip(t):this.openTooltip(t)}}])&&Ot(e.prototype,i),n&&Ot(e,n),t}(),_t=i(22),Mt=i(57);function St(t,e){for(var i=0;i=this.thumbnails.length&&(e=this.thumbnails.length-1);var i=this.thumbnails[e].img;return i.indexOf("://")<0&&(i=this.vttPath?this.vttPath+"/"+i:i),i},loadThumbnail:function(t){var e=this.chooseThumbnail(t),i={margin:"0 auto",backgroundPosition:"0 0"};if(e.indexOf("#xywh")>0)try{var n=/(.+)#xywh=(\d+),(\d+),(\d+),(\d+)/.exec(e);e=n[1],i.backgroundPosition=-1*n[2]+"px "+-1*n[3]+"px",i.width=n[4],this.timeTip.setWidth(+i.width),i.height=n[5]}catch(t){return}else this.individualImage||(this.individualImage=new Image,this.individualImage.onload=Object(h.a)((function(){this.individualImage.onload=null,this.timeTip.image({width:this.individualImage.width,height:this.individualImage.height}),this.timeTip.setWidth(this.individualImage.width)}),this),this.individualImage.src=e);return i.backgroundImage='url("'+e+'")',i},showThumbnail:function(t){this._model.get("containerWidth")<=420||this.thumbnails.length<1||this.timeTip.image(this.loadThumbnail(t))},resetThumbnails:function(){this.timeTip.image({backgroundImage:"",width:0,height:0}),this.thumbnails=[]}};function Pt(t,e,i){return(Pt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(t,e,i){var n=function(t,e){for(;!Object.prototype.hasOwnProperty.call(t,e)&&null!==(t=Ht(t)););return t}(t,e);if(n){var o=Object.getOwnPropertyDescriptor(n,e);return o.get?o.get.call(i):o.value}})(t,e,i||t)}function Rt(t){return(Rt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function zt(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Bt(t,e){for(var i=0;i-1&&(n="Live")}var d=this.timeTip;d.update(n),this.textLength!==n.length&&(this.textLength=n.length,d.resetWidth()),this.showThumbnail(u),Object(l.a)(d.el,"jw-open");var p=d.getWidth(),h=a.width/100,f=o-a.width,w=0;p>f&&(w=(p-f)/(200*h));var g=100*Math.min(1-w,Math.max(w,c)).toFixed(3);Object(gt.d)(d.el,{left:g+"%"})}}},{key:"hideTimeTooltip",value:function(){Object(l.o)(this.timeTip.el,"jw-open")}},{key:"updateCues",value:function(t,e){var i=this;this.resetCues(),e&&e.length&&(e.forEach((function(t){i.addCue(t)})),this.drawCues())}},{key:"updateAriaText",value:function(){var t=this._model;if(!t.get("seeking")){var e=t.get("position"),i=t.get("duration"),n=Object(vt.timeFormat)(e);"DVR"!==this.streamType&&(n+=" of ".concat(Object(vt.timeFormat)(i)));var o=this.el;document.activeElement!==o&&(this.timeUpdateKeeper.textContent=n),Object(l.t)(o,"aria-valuenow",e),Object(l.t)(o,"aria-valuetext",n)}}},{key:"reset",value:function(){this.resetThumbnails(),this.timeTip.resetWidth(),this.textLength=0}}]),e}(xt);Object(h.g)(Ut.prototype,It,At);var Wt=Ut;function Qt(t,e){for(var i=0;i=75&&!t),Object(l.t)(r,"aria-valuenow",o),Object(l.t)(s,"aria-valuenow",o);var c="Volume ".concat(o,"%");Object(l.t)(r,"aria-valuetext",c),Object(l.t)(s,"aria-valuetext",c),document.activeElement!==r&&document.activeElement!==s&&(this._volumeAnnouncer.textContent=c)}}},{key:"onCastAvailable",value:function(t,e){this.elements.cast.toggle(e)}},{key:"onCastActive",value:function(t,e){this.elements.fullscreen.toggle(!e),this.elements.cast.button&&Object(l.v)(this.elements.cast.button,"jw-off",!e)}},{key:"onElapsed",value:function(t,e){var i,n,o=t.get("duration");if("DVR"===t.get("streamType")){var a=Math.ceil(e),r=this._model.get("dvrSeekLimit");i=n=a>=-r?"":"-"+Object(vt.timeFormat)(-(e+r)),t.set("dvrLive",a>=-r)}else i=Object(vt.timeFormat)(e),n=Object(vt.timeFormat)(o-e);this.elements.elapsed.textContent=i,this.elements.countdown.textContent=n}},{key:"onDuration",value:function(t,e){this.elements.duration.textContent=Object(vt.timeFormat)(Math.abs(e))}},{key:"onAudioMode",value:function(t,e){var i=this.elements.time.element();e?this.elements.buttonContainer.insertBefore(i,this.elements.elapsed):Object(l.m)(this.el,i)}},{key:"element",value:function(){return this.el}},{key:"setAltText",value:function(t,e){this.elements.alt.textContent=e}},{key:"closeMenus",value:function(t){this.menus.forEach((function(e){t&&t.target===e.el||e.closeTooltip(t)}))}},{key:"rewind",value:function(){var t,e=0,i=this._model.get("currentTime");i?t=i-10:(t=this._model.get("position")-10,"DVR"===this._model.get("streamType")&&(e=this._model.get("duration"))),this._api.seek(Math.max(t,e),{reason:"interaction"})}},{key:"onState",value:function(t,e){var i=t.get("localization"),n=i.play;this.setPlayText(n),e===a.pb&&("LIVE"!==t.get("streamType")?(n=i.pause,this.setPlayText(n)):(n=i.stop,this.setPlayText(n))),Object(l.t)(this.elements.play.element(),"aria-label",n)}},{key:"onStreamTypeChange",value:function(t,e){var i="LIVE"===e,n="DVR"===e;this.elements.rewind.toggle(!i),this.elements.live.toggle(i||n),Object(l.t)(this.elements.live.element(),"tabindex",i?"-1":"0"),this.elements.duration.style.display=n?"none":"",this.onDuration(t,t.get("duration")),this.onState(t,t.get("state"))}},{key:"addLogo",value:function(t){var e=this.elements.buttonContainer,i=new mt(t.file,this._model.get("localization").logo,(function(){t.link&&Object(l.l)(t.link,"_blank",{rel:"noreferrer"})}),"logo","jw-logo-button");t.link||Object(l.t)(i.element(),"tabindex","-1"),e.insertBefore(i.element(),e.querySelector(".jw-spacer").nextSibling)}},{key:"goToLiveEdge",value:function(){if("DVR"===this._model.get("streamType")){var t=Math.min(this._model.get("position"),-1),e=this._model.get("dvrSeekLimit");this._api.seek(Math.max(-e,t),{reason:"interaction"}),this._api.play({reason:"interaction"})}}},{key:"updateButtons",value:function(t,e,i){if(e){var n,o,a=this.elements.buttonContainer;e!==i&&i?(n=ce(e,i),o=ce(i,e),this.removeButtons(a,o)):n=e;for(var r=n.length-1;r>=0;r--){var s=n[r],l=new mt(s.img,s.tooltip,s.callback,s.id,s.btnClass);s.tooltip&&ne(l.element(),s.id,s.tooltip);var c=void 0;"related"===l.id?c=this.elements.settingsButton.element():"share"===l.id?c=a.querySelector('[button="related"]')||this.elements.settingsButton.element():(c=this.elements.spacer.nextSibling)&&"logo"===c.getAttribute("button")&&(c=c.nextSibling),a.insertBefore(l.element(),c)}}}},{key:"removeButtons",value:function(t,e){for(var i=e.length;i--;){var n=t.querySelector('[button="'.concat(e[i].id,'"]'));n&&t.removeChild(n)}}},{key:"toggleCaptionsButtonState",value:function(t){var e=this.elements.captionsButton;e&&Object(l.v)(e.element(),"jw-off",!t)}},{key:"destroy",value:function(){var t=this;this._model.off(null,null,this),Object.keys(this.elements).forEach((function(e){var i=t.elements[e];i&&"function"==typeof i.destroy&&t.elements[e].destroy()})),this.ui.forEach((function(t){t.destroy()})),this.ui=[]}}])&&ae(e.prototype,i),n&&ae(e,n),t}(),pe=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return'
    ')+'
    ')+"
    "},he=function(t){return'
    '+pe("rewind",t.rewind)+pe("display",t.playback)+pe("next",t.next)+"
    "};function fe(t,e){for(var i=0;i'.concat(a.playback,"")),Object(l.a)(o.icon,"jw-idle-label"),o.icon.appendChild(s))}return o}var i,n,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&ve(t,e)}(e,t),i=e,(n=[{key:"element",value:function(){return this.el}}])&&je(i.prototype,n),o&&je(i,o),e}(r.a);function ke(t,e){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";return'
    '+'
    '.concat(t,"
    ")+'
    '.concat(e,"
    ")+'
    '.concat(i,"
    ")+"
    "+'')+"
    "}());e.querySelector(".jw-nextup-close").appendChild(dt("close")),this.addContent(e),this.closeButton=this.content.querySelector(".jw-nextup-close"),this.closeButton.setAttribute("aria-label",this.localization.close),this.tooltip=this.content.querySelector(".jw-nextup-tooltip");var i=this._model,n=i.player;this.enabled=!1,i.on("change:nextUp",this.onNextUp,this),n.change("duration",this.onDuration,this),n.change("position",this.onElapsed,this),n.change("streamType",this.onStreamType,this),n.change("state",(function(t,e){"complete"===e&&this.toggle(!1)}),this),this.closeUi=new u.a(this.closeButton,{directSelect:!0}).on("click tap enter",(function(){this.nextUpSticky=!1,this.toggle(!1)}),this),this.tooltipUi=new u.a(this.tooltip).on("click tap",this.click,this)}},{key:"loadThumbnail",value:function(t){return this.nextUpImage=new Image,this.nextUpImage.onload=function(){this.nextUpImage.onload=null}.bind(this),this.nextUpImage.src=t,{backgroundImage:'url("'+t+'")'}}},{key:"click",value:function(){var t=this.feedShownId;this.reset(),this._api.next({feedShownId:t,reason:"interaction"})}},{key:"toggle",value:function(t,e){if(this.enabled&&(Object(l.v)(this.container,"jw-nextup-sticky",!!this.nextUpSticky),this.shown!==t)){this.shown=t,Object(l.v)(this.container,"jw-nextup-container-visible",t),Object(l.v)(this._playerElement,"jw-flag-nextup",t);var i=this._model.get("nextUp");t&&i?(this.feedShownId=Object(oe.b)(oe.a),this.trigger("nextShown",{mode:i.mode,ui:"nextup",itemsShown:[i],feedData:i.feedData,reason:e,feedShownId:this.feedShownId})):this.feedShownId=""}}},{key:"setNextUpItem",value:function(t){var e=this;setTimeout((function(){if(e.thumbnail=e.content.querySelector(".jw-nextup-thumbnail"),Object(l.v)(e.content,"jw-nextup-thumbnail-visible",!!t.image),t.image){var i=e.loadThumbnail(t.image);Object(gt.d)(e.thumbnail,i)}e.header=e.content.querySelector(".jw-nextup-header"),e.header.textContent=Object(l.e)(e.localization.nextUp).textContent,e.title=e.content.querySelector(".jw-nextup-title");var n=t.title;e.title.textContent=n?Object(l.e)(n).textContent:"";var o=t.duration;o&&(e.duration=e.content.querySelector(".jw-nextup-duration"),e.duration.textContent="number"==typeof o?Object(vt.timeFormat)(o):o)}),500)}},{key:"onNextUp",value:function(t,e){this.reset(),e||(e={showNextUp:!1}),this.enabled=!(!e.title&&!e.image),this.enabled&&(e.showNextUp||(this.nextUpSticky=!1,this.toggle(!1)),this.setNextUpItem(e))}},{key:"onDuration",value:function(t,e){if(e){var i=t.get("nextupoffset"),n=-10;i&&(n=Object(_e.d)(i,e)),n<0&&(n+=e),Object(_e.c)(i)&&e-5=this.offset;n&&void 0===i?(this.nextUpSticky=n,this.toggle(n,"time")):!n&&i&&this.reset()}}},{key:"onStreamType",value:function(t,e){"VOD"!==e&&(this.nextUpSticky=!1,this.toggle(!1))}},{key:"element",value:function(){return this.container}},{key:"addContent",value:function(t){this.content&&this.removeContent(),this.content=t,this.container.appendChild(t)}},{key:"removeContent",value:function(){this.content&&(this.container.removeChild(this.content),this.content=null)}},{key:"reset",value:function(){this.nextUpSticky=void 0,this.toggle(!1)}},{key:"destroy",value:function(){this.off(),this._model.off(null,null,this),this.closeUi&&this.closeUi.destroy(),this.tooltipUi&&this.tooltipUi.destroy()}}])&&Me(e.prototype,i),n&&Me(e,n),t}(),Ee=function(t,e){var i=t.featured,n=t.showLogo,o=t.type;return t.logo=n?'':"",'
  • ').concat(Ie[o](t,e),"
  • ")},Ie={link:function(t){var e=t.link,i=t.title,n=t.logo;return'').concat(n).concat(i||"","")},info:function(t,e){return'")},share:function(t,e){return'")},keyboardShortcuts:function(t,e){return'")}},Le=i(23),Ae=i(6),Pe=i(13);function Re(t,e){for(var i=0;iJW Player '.concat(t,""),a={items:[{type:"info"},{title:Object(Pe.e)(n)?"".concat(o," ").concat(n):"".concat(n," ").concat(o),type:"link",featured:!0,showLogo:!0,link:"https://jwplayer.com/learn-more?e=".concat(ze[i])}]},r=e.get("provider"),s=a.items;if(r&&r.name.indexOf("flash")>=0){var l="Flash Version "+Object(Ae.a)();s.push({title:l,type:"link",link:"http://www.adobe.com/software/flash/about/"})}return this.shortcutsTooltip&&s.splice(s.length-1,0,{type:"keyboardShortcuts"}),a}},{key:"rightClick",value:function(t){if(this.lazySetup(),this.mouseOverContext)return!1;this.hideMenu(),this.showMenu(t),this.addHideMenuHandlers()}},{key:"getOffset",value:function(t){var e=Object(l.c)(this.wrapperElement),i=t.pageX-e.left,n=t.pageY-e.top;return this.model.get("touchMode")&&(n-=100),{x:i,y:n}}},{key:"showMenu",value:function(t){var e=this,i=this.getOffset(t);return this.el.style.left=i.x+"px",this.el.style.top=i.y+"px",this.outCount=0,Object(l.a)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.a)(this.el,"jw-open"),clearTimeout(this._menuTimeout),this._menuTimeout=setTimeout((function(){return e.hideMenu()}),3e3),!1}},{key:"hideMenu",value:function(t){t&&this.el&&this.el.contains(t.target)||(Object(l.o)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.o)(this.el,"jw-open"))}},{key:"lazySetup",value:function(){var t,e,i,n,o=this,a=(t=this.buildArray(),e=this.model.get("localization"),i=t.items,n=(void 0===i?[]:i).map((function(t){return Ee(t,e)})),'
    '+'
      '.concat(n.join(""),"
    ")+"
    ");if(this.el){if(this.html!==a){this.html=a;var r=Be(a);Object(l.h)(this.el);for(var s=r.childNodes.length;s--;)this.el.appendChild(r.firstChild)}}else this.html=a,this.el=Be(this.html),this.wrapperElement.appendChild(this.el),this.hideMenuHandler=function(t){return o.hideMenu(t)},this.overHandler=function(){o.mouseOverContext=!0},this.outHandler=function(t){o.mouseOverContext=!1,t.relatedTarget&&!o.el.contains(t.relatedTarget)&&++o.outCount>1&&o.hideMenu()},this.infoOverlayHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.infoOverlay.open()},this.shortcutsTooltipHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.shortcutsTooltip.open()}}},{key:"setup",value:function(t,e,i){this.wrapperElement=i,this.model=t,this.mouseOverContext=!1,this.playerContainer=e,this.ui=new u.a(i).on("longPress",this.rightClick,this)}},{key:"addHideMenuHandlers",value:function(){this.removeHideMenuHandlers(),this.wrapperElement.addEventListener("touchstart",this.hideMenuHandler),document.addEventListener("touchstart",this.hideMenuHandler),o.OS.mobile||(this.wrapperElement.addEventListener("click",this.hideMenuHandler),document.addEventListener("click",this.hideMenuHandler),this.el.addEventListener("mouseover",this.overHandler),this.el.addEventListener("mouseout",this.outHandler)),this.el.querySelector(".jw-info-overlay-item").addEventListener("click",this.infoOverlayHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").addEventListener("click",this.shortcutsTooltipHandler)}},{key:"removeHideMenuHandlers",value:function(){this.wrapperElement&&(this.wrapperElement.removeEventListener("click",this.hideMenuHandler),this.wrapperElement.removeEventListener("touchstart",this.hideMenuHandler)),this.el&&(this.el.querySelector(".jw-info-overlay-item").removeEventListener("click",this.infoOverlayHandler),this.el.removeEventListener("mouseover",this.overHandler),this.el.removeEventListener("mouseout",this.outHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").removeEventListener("click",this.shortcutsTooltipHandler)),document.removeEventListener("click",this.hideMenuHandler),document.removeEventListener("touchstart",this.hideMenuHandler)}},{key:"destroy",value:function(){clearTimeout(this._menuTimeout),this.removeHideMenuHandlers(),this.el&&(this.hideMenu(),this.hideMenuHandler=null,this.el=null),this.wrapperElement&&(this.wrapperElement.oncontextmenu=null,this.wrapperElement=null),this.model&&(this.model=null),this.ui&&(this.ui.destroy(),this.ui=null)}}])&&Re(e.prototype,i),n&&Re(e,n),t}(),Ne=function(t){return'")},He=function(t){return'"},Fe=function(t){return'"};function De(t){return(De="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function qe(t,e){return!e||"object"!==De(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function Ue(t){return(Ue=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function We(t,e){return(We=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function Qe(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Ye(t,e){for(var i=0;i2&&void 0!==arguments[2]?arguments[2]:Ne;Qe(this,t),this.el=Object(l.e)(n(e)),this.ui=new u.a(this.el).on("click tap enter",i,this)}return Xe(t,[{key:"destroy",value:function(){this.ui.destroy()}}]),t}(),Ze=function(t){function e(t,i){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Fe;return Qe(this,e),qe(this,Ue(e).call(this,t,i,n))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&We(t,e)}(e,t),Xe(e,[{key:"activate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!0),this.el.setAttribute("aria-checked","true"),this.active=!0}},{key:"deactivate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!1),this.el.setAttribute("aria-checked","false"),this.active=!1}}]),e}(Je),Ge=function(t,e){return t?'':''},$e=function(t,e){var i=t.name,n={captions:"cc-off",audioTracks:"audio-tracks",quality:"quality-100",playbackRates:"playback-rate"}[i];if(n||t.icon){var o=p("jw-settings-".concat(i," jw-submenu-").concat(i),(function(e){t.open(e)}),i,[t.icon&&Object(l.e)(t.icon)||dt(n)]),a=o.element();return a.setAttribute("role","menuitemradio"),a.setAttribute("aria-checked","false"),a.setAttribute("aria-label",e),"ontouchstart"in window||(o.tooltip=ne(a,i,e)),o}};function ti(t){return(ti="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ei(t,e){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:Ge;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),a=this,(o=!(r=ii(e).call(this))||"object"!==ti(r)&&"function"!=typeof r?oi(a):r).open=o.open.bind(oi(oi(o))),o.close=o.close.bind(oi(oi(o))),o.toggle=o.toggle.bind(oi(oi(o))),o.onDocumentClick=o.onDocumentClick.bind(oi(oi(o))),o.name=t,o.isSubmenu=!!i,o.el=Object(l.e)(s(o.isSubmenu,t)),o.topbar=o.el.querySelector(".jw-".concat(o.name,"-topbar")),o.buttonContainer=o.el.querySelector(".jw-".concat(o.name,"-topbar-buttons")),o.children={},o.openMenus=[],o.items=[],o.visible=!1,o.parentMenu=i,o.mainMenu=o.parentMenu?o.parentMenu.mainMenu:oi(oi(o)),o.categoryButton=null,o.closeButton=o.parentMenu&&o.parentMenu.closeButton||o.createCloseButton(n),o.isSubmenu?(o.categoryButton=o.parentMenu.categoryButton||o.createCategoryButton(n),o.parentMenu.parentMenu&&!o.mainMenu.backButton&&(o.mainMenu.backButton=o.createBackButton(n)),o.itemsContainer=o.createItemsContainer(),o.parentMenu.appendMenu(oi(oi(o)))):o.ui=ri(oi(oi(o))),o}var i,n,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&ni(t,e)}(e,t),i=e,(n=[{key:"createItemsContainer",value:function(){var t,e,i=this,n=this.el.querySelector(".jw-settings-submenu-items"),o=new u.a(n),a=this.categoryButton&&this.categoryButton.element()||this.parentMenu.categoryButton&&this.parentMenu.categoryButton.element()||this.mainMenu.buttonContainer.firstChild;return this.parentMenu.isSubmenu&&(t=this.mainMenu.closeButton.element(),e=this.mainMenu.backButton.element()),o.on("keydown",(function(o){if(o.target.parentNode===n){var r=function(t,e){t?t.focus():void 0!==e&&n.childNodes[e].focus()},s=o.sourceEvent,c=s.target,u=n.firstChild===c,d=n.lastChild===c,p=i.topbar,h=t||Object(l.k)(a),f=e||Object(l.n)(a),w=Object(l.k)(s.target),g=Object(l.n)(s.target),j=s.key.replace(/(Arrow|ape)/,"");switch(j){case"Tab":r(s.shiftKey?f:h);break;case"Left":r(f||Object(l.n)(document.getElementsByClassName("jw-icon-settings")[0]));break;case"Up":p&&u?r(p.firstChild):r(g,n.childNodes.length-1);break;case"Right":r(h);break;case"Down":p&&d?r(p.firstChild):r(w,0)}s.preventDefault(),"Esc"!==j&&s.stopPropagation()}})),o}},{key:"createCloseButton",value:function(t){var e=p("jw-settings-close",this.close,t.close,[dt("close")]);return this.topbar.appendChild(e.element()),e.show(),e.ui.on("keydown",(function(t){var e=t.sourceEvent,i=e.key.replace(/(Arrow|ape)/,"");("Enter"===i||"Right"===i||"Tab"===i&&!e.shiftKey)&&this.close(t)}),this),this.buttonContainer.appendChild(e.element()),e}},{key:"createCategoryButton",value:function(t){var e=t[{captions:"cc",audioTracks:"audioTracks",quality:"hd",playbackRates:"playbackRates"}[this.name]];"sharing"===this.name&&(e=t.sharing.heading);var i=$e(this,e);return i.element().setAttribute("name",this.name),i}},{key:"createBackButton",value:function(t){var e=p("jw-settings-back",(function(t){Ke&&Ke.open(t)}),t.close,[dt("arrow-left")]);return Object(l.m)(this.mainMenu.topbar,e.element()),e}},{key:"createTopbar",value:function(){var t=Object(l.e)('
    ');return Object(l.m)(this.el,t),t}},{key:"createItems",value:function(t,e){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Ze,a=this.name,r=t.map((function(t,r){var s,l;switch(a){case"quality":s="Auto"===t.label&&0===r?"".concat(n.defaultText,' '):t.label;break;case"captions":s="Off"!==t.label&&"off"!==t.id||0!==r?t.label:n.defaultText;break;case"playbackRates":l=t,s=Object(Pe.e)(n.tooltipText)?"x"+t:t+"x";break;case"audioTracks":s=t.name}s||(s=t,"object"===ti(t)&&(s.options=n));var c=new o(s,function(t){c.active||(e(l||r),c.deactivate&&(i.items.filter((function(t){return!0===t.active})).forEach((function(t){t.deactivate()})),Ke?Ke.open(t):i.mainMenu.close(t)),c.activate&&c.activate())}.bind(i));return c}));return r}},{key:"setMenuItems",value:function(t,e){var i=this;t?(this.items=[],Object(l.h)(this.itemsContainer.el),t.forEach((function(t){i.items.push(t),i.itemsContainer.el.appendChild(t.el)})),e>-1&&t[e].activate(),this.categoryButton.show()):this.removeMenu()}},{key:"appendMenu",value:function(t){if(t){var e=t.el,i=t.name,n=t.categoryButton;if(this.children[i]=t,n){var o=this.mainMenu.buttonContainer,a=o.querySelector(".jw-settings-sharing"),r="quality"===i?o.firstChild:a||this.closeButton.element();o.insertBefore(n.element(),r)}this.mainMenu.el.appendChild(e)}}},{key:"removeMenu",value:function(t){if(!t)return this.parentMenu.removeMenu(this.name);var e=this.children[t];e&&(delete this.children[t],e.destroy())}},{key:"open",value:function(t){if(!this.visible||this.openMenus){var e;if(Ke=null,this.isSubmenu){var i=this.mainMenu,n=this.parentMenu,o=this.categoryButton;if(n.openMenus.length&&n.closeChildren(),o&&o.element().setAttribute("aria-checked","true"),n.isSubmenu){n.el.classList.remove("jw-settings-submenu-active"),i.topbar.classList.add("jw-nested-menu-open");var a=i.topbar.querySelector(".jw-settings-topbar-text");a.setAttribute("name",this.name),a.innerText=this.title||this.name,i.backButton.show(),Ke=this.parentMenu,e=this.topbar?this.topbar.firstChild:t&&"enter"===t.type?this.items[0].el:a}else i.topbar.classList.remove("jw-nested-menu-open"),i.backButton&&i.backButton.hide();this.el.classList.add("jw-settings-submenu-active"),n.openMenus.push(this.name),i.visible||(i.open(t),this.items&&t&&"enter"===t.type?e=this.topbar?this.topbar.firstChild.focus():this.items[0].el:o.tooltip&&(o.tooltip.suppress=!0,e=o.element())),this.openMenus.length&&this.closeChildren(),e&&e.focus(),this.el.scrollTop=0}else this.el.parentNode.classList.add("jw-settings-open"),this.trigger("menuVisibility",{visible:!0,evt:t}),document.addEventListener("click",this.onDocumentClick);this.visible=!0,this.el.setAttribute("aria-expanded","true")}}},{key:"close",value:function(t){var e=this;this.visible&&(this.visible=!1,this.el.setAttribute("aria-expanded","false"),this.isSubmenu?(this.el.classList.remove("jw-settings-submenu-active"),this.categoryButton.element().setAttribute("aria-checked","false"),this.parentMenu.openMenus=this.parentMenu.openMenus.filter((function(t){return t!==e.name})),!this.mainMenu.openMenus.length&&this.mainMenu.visible&&this.mainMenu.close(t)):(this.el.parentNode.classList.remove("jw-settings-open"),this.trigger("menuVisibility",{visible:!1,evt:t}),document.removeEventListener("click",this.onDocumentClick)),this.openMenus.length&&this.closeChildren())}},{key:"closeChildren",value:function(){var t=this;this.openMenus.forEach((function(e){var i=t.children[e];i&&i.close()}))}},{key:"toggle",value:function(t){this.visible?this.close(t):this.open(t)}},{key:"onDocumentClick",value:function(t){/jw-(settings|video|nextup-close|sharing-link|share-item)/.test(t.target.className)||this.close()}},{key:"destroy",value:function(){var t=this;if(document.removeEventListener("click",this.onDocumentClick),Object.keys(this.children).map((function(e){t.children[e].destroy()})),this.isSubmenu){this.parentMenu.name===this.mainMenu.name&&this.categoryButton&&(this.parentMenu.buttonContainer.removeChild(this.categoryButton.element()),this.categoryButton.ui.destroy()),this.itemsContainer&&this.itemsContainer.destroy();var e=this.parentMenu.openMenus,i=e.indexOf(this.name);e.length&&i>-1&&this.openMenus.splice(i,1),delete this.parentMenu}else this.ui.destroy();this.visible=!1,this.el.parentNode&&this.el.parentNode.removeChild(this.el)}},{key:"defaultChild",get:function(){var t=this.children,e=t.quality,i=t.captions,n=t.audioTracks,o=t.sharing,a=t.playbackRates;return e||i||n||o||a}}])&&ei(i.prototype,n),o&&ei(i,o),e}(r.a),ri=function(t){var e=t.closeButton,i=t.el;return new u.a(i).on("keydown",(function(i){var n=i.sourceEvent,o=i.target,a=Object(l.k)(o),r=Object(l.n)(o),s=n.key.replace(/(Arrow|ape)/,""),c=function(e){r?e||r.focus():t.close(i)};switch(s){case"Esc":t.close(i);break;case"Left":c();break;case"Right":a&&e.element()&&o!==e.element()&&a.focus();break;case"Tab":n.shiftKey&&c(!0);break;case"Up":case"Down":!function(){var e=t.children[o.getAttribute("name")];if(!e&&Ke&&(e=Ke.children[Ke.openMenus]),e)return e.open(i),void(e.topbar?e.topbar.firstChild.focus():e.items&&e.items.length&&e.items[0].el.focus());if(i.target.parentNode.classList.contains("jw-submenu-topbar")){var n=i.target.parentNode.parentNode.querySelector(".jw-settings-submenu-items");("Down"===s?n.childNodes[0]:n.childNodes[n.childNodes.length-1]).focus()}}()}if(n.stopPropagation(),/13|32|37|38|39|40/.test(n.keyCode))return n.preventDefault(),!1}))},si=i(59),li=function(t){return hi[t]},ci=function(t){for(var e,i=Object.keys(hi),n=0;n1;i.elements.settingsButton.toggle(c)};e.change("levels",(function(t,e){r(e)}),o);var s=function(t,i,n){var o=e.get("levels");if(o&&"Auto"===o[0].label&&i&&i.items.length){var a=i.items[0].el.querySelector(".jw-auto-label"),r=o[t.index]||{label:""};a.textContent=n?"":r.label}};e.on("change:visualQuality",(function(t,i){var n=o.children.quality;i&&n&&s(i.level,n,e.get("currentLevel"))})),e.on("change:currentLevel",(function(t,i){var n=o.children.quality,a=e.get("visualQuality");a&&n&&s(a.level,n,i)}),o),e.change("captionsList",(function(i,r){var s={defaultText:n.off},l=e.get("captionsIndex");a("captions",r,(function(e){return t.setCurrentCaptions(e)}),l,s);var c=o.children.captions;if(c&&!c.children.captionsSettings){c.topbar=c.topbar||c.createTopbar();var u=new ai("captionsSettings",c,n);u.title="Subtitle Settings";var d=new Je("Settings",u.open);c.topbar.appendChild(d.el);var p=new Ze("Reset",(function(){e.set("captions",si.a),w()}));p.el.classList.add("jw-settings-reset");var f=e.get("captions"),w=function(){var t=[];pi.forEach((function(i){f&&f[i.propertyName]&&(i.defaultVal=i.getOption(f[i.propertyName]));var o=new ai(i.name,u,n),a=new Je({label:i.name,value:i.defaultVal},o.open,He),r=o.createItems(i.options,(function(t){var n=a.el.querySelector(".jw-settings-content-item-value");!function(t,i){var n=e.get("captions"),o=t.propertyName,a=t.options&&t.options[i],r=t.getTypedValue(a),s=Object(h.g)({},n);s[o]=r,e.set("captions",s)}(i,t),n.innerText=i.options[t]}),null);o.setMenuItems(r,i.options.indexOf(i.defaultVal)||0),t.push(a)})),t.push(p),u.setMenuItems(t)};w()}}));var l=function(t,e){t&&e>-1&&t.items[e].activate()};e.change("captionsIndex",(function(t,e){var n=o.children.captions;n&&l(n,e),i.toggleCaptionsButtonState(!!e)}),o);var c=function(i){if(e.get("supportsPlaybackRate")&&"LIVE"!==e.get("streamType")&&e.get("playbackRateControls")){var r=i.indexOf(e.get("playbackRate")),s={tooltipText:n.playbackRates};a("playbackRates",i,(function(e){return t.setPlaybackRate(e)}),r,s)}else o.children.playbackRates&&o.removeMenu("playbackRates")};e.on("change:playbackRates",(function(t,e){c(e)}),o);var u=function(i){a("audioTracks",i,(function(e){return t.setCurrentAudioTrack(e)}),e.get("currentAudioTrack"))};return e.on("change:audioTracks",(function(t,e){u(e)}),o),e.on("change:playbackRate",(function(t,i){var n=e.get("playbackRates"),a=-1;n&&(a=n.indexOf(i)),l(o.children.playbackRates,a)}),o),e.on("change:currentAudioTrack",(function(t,e){o.children.audioTracks.items[e].activate()}),o),e.on("change:playlistItem",(function(){o.removeMenu("captions"),i.elements.captionsButton.hide(),o.visible&&o.close()}),o),e.on("change:playbackRateControls",(function(){c(e.get("playbackRates"))})),e.on("change:castActive",(function(t,i,n){i!==n&&(i?(o.removeMenu("audioTracks"),o.removeMenu("quality"),o.removeMenu("playbackRates")):(u(e.get("audioTracks")),r(e.get("levels")),c(e.get("playbackRates"))))}),o),e.on("change:streamType",(function(){c(e.get("playbackRates"))}),o),o},wi=i(58),gi=i(35),ji=i(12),bi=function(t,e,i,n){var o=Object(l.e)('
    '),r=!1,s=null,c=!1,u=function(t){/jw-info/.test(t.target.className)||h.close()},d=function(){var n,a,s,c,u,d=p("jw-info-close",(function(){h.close()}),e.get("localization").close,[dt("close")]);d.show(),Object(l.m)(o,d.element()),a=o.querySelector(".jw-info-title"),s=o.querySelector(".jw-info-duration"),c=o.querySelector(".jw-info-description"),u=o.querySelector(".jw-info-clientid"),e.change("playlistItem",(function(t,e){var i=e.description,n=e.title;Object(l.q)(c,i||""),Object(l.q)(a,n||"Unknown Title")})),e.change("duration",(function(t,i){var n="";switch(e.get("streamType")){case"LIVE":n="Live";break;case"DVR":n="DVR";break;default:i&&(n=Object(vt.timeFormat)(i))}s.textContent=n}),h),u.textContent=(n=i.getPlugin("jwpsrv"))&&"function"==typeof n.doNotTrackUser&&n.doNotTrackUser()?"":"Client ID: ".concat(function(){try{return window.localStorage.jwplayerLocalId}catch(t){return"none"}}()),t.appendChild(o),r=!0};var h={open:function(){r||d(),document.addEventListener("click",u),c=!0;var t=e.get("state");t===a.pb&&i.pause("infoOverlayInteraction"),s=t,n(!0)},close:function(){document.removeEventListener("click",u),c=!1,e.get("state")===a.ob&&s===a.pb&&i.play("infoOverlayInteraction"),s=null,n(!1)},destroy:function(){this.close(),e.off(null,null,this)}};return Object.defineProperties(h,{visible:{enumerable:!0,get:function(){return c}}}),h};var mi=function(t,e,i){var n,o=!1,r=null,s=i.get("localization").shortcuts,c=Object(l.e)(function(t,e){var i=t.map((function(t){return'
    '+''.concat(t.description,"")+''.concat(t.key,"")+"
    "})).join("");return'
    ')+'Press shift question mark to access a list of keyboard shortcuts
    '+''.concat(e,"")+'
    '+"".concat(i)+"
    "}(function(t){var e=t.playPause,i=t.volumeToggle,n=t.fullscreenToggle,o=t.seekPercent,a=t.increaseVolume,r=t.decreaseVolume,s=t.seekForward,l=t.seekBackward;return[{key:t.spacebar,description:e},{key:"↑",description:a},{key:"↓",description:r},{key:"→",description:s},{key:"←",description:l},{key:"c",description:t.captionsToggle},{key:"f",description:n},{key:"m",description:i},{key:"0-9",description:o}]}(s),s.keyboardShortcuts)),d={reason:"settingsInteraction"},h=new u.a(c.querySelector(".jw-switch")),f=function(){h.el.setAttribute("aria-checked",i.get("enableShortcuts")),Object(l.a)(c,"jw-open"),r=i.get("state"),c.querySelector(".jw-shortcuts-close").focus(),document.addEventListener("click",g),o=!0,e.pause(d)},w=function(){Object(l.o)(c,"jw-open"),document.removeEventListener("click",g),t.focus(),o=!1,r===a.pb&&e.play(d)},g=function(t){/jw-shortcuts|jw-switch/.test(t.target.className)||w()},j=function(t){var e=t.currentTarget,n="true"!==e.getAttribute("aria-checked");e.setAttribute("aria-checked",n),i.set("enableShortcuts",n)};return n=p("jw-shortcuts-close",w,i.get("localization").close,[dt("close")]),Object(l.m)(c,n.element()),n.show(),t.appendChild(c),h.on("click tap enter",j),{el:c,open:f,close:w,destroy:function(){w(),h.destroy()},toggleVisibility:function(){o?w():f()}}},vi=function(t){return'
    ')+"
    "};function yi(t){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ki(t,e){for(var i=0;i16?n.activeTimeout=setTimeout(n.userInactiveTimeout,t):n.playerContainer.querySelector(".jw-tab-focus")?n.resetActiveTimeout():n.userInactive()},n}var i,n,r;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Ii(t,e)}(e,t),i=e,(n=[{key:"resetActiveTimeout",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.inactiveTime=0}},{key:"enable",value:function(t,e){var i=this,n=this.context.createElement("div");n.className="jw-controls jw-reset",this.div=n;var r=this.context.createElement("div");r.className="jw-controls-backdrop jw-reset",this.backdrop=r,this.logo=this.playerContainer.querySelector(".jw-logo");var c=e.get("touchMode"),u=function(){(e.get("isFloating")?i.wrapperElement:i.playerContainer).focus()};if(!this.displayContainer){var d=new Oe(e,t);d.buttons.display.on("click tap enter",(function(){i.trigger(a.p),i.userActive(1e3),t.playToggle(Pi()),u()})),this.div.appendChild(d.element()),this.displayContainer=d}this.infoOverlay=new bi(n,e,t,(function(t){Object(l.v)(i.div,"jw-info-open",t),t&&i.div.querySelector(".jw-info-close").focus()})),o.OS.mobile||(this.shortcutsTooltip=new mi(this.wrapperElement,t,e)),this.rightClickMenu=new Ve(this.infoOverlay,this.shortcutsTooltip),c?(Object(l.a)(this.playerContainer,"jw-flag-touch"),this.rightClickMenu.setup(e,this.playerContainer,this.wrapperElement)):e.change("flashBlocked",(function(t,e){e?i.rightClickMenu.destroy():i.rightClickMenu.setup(t,i.playerContainer,i.wrapperElement)}),this);var h=e.get("floating");if(h){var f=new Ci(n,e.get("localization").close);f.on(a.sb,(function(){return i.trigger("dismissFloating",{doNotForward:!0})})),!1!==h.dismissible&&Object(l.a)(this.playerContainer,"jw-floating-dismissible")}var w=this.controlbar=new de(t,e,this.playerContainer.querySelector(".jw-hidden-accessibility"));if(w.on(a.sb,(function(){return i.userActive()})),w.on("nextShown",(function(t){this.trigger("nextShown",t)}),this),w.on("adjustVolume",k,this),e.get("nextUpDisplay")&&!w.nextUpToolTip){var g=new Se(e,t,this.playerContainer);g.on("all",this.trigger,this),g.setup(this.context),w.nextUpToolTip=g,this.div.appendChild(g.element())}this.div.appendChild(w.element());var j=e.get("localization"),b=this.settingsMenu=fi(t,e.player,this.controlbar,j),m=null;this.controlbar.on("menuVisibility",(function(n){var o=n.visible,r=n.evt,s=e.get("state"),l={reason:"settingsInteraction"},c=i.controlbar.elements.settingsButton,d="keydown"===(r&&r.sourceEvent||r||{}).type,p=o||d?0:Li;i.userActive(p),m=s,Object(wi.a)(e.get("containerWidth"))<2&&(o&&s===a.pb?t.pause(l):o||s!==a.ob||m!==a.pb||t.play(l)),!o&&d&&c?c.element().focus():r&&u()})),b.on("menuVisibility",(function(t){return i.controlbar.trigger("menuVisibility",t)})),this.controlbar.on("settingsInteraction",(function(t,e,i){if(e)return b.defaultChild.toggle(i);b.children[t].toggle(i)})),o.OS.mobile?this.div.appendChild(b.el):(this.playerContainer.setAttribute("aria-describedby","jw-shortcuts-tooltip-explanation"),this.div.insertBefore(b.el,w.element()));var v=function(e){if(e.get("autostartMuted")){var n=function(){return i.unmuteAutoplay(t,e)},a=function(t,e){e||n()};o.OS.mobile&&(i.mute=p("jw-autostart-mute jw-off",n,e.get("localization").unmute,[dt("volume-0")]),i.mute.show(),i.div.appendChild(i.mute.element())),w.renderVolume(!0,e.get("volume")),Object(l.a)(i.playerContainer,"jw-flag-autostart"),e.on("change:autostartFailed",n,i),e.on("change:autostartMuted change:mute",a,i),i.muteChangeCallback=a,i.unmuteCallback=n}};function y(i){var n=0,o=e.get("duration"),a=e.get("position");if("DVR"===e.get("streamType")){var r=e.get("dvrSeekLimit");n=o,o=Math.max(a,-r)}var l=Object(s.a)(a+i,n,o);t.seek(l,Pi())}function k(i){var n=Object(s.a)(e.get("volume")+i,0,100);t.setVolume(n)}e.once("change:autostartMuted",v),v(e);var x=function(n){if(n.ctrlKey||n.metaKey)return!0;var o=!i.settingsMenu.visible,a=!0===e.get("enableShortcuts"),r=i.instreamState;if(a||-1!==Ai.indexOf(n.keyCode)){switch(n.keyCode){case 27:if(e.get("fullscreen"))t.setFullscreen(!1),i.playerContainer.blur(),i.userInactive();else{var s=t.getPlugin("related");s&&s.close({type:"escape"})}i.rightClickMenu.el&&i.rightClickMenu.hideMenuHandler(),i.infoOverlay.visible&&i.infoOverlay.close(),i.shortcutsTooltip&&i.shortcutsTooltip.close();break;case 13:case 32:if(document.activeElement.classList.contains("jw-switch")&&13===n.keyCode)return!0;t.playToggle(Pi());break;case 37:!r&&o&&y(-5);break;case 39:!r&&o&&y(5);break;case 38:o&&k(10);break;case 40:o&&k(-10);break;case 67:var l=t.getCaptionsList().length;if(l){var c=(t.getCurrentCaptions()+1)%l;t.setCurrentCaptions(c)}break;case 77:t.setMute();break;case 70:t.setFullscreen();break;case 191:i.shortcutsTooltip&&i.shortcutsTooltip.toggleVisibility();break;default:if(n.keyCode>=48&&n.keyCode<=59){var u=(n.keyCode-48)/10*e.get("duration");t.seek(u,Pi())}}return/13|32|37|38|39|40/.test(n.keyCode)?(n.preventDefault(),!1):void 0}};this.playerContainer.addEventListener("keydown",x),this.keydownCallback=x;var T=function(t){switch(t.keyCode){case 9:var e=i.playerContainer.contains(t.target)?0:Li;i.userActive(e);break;case 32:t.preventDefault()}};this.playerContainer.addEventListener("keyup",T),this.keyupCallback=T;var O=function(t){var e=t.relatedTarget||document.querySelector(":focus");e&&(i.playerContainer.contains(e)||i.userInactive())};this.playerContainer.addEventListener("blur",O,!0),this.blurCallback=O;var C=function t(){"jw-shortcuts-tooltip-explanation"===i.playerContainer.getAttribute("aria-describedby")&&i.playerContainer.removeAttribute("aria-describedby"),i.playerContainer.removeEventListener("blur",t,!0)};this.shortcutsTooltip&&(this.playerContainer.addEventListener("blur",C,!0),this.onRemoveShortcutsDescription=C),this.userActive(),this.addControls(),this.addBackdrop(),e.set("controlsEnabled",!0)}},{key:"addControls",value:function(){this.wrapperElement.appendChild(this.div)}},{key:"disable",value:function(t){var e=this.nextUpToolTip,i=this.settingsMenu,n=this.infoOverlay,o=this.controlbar,a=this.rightClickMenu,r=this.shortcutsTooltip,s=this.playerContainer,c=this.div;clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.off(),t.off(null,null,this),t.set("controlsEnabled",!1),c.parentNode&&(Object(l.o)(s,"jw-flag-touch"),c.parentNode.removeChild(c)),o&&o.destroy(),a&&a.destroy(),this.keydownCallback&&s.removeEventListener("keydown",this.keydownCallback),this.keyupCallback&&s.removeEventListener("keyup",this.keyupCallback),this.blurCallback&&s.removeEventListener("blur",this.blurCallback),this.onRemoveShortcutsDescription&&s.removeEventListener("blur",this.onRemoveShortcutsDescription),this.displayContainer&&this.displayContainer.destroy(),e&&e.destroy(),i&&i.destroy(),n&&n.destroy(),r&&r.destroy(),this.removeBackdrop()}},{key:"controlbarHeight",value:function(){return this.dimensions.cbHeight||(this.dimensions.cbHeight=this.controlbar.element().clientHeight),this.dimensions.cbHeight}},{key:"element",value:function(){return this.div}},{key:"resize",value:function(){this.dimensions={}}},{key:"unmuteAutoplay",value:function(t,e){var i=!e.get("autostartFailed"),n=e.get("mute");i?n=!1:e.set("playOnViewable",!1),this.muteChangeCallback&&(e.off("change:autostartMuted change:mute",this.muteChangeCallback),this.muteChangeCallback=null),this.unmuteCallback&&(e.off("change:autostartFailed",this.unmuteCallback),this.unmuteCallback=null),e.set("autostartFailed",void 0),e.set("autostartMuted",void 0),t.setMute(n),this.controlbar.renderVolume(n,e.get("volume")),this.mute&&this.mute.hide(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.userActive()}},{key:"mouseMove",value:function(t){var e=this.controlbar.element().contains(t.target),i=this.controlbar.nextUpToolTip&&this.controlbar.nextUpToolTip.element().contains(t.target),n=this.logo&&this.logo.contains(t.target),o=e||i||n?0:Li;this.userActive(o)}},{key:"userActive",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Li;t>0?(this.inactiveTime=Object(c.a)()+t,-1===this.activeTimeout&&(this.activeTimeout=setTimeout(this.userInactiveTimeout,t))):this.resetActiveTimeout(),this.showing||(Object(l.o)(this.playerContainer,"jw-flag-user-inactive"),this.showing=!0,this.trigger("userActive"))}},{key:"userInactive",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.settingsMenu.visible||(this.inactiveTime=0,this.showing=!1,Object(l.a)(this.playerContainer,"jw-flag-user-inactive"),this.trigger("userInactive"))}},{key:"addBackdrop",value:function(){var t=this.instreamState?this.div:this.wrapperElement.querySelector(".jw-captions");this.wrapperElement.insertBefore(this.backdrop,t)}},{key:"removeBackdrop",value:function(){var t=this.backdrop.parentNode;t&&t.removeChild(this.backdrop)}},{key:"setupInstream",value:function(){this.instreamState=!0,this.userActive(),this.addBackdrop(),this.settingsMenu&&this.settingsMenu.close(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","-1")}},{key:"destroyInstream",value:function(t){this.instreamState=null,this.addBackdrop(),t.get("autostartMuted")&&Object(l.a)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","0")}}])&&Mi(i.prototype,n),r&&Mi(i,r),e}(r.a)},function(t,e,i){"use strict";i.r(e);var n=i(0),o=i(12),a=i(50),r=i(36);var s=i(44),l=i(51),c=i(26),u=i(25),d=i(3),p=i(46),h=i(2),f=i(7),w=i(34);function g(t){var e=!1;return{async:function(){var i=this,n=arguments;return Promise.resolve().then((function(){if(!e)return t.apply(i,n)}))},cancel:function(){e=!0},cancelled:function(){return e}}}var j=i(1);function b(t){return function(e,i){var o=t.mediaModel,a=Object(n.g)({},i,{type:e});switch(e){case d.T:if(o.get(d.T)===i.mediaType)return;o.set(d.T,i.mediaType);break;case d.U:return void o.set(d.U,Object(n.g)({},i));case d.M:if(i[e]===t.model.getMute())return;break;case d.bb:i.newstate===d.mb&&(t.thenPlayPromise.cancel(),o.srcReset());var r=o.attributes.mediaState;o.attributes.mediaState=i.newstate,o.trigger("change:mediaState",o,i.newstate,r);break;case d.F:return t.beforeComplete=!0,t.trigger(d.B,a),void(t.attached&&!t.background&&t._playbackComplete());case d.G:o.get("setup")?(t.thenPlayPromise.cancel(),o.srcReset()):(e=d.tb,a.code+=1e5);break;case d.K:a.metadataType||(a.metadataType="unknown");var s=i.duration;Object(n.u)(s)&&(o.set("seekRange",i.seekRange),o.set("duration",s));break;case d.D:o.set("buffer",i.bufferPercent);case d.S:o.set("seekRange",i.seekRange),o.set("position",i.position),o.set("currentTime",i.currentTime);var l=i.duration;Object(n.u)(l)&&o.set("duration",l),e===d.S&&Object(n.r)(t.item.starttime)&&delete t.item.starttime;break;case d.R:var c=t.mediaElement;c&&c.paused&&o.set("mediaState","paused");break;case d.I:o.set(d.I,i.levels);case d.J:var u=i.currentQuality,p=i.levels;u>-1&&p.length>1&&o.set("currentLevel",parseInt(u));break;case d.f:o.set(d.f,i.tracks);case d.g:var h=i.currentTrack,f=i.tracks;h>-1&&f.length>0&&h=Math.max(l,p.a)&&(t.preloadNextItem(),v=!0)}function L(t){var e={};b.tag&&(e.tag=b.tag),this.trigger(d.F,e),A.call(this,t)}function A(t){g={},a&&w+10?t:null,f&&f.model.set("skipOffset",s)}};Object(n.g)(lt.prototype,f.a);var ct=lt,ut=i(66),dt=i(63),pt=function(t){var e=this,i=[],n={},o=0,a=0;function r(t){if(t.data=t.data||[],t.name=t.label||t.name||t.language,t._id=Object(dt.a)(t,i.length),!t.name){var e=Object(dt.b)(t,o);t.name=e.label,o=e.unknownCount}n[t._id]=t,i.push(t)}function s(){for(var t=[{id:"off",label:"Off"}],e=0;e')+'
    '},wt=i(35),gt=44,jt=function(t){var e=t.get("height");if(t.get("aspectratio"))return!1;if("string"==typeof e&&e.indexOf("%")>-1)return!1;var i=1*e||NaN;return!!(i=isNaN(i)?t.get("containerHeight"):i)&&(i&&i<=gt)},bt=i(54);function mt(t,e){if(t.get("fullscreen"))return 1;if(!t.get("activeTab"))return 0;if(t.get("isFloating"))return 1;var i=t.get("intersectionRatio");return void 0===i&&(i=function(t){var e=document.documentElement,i=document.body,n={top:0,left:0,right:e.clientWidth||i.clientWidth,width:e.clientWidth||i.clientWidth,bottom:e.clientHeight||i.clientHeight,height:e.clientHeight||i.clientHeight};if(!i.contains(t))return 0;if("none"===window.getComputedStyle(t).display)return 0;var o=vt(t);if(!o)return 0;var a=o,r=t.parentNode,s=!1;for(;!s;){var l=null;if(r===i||r===e||1!==r.nodeType?(s=!0,l=n):"visible"!==window.getComputedStyle(r).overflow&&(l=vt(r)),l&&(c=l,u=a,d=void 0,p=void 0,h=void 0,f=void 0,w=void 0,g=void 0,d=Math.max(c.top,u.top),p=Math.min(c.bottom,u.bottom),h=Math.max(c.left,u.left),f=Math.min(c.right,u.right),g=p-d,!(a=(w=f-h)>=0&&g>=0&&{top:d,bottom:p,left:h,right:f,width:w,height:g})))return 0;r=r.parentNode}var c,u,d,p,h,f,w,g;var j=o.width*o.height,b=a.width*a.height;return j?b/j:0}(e),window.top!==window.self&&i)?0:i}function vt(t){try{return t.getBoundingClientRect()}catch(t){}}var yt=i(49),kt=i(42),xt=i(58),Tt=i(10);var Ot=i(32),Ct=i(5),_t=i(6),Mt=["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],St=function(t,e,i){for(var n=t.requestFullscreen||t.webkitRequestFullscreen||t.webkitRequestFullScreen||t.mozRequestFullScreen||t.msRequestFullscreen,o=e.exitFullscreen||e.webkitExitFullscreen||e.webkitCancelFullScreen||e.mozCancelFullScreen||e.msExitFullscreen,a=!(!n||!o),r=Mt.length;r--;)e.addEventListener(Mt[r],i);return{events:Mt,supportsDomFullscreen:function(){return a},requestFullscreen:function(){n.call(t,{navigationUI:"hide"})},exitFullscreen:function(){null!==this.fullscreenElement()&&o.apply(e)},fullscreenElement:function(){var t=e.fullscreenElement,i=e.webkitCurrentFullScreenElement,n=e.mozFullScreenElement,o=e.msFullscreenElement;return null===t?t:t||i||n||o},destroy:function(){for(var t=Mt.length;t--;)e.removeEventListener(Mt[t],i)}}},Et=i(40);function It(t,e){for(var i=0;i')},Rt={linktarget:"_blank",margin:8,hide:!1,position:"top-right"};function zt(t){var e,i;Object(n.g)(this,f.a);var o=new Image;this.setup=function(){(i=Object(n.g)({},Rt,t.get("logo"))).position=i.position||Rt.position,i.hide="true"===i.hide.toString(),i.file&&"control-bar"!==i.position&&(e||(e=Object(Ct.e)(Pt(i.position,i.hide))),t.set("logo",i),o.onload=function(){var n=this.height,o=this.width,a={backgroundImage:'url("'+this.src+'")'};if(i.margin!==Rt.margin){var r=/(\w+)-(\w+)/.exec(i.position);3===r.length&&(a["margin-"+r[1]]=i.margin,a["margin-"+r[2]]=i.margin)}var s=.15*t.get("containerHeight"),l=.15*t.get("containerWidth");if(n>s||o>l){var c=o/n;l/s>c?(n=s,o=s*c):(o=l,n=l/c)}a.width=Math.round(o),a.height=Math.round(n),Object(Tt.d)(e,a),t.set("logoWidth",a.width)},o.src=i.file,i.link&&(e.setAttribute("tabindex","0"),e.setAttribute("aria-label",t.get("localization").logo)),this.ui=new Et.a(e).on("click tap enter",(function(t){t&&t.stopPropagation&&t.stopPropagation(),this.trigger(d.A,{link:i.link,linktarget:i.linktarget})}),this))},this.setContainer=function(t){e&&t.appendChild(e)},this.element=function(){return e},this.position=function(){return i.position},this.destroy=function(){o.onload=null,this.ui&&this.ui.destroy()}}var Bt=function(t){this.model=t,this.image=null};Object(n.g)(Bt.prototype,{setup:function(t){this.el=t},setImage:function(t){var e=this.image;e&&(e.onload=null),this.image=null;var i="";"string"==typeof t&&(i='url("'+t+'")',(e=this.image=new Image).src=t),Object(Tt.d)(this.el,{backgroundImage:i})},resize:function(t,e,i){if("uniform"===i){if(t&&(this.playerAspectRatio=t/e),!this.playerAspectRatio||!this.image||"complete"!==(s=this.model.get("state"))&&"idle"!==s&&"error"!==s&&"buffering"!==s)return;var n=this.image,o=null;if(n){if(0===n.width){var a=this;return void(n.onload=function(){a.resize(t,e,i)})}var r=n.width/n.height;Math.abs(this.playerAspectRatio-r)<.09&&(o="cover")}Object(Tt.d)(this.el,{backgroundSize:o})}var s},element:function(){return this.el}});var Vt=Bt,Nt=function(t){this.model=t.player};Object(n.g)(Nt.prototype,{hide:function(){Object(Tt.d)(this.el,{display:"none"})},show:function(){Object(Tt.d)(this.el,{display:""})},setup:function(t){this.el=t;var e=this.el.getElementsByTagName("div");this.title=e[0],this.description=e[1],this.model.on("change:logoWidth",this.update,this),this.model.change("playlistItem",this.playlistItem,this)},update:function(t){var e={},i=t.get("logo");if(i){var n=1*(""+i.margin).replace("px",""),o=t.get("logoWidth")+(isNaN(n)?0:n+10);"top-left"===i.position?e.paddingLeft=o:"top-right"===i.position&&(e.paddingRight=o)}Object(Tt.d)(this.el,e)},playlistItem:function(t,e){if(e)if(t.get("displaytitle")||t.get("displaydescription")){var i="",n="";e.title&&t.get("displaytitle")&&(i=e.title),e.description&&t.get("displaydescription")&&(n=e.description),this.updateText(i,n)}else this.hide()},updateText:function(t,e){Object(Ct.q)(this.title,t),Object(Ct.q)(this.description,e),this.title.firstChild||this.description.firstChild?this.show():this.hide()},element:function(){return this.el}});var Ht=Nt;function Ft(t,e){for(var i=0;it)}if(e.get("controls")){var r=jt(e);Object(Ct.v)(u,"jw-flag-audio-player",r),e.set("audioMode",r)}}function z(){e.set("visibility",mt(e,u))}this.updateBounds=function(){Object(kt.a)(k);var t=e.get("isFloating")?p:u,i=document.body.contains(t),n=Object(Ct.c)(t),r=Math.round(n.width),s=Math.round(n.height);if(S=Object(Ct.c)(u),r===o&&s===a)return o&&a||A(),void e.set("inDom",i);r&&s||o&&a||A(),(r||s||i)&&(e.set("containerWidth",r),e.set("containerHeight",s)),e.set("inDom",i),i&&bt.a.observe(u)},this.updateStyles=function(){var t=e.get("containerWidth"),i=e.get("containerHeight");R(t,i),I&&I.resize(t,i),$(t,i),v.resize(),T&&F()},this.checkResized=function(){var t=e.get("containerWidth"),i=e.get("containerHeight"),n=e.get("isFloating");if(t!==o||i!==a){this.resizeListener||(this.resizeListener=new Ut.a(p,this,e)),o=t,a=i,l.trigger(d.hb,{width:t,height:i});var s=Object(xt.a)(t);E!==s&&(E=s,l.trigger(d.j,{breakpoint:E}))}n!==r&&(r=n,l.trigger(d.x,{floating:n}),z())},this.responsiveListener=A,this.setup=function(){j.setup(u.querySelector(".jw-preview")),b.setup(u.querySelector(".jw-title")),(i=new zt(e)).setup(),i.setContainer(p),i.on(d.A,J),v.setup(u.id,e.get("captions")),b.element().parentNode.insertBefore(v.element(),b.element()),O=function(t,e,i){var n=new Lt(e,i),o=e.get("controls");n.on({click:function(){l.trigger(d.p),I&&(ct()?I.settingsMenu.close():ut()?I.infoOverlay.close():t.playToggle({reason:"interaction"}))},tap:function(){l.trigger(d.p),ct()&&I.settingsMenu.close(),ut()&&I.infoOverlay.close();var i=e.get("state");if(o&&(i===d.mb||i===d.kb||e.get("instream")&&i===d.ob)&&t.playToggle({reason:"interaction"}),o&&i===d.ob){if(e.get("instream")||e.get("castActive")||"audio"===e.get("mediaType"))return;Object(Ct.v)(u,"jw-flag-controls-hidden"),l.dismissible&&Object(Ct.v)(u,"jw-floating-dismissible",Object(Ct.i)(u,"jw-flag-controls-hidden")),v.renderCues(!0)}else I&&(I.showing?I.userInactive():I.userActive())},doubleClick:function(){return I&&t.setFullscreen()}}),Wt||(u.addEventListener("mousemove",W),u.addEventListener("mouseover",Q),u.addEventListener("mouseout",Y));return n}(t,e,w),_=new Et.a(u).on("click",(function(){})),C=St(u,document,et),e.on("change:hideAdsControls",(function(t,e){Object(Ct.v)(u,"jw-flag-ads-hide-controls",e)})),e.on("change:scrubbing",(function(t,e){Object(Ct.v)(u,"jw-flag-dragging",e)})),e.on("change:playRejected",(function(t,e){Object(Ct.v)(u,"jw-flag-play-rejected",e)})),e.on(d.X,tt),e.on("change:".concat(d.U),(function(){$(),v.resize()})),e.player.on("change:errorEvent",at),e.change("stretching",X);var n=e.get("width"),o=e.get("height"),a=G(n,o);Object(Tt.d)(u,a),e.change("aspectratio",K),R(n,o),e.get("controls")||(Object(Ct.a)(u,"jw-flag-controls-hidden"),Object(Ct.o)(u,"jw-floating-dismissible")),Qt&&Object(Ct.a)(u,"jw-ie");var r=e.get("skin")||{};r.name&&Object(Ct.p)(u,/jw-skin-\S+/,"jw-skin-"+r.name);var s=function(t){t||(t={});var e=t.active,i=t.inactive,n=t.background,o={};return o.controlbar=function(t){if(t||e||i||n){var o={};return t=t||{},o.iconsActive=t.iconsActive||e,o.icons=t.icons||i,o.text=t.text||i,o.background=t.background||n,o}}(t.controlbar),o.timeslider=function(t){if(t||e){var i={};return t=t||{},i.progress=t.progress||e,i.rail=t.rail,i}}(t.timeslider),o.menus=function(t){if(t||e||i||n){var o={};return t=t||{},o.text=t.text||i,o.textActive=t.textActive||e,o.background=t.background||n,o}}(t.menus),o.tooltips=function(t){if(t||i||n){var e={};return t=t||{},e.text=t.text||i,e.background=t.background||n,e}}(t.tooltips),o}(r);!function(t,e){var i;function n(e,i,n,o){if(n){e=Object(h.f)(e,"#"+t+(o?"":" "));var a={};a[i]=n,Object(Tt.b)(e.join(", "),a,t)}}e&&(e.controlbar&&function(e){n([".jw-controlbar .jw-icon-inline.jw-text",".jw-title-primary",".jw-title-secondary"],"color",e.text),e.icons&&(n([".jw-button-color:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:not(.jw-icon-cast)"],"color",e.icons),n([".jw-display-icon-container .jw-button-color"],"color",e.icons),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.icons,"}"),t));e.iconsActive&&(n([".jw-display-icon-container .jw-button-color:hover",".jw-display-icon-container .jw-button-color:focus"],"color",e.iconsActive),n([".jw-button-color.jw-toggle:not(.jw-icon-cast)",".jw-button-color:hover:not(.jw-icon-cast)",".jw-button-color:focus:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:hover:not(.jw-icon-cast)"],"color",e.iconsActive),n([".jw-svg-icon-buffer"],"fill",e.icons),Object(Tt.b)("#".concat(t," .jw-icon-cast:hover google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:focus google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher.jw-off:focus"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher:focus"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:hover google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:focus google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t));n([" .jw-settings-topbar",":not(.jw-state-idle) .jw-controlbar",".jw-flag-audio-player .jw-controlbar"],"background",e.background,!0)}(e.controlbar),e.timeslider&&function(t){var e=t.progress;"none"!==e&&(n([".jw-progress",".jw-knob"],"background-color",e),n([".jw-buffer"],"background-color",Object(Tt.c)(e,50)));n([".jw-rail"],"background-color",t.rail),n([".jw-background-color.jw-slider-time",".jw-slider-time .jw-cue"],"background-color",t.background)}(e.timeslider),e.menus&&(n([".jw-option",".jw-toggle.jw-off",".jw-skip .jw-skip-icon",".jw-nextup-tooltip",".jw-nextup-close",".jw-settings-content-item",".jw-related-title"],"color",(i=e.menus).text),n([".jw-option.jw-active-option",".jw-option:not(.jw-active-option):hover",".jw-option:not(.jw-active-option):focus",".jw-settings-content-item:hover",".jw-nextup-tooltip:hover",".jw-nextup-tooltip:focus",".jw-nextup-close:hover"],"color",i.textActive),n([".jw-nextup",".jw-settings-menu"],"background",i.background)),e.tooltips&&function(t){n([".jw-skip",".jw-tooltip .jw-text",".jw-time-tip .jw-text"],"background-color",t.background),n([".jw-time-tip",".jw-tooltip"],"color",t.background),n([".jw-skip"],"border","none"),n([".jw-skip .jw-text",".jw-skip .jw-icon",".jw-time-tip .jw-text",".jw-tooltip .jw-text"],"color",t.text)}(e.tooltips),e.menus&&function(e){if(e.textActive){var i={color:e.textActive,borderColor:e.textActive,stroke:e.textActive};Object(Tt.b)("#".concat(t," .jw-color-active"),i,t),Object(Tt.b)("#".concat(t," .jw-color-active-hover:hover"),i,t)}if(e.text){var n={color:e.text,borderColor:e.text,stroke:e.text};Object(Tt.b)("#".concat(t," .jw-color-inactive"),n,t),Object(Tt.b)("#".concat(t," .jw-color-inactive-hover:hover"),n,t)}}(e.menus))}(e.get("id"),s),e.set("mediaContainer",w),e.set("iFrame",m.Features.iframe),e.set("activeTab",Object(yt.a)()),e.set("touchMode",Wt&&("string"==typeof o||o>=gt)),bt.a.add(this),e.get("enableGradient")&&!Qt&&Object(Ct.a)(u,"jw-ab-drop-shadow"),this.isSetup=!0,e.trigger("viewSetup",u);var c=document.body.contains(u);c&&bt.a.observe(u),e.set("inDom",c)},this.init=function(){this.updateBounds(),e.on("change:fullscreen",Z),e.on("change:activeTab",z),e.on("change:fullscreen",z),e.on("change:intersectionRatio",z),e.on("change:visibility",U),e.on("instreamMode",(function(t){t?dt():pt()})),z(),1!==bt.a.size()||e.get("visibility")||U(e,1,0);var t=e.player;e.change("state",rt),t.change("controls",D),e.change("streamType",nt),e.change("mediaType",ot),t.change("playlistItem",(function(t,e){lt(t,e)})),o=a=null,T&&Wt&&bt.a.addScrollHandler(F),this.checkResized()};var B,V=62,N=!0;function H(){var t=e.get("isFloating"),i=S.top0&&void 0!==arguments[0])||arguments[0],e={x:0,y:0,width:o||0,height:a||0};return I&&t&&(e.height-=I.controlbarHeight()),e},this.setCaptions=function(t){v.clear(),v.setup(e.get("id"),t),v.resize()},this.setIntersection=function(t){var i=Math.round(100*t.intersectionRatio)/100;e.set("intersectionRatio",i),T&&!L()&&(M=M||i>=.5)&&ht(i)},this.stopFloating=function(t,i){if(t&&(T=null,bt.a.removeScrollHandler(F)),Yt===u){Yt=null,e.set("isFloating",!1);var n=function(){Object(Ct.o)(u,"jw-flag-floating"),K(e,e.get("aspectratio")),Object(Tt.d)(u,{backgroundImage:null}),Object(Tt.d)(p,{maxWidth:null,width:null,height:null,left:null,right:null,top:null,bottom:null,margin:null,transform:null,transition:null,"transition-timing-function":null})};i?(Object(Tt.d)(p,{transform:"translateY(-".concat(V-S.top,"px)"),"transition-timing-function":"ease-out"}),setTimeout(n,150)):n(),g.disable(),A()}},this.destroy=function(){e.destroy(),bt.a.unobserve(u),bt.a.remove(this),this.isSetup=!1,this.off(),Object(kt.a)(k),clearTimeout(y),Yt===u&&(Yt=null),_&&(_.destroy(),_=null),C&&(C.destroy(),C=null),I&&I.disable(e),O&&(O.destroy(),u.removeEventListener("mousemove",W),u.removeEventListener("mouseout",Y),u.removeEventListener("mouseover",Q),O=null),v.destroy(),i&&(i.destroy(),i=null),Object(Tt.a)(e.get("id")),this.resizeListener&&(this.resizeListener.destroy(),delete this.resizeListener),T&&Wt&&bt.a.removeScrollHandler(F)}};function Kt(t,e,i){return(Kt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(t,e,i){var n=function(t,e){for(;!Object.prototype.hasOwnProperty.call(t,e)&&null!==(t=ee(t)););return t}(t,e);if(n){var o=Object.getOwnPropertyDescriptor(n,e);return o.get?o.get.call(i):o.value}})(t,e,i||t)}function Jt(t){return(Jt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Zt(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Gt(t,e){for(var i=0;ie&&t(),e=n}};function Oe(t,e){e.off(d.N,t._onPlayAttempt),e.off(d.fb,t._triggerFirstFrame),e.off(d.S,t._onTime),t.off("change:activeTab",t._onTabVisible)}var Ce=function(t,e){t.change("mediaModel",(function(t,i,n){t._qoeItem&&n&&t._qoeItem.end(n.get("mediaState")),t._qoeItem=new ye.a,t._qoeItem.getFirstFrame=function(){var t=this.between(d.N,d.H),e=this.between(xe,d.H);return e>0&&e0&&rt(e,t.tracks)}),O).on(d.F,(function(){Promise.resolve().then(at)}),O).on(d.G,O.triggerError,O),Ce(C,B),C.on(d.w,O.triggerError,O),C.on("change:state",(function(t,e,i){X()||K.call(T,t,e,i)}),this),C.on("change:castState",(function(t,e){O.trigger(d.m,e)})),C.on("change:fullscreen",(function(t,e){O.trigger(d.y,{fullscreen:e}),e&&t.set("playOnViewable",!1)})),C.on("change:volume",(function(t,e){O.trigger(d.V,{volume:e})})),C.on("change:mute",(function(t){O.trigger(d.M,{mute:t.getMute()})})),C.on("change:playbackRate",(function(t,e){O.trigger(d.ab,{playbackRate:e,position:t.get("position")})}));var V=function t(e,i){"clickthrough"!==i&&"interaction"!==i&&"external"!==i||(C.set("playOnViewable",!1),C.off("change:playReason change:pauseReason",t))};function N(t,e){Object(n.t)(e)||C.set("viewable",Math.round(e))}function H(){dt&&(!0!==C.get("autostart")||C.get("playOnViewable")||$("autostart"),dt.flush())}function F(t,e){O.trigger("viewable",{viewable:e}),D()}function D(){if((o.a[0]===e||1===C.get("viewable"))&&"idle"===C.get("state")&&!1===C.get("autostart"))if(!b.primed()&&m.OS.android){var t=b.getTestElement(),i=O.getMute();Promise.resolve().then((function(){return fe(t,{muted:i})})).then((function(){"idle"===C.get("state")&&B.preloadVideo()})).catch(Se)}else B.preloadVideo()}function q(t){O._instreamAdapter.noResume=!t,t||et({reason:"viewable"})}function U(t){t||(O.pause({reason:"viewable"}),C.set("playOnViewable",!t))}function W(t,e){var i=X();if(t.get("playOnViewable")){if(e){var n=t.get("autoPause").pauseAds,o=t.get("pauseReason");J()===d.mb?$("viewable"):i&&!n||"interaction"===o||Z({reason:"viewable"})}else m.OS.mobile&&!i&&(O.pause({reason:"autostart"}),C.set("playOnViewable",!0));m.OS.mobile&&i&&q(e)}}function Q(t,e){var i=t.get("state"),n=X(),o=t.get("playReason");n?t.get("autoPause").pauseAds?U(e):q(e):i===d.pb||i===d.jb?U(e):i===d.mb&&"playlist"===o&&t.once("change:state",(function(){U(e)}))}function X(){var t=O._instreamAdapter;return!!t&&t.getState()}function J(){var t=X();return t||C.get("state")}function Z(t){if(E.cancel(),M=!1,C.get("state")===d.lb)return Promise.resolve();var i=G(t);return C.set("playReason",i),X()?(e.pauseAd(!1,t),Promise.resolve()):(C.get("state")===d.kb&&(tt(!0),O.setItemIndex(0)),!_&&(_=!0,O.trigger(d.C,{playReason:i,startTime:t&&t.startTime?t.startTime:C.get("playlistItem").starttime}),_=!1,ve()&&!b.primed()&&b.prime(),"playlist"===i&&C.get("autoPause").viewability&&Q(C,C.get("viewable")),x)?(ve()&&!R&&C.get("mediaElement").load(),x=!1,k=null,Promise.resolve()):B.playVideo(i).then(b.played))}function G(t){return t&&t.reason?t.reason:"unknown"}function $(t){if(J()===d.mb){E=g(H);var e=C.get("advertising");(function(t,e){var i=e.cancelable,n=e.muted,o=void 0!==n&&n,a=e.allowMuted,r=void 0!==a&&a,s=e.timeout,l=void 0===s?1e4:s,c=t.getTestElement(),u=o?"muted":"".concat(r);be[u]||(be[u]=fe(c,{muted:o}).catch((function(t){if(!i.cancelled()&&!1===o&&r)return fe(c,{muted:o=!0});throw t})).then((function(){return o?(be[u]=null,ge):we})).catch((function(t){throw clearTimeout(d),be[u]=null,t.reason=je,t})));var d,p=be[u].then((function(t){if(clearTimeout(d),i.cancelled()){var e=new Error("Autoplay test was cancelled");throw e.reason="cancelled",e}return t})),h=new Promise((function(t,e){d=setTimeout((function(){be[u]=null;var t=new Error("Autoplay test timed out");t.reason="timeout",e(t)}),l)}));return Promise.race([p,h])})(b,{cancelable:E,muted:O.getMute(),allowMuted:!e||e.autoplayadsmuted}).then((function(e){return C.set("canAutoplay",e),e!==ge||O.getMute()||(C.set("autostartMuted",!0),ut(),C.once("change:autostartMuted",(function(t){t.off("change:viewable",W),O.trigger(d.M,{mute:C.getMute()})}))),O.getMute()&&C.get("enableDefaultCaptions")&&y.selectDefaultIndex(1),Z({reason:t}).catch((function(){O._instreamAdapter||C.set("autostartFailed",!0),k=null}))})).catch((function(t){if(C.set("canAutoplay",je),C.set("autostart",!1),!E.cancelled()){var e=Object(j.w)(t);O.trigger(d.h,{reason:t.reason,code:e,error:t})}}))}}function tt(t){if(E.cancel(),dt.empty(),X()){var e=O._instreamAdapter;return e&&(e.noResume=!0),void(k=function(){return B.stopVideo()})}k=null,!t&&(M=!0),_&&(x=!0),C.set("errorEvent",void 0),B.stopVideo()}function et(t){var e=G(t);C.set("pauseReason",e),C.set("playOnViewable","viewable"===e)}function it(t){k=null,E.cancel();var i=X();if(i&&i!==d.ob)return et(t),void e.pauseAd(!0,t);switch(C.get("state")){case d.lb:return;case d.pb:case d.jb:et(t),B.pause();break;default:_&&(x=!0)}}function nt(t,e){tt(!0),O.setItemIndex(t),O.play(e)}function ot(t){nt(C.get("item")+1,t)}function at(){O.completeCancelled()||(k=O.completeHandler,O.shouldAutoAdvance()?O.nextItem():C.get("repeat")?ot({reason:"repeat"}):(m.OS.iOS&<(!1),C.set("playOnViewable",!1),C.set("state",d.kb),O.trigger(d.cb,{})))}function rt(t,e){t=parseInt(t,10)||0,C.persistVideoSubtitleTrack(t,e),B.subtitles=t,O.trigger(d.k,{tracks:st(),track:t})}function st(){return y.getCaptionsList()}function lt(t){Object(n.n)(t)||(t=!C.get("fullscreen")),C.set("fullscreen",t),O._instreamAdapter&&O._instreamAdapter._adModel&&O._instreamAdapter._adModel.set("fullscreen",t)}function ut(){B.mute=C.getMute(),B.volume=C.get("volume")}C.on("change:playReason change:pauseReason",V),O.on(d.c,(function(t){return V(0,t.playReason)})),O.on(d.b,(function(t){return V(0,t.pauseReason)})),C.on("change:scrubbing",(function(t,e){e?(S=C.get("state")!==d.ob,it()):S&&Z({reason:"interaction"})})),C.on("change:captionsList",(function(t,e){O.trigger(d.l,{tracks:e,track:C.get("captionsIndex")||0})})),C.on("change:mediaModel",(function(t,e){var i=this;t.set("errorEvent",void 0),e.change("mediaState",(function(e,i){var n;t.get("errorEvent")||t.set(d.bb,(n=i)===d.nb||n===d.qb?d.jb:n)}),this),e.change("duration",(function(e,i){if(0!==i){var n=t.get("minDvrWindow"),o=Object(me.b)(i,n);t.setStreamType(o)}}),this);var n=t.get("item")+1,o="autoplay"===(t.get("related")||{}).oncomplete,a=t.get("playlist")[n];if((a||o)&&R){e.on("change:position",(function t(n,r){var s=a&&!a.daiSetting,l=e.get("duration");s&&r&&l>0&&r>=l-p.b?(e.off("change:position",t,i),B.backgroundLoad(a)):o&&(a=C.get("nextUp"))}),this)}})),(y=new ht(C)).on("all",P,O),z.on("viewSetup",(function(t){Object(a.b)(T,t)})),this.playerReady=function(){v.once(d.hb,(function(){try{!function(){C.change("visibility",N),L.off(),O.trigger(d.gb,{setupTime:0}),C.change("playlist",(function(t,e){if(e.length){var i={playlist:e},o=C.get("feedData");o&&(i.feedData=Object(n.g)({},o)),O.trigger(d.eb,i)}})),C.change("playlistItem",(function(t,e){if(e){var i=e.title,n=e.image;if("mediaSession"in navigator&&window.MediaMetadata&&(i||n))try{navigator.mediaSession.metadata=new window.MediaMetadata({title:i,artist:window.location.hostname,artwork:[{src:n||""}]})}catch(t){}t.set("cues",[]),O.trigger(d.db,{index:C.get("item"),item:e})}})),L.flush(),L.destroy(),L=null,C.change("viewable",F),C.change("viewable",W),C.get("autoPause").viewability?C.change("viewable",Q):C.once("change:autostartFailed change:mute",(function(t){t.off("change:viewable",W)}));H(),C.on("change:itemReady",(function(t,e){e&&dt.flush()}))}()}catch(t){O.triggerError(Object(j.v)(j.m,j.a,t))}})),v.init()},this.preload=D,this.load=function(t,e){var i,n=O._instreamAdapter;switch(n&&(n.noResume=!0),O.trigger("destroyPlugin",{}),tt(!0),E.cancel(),E=g(H),I.cancel(),ve()&&b.prime(),_e(t)){case"string":C.attributes.item=0,C.attributes.itemReady=!1,I=g((function(t){if(t)return O.updatePlaylist(Object(c.a)(t.playlist),t)})),i=function(t){var e=this;return new Promise((function(i,n){var o=new l.a;o.on(d.eb,(function(t){i(t)})),o.on(d.w,n,e),o.load(t)}))}(t).then(I.async);break;case"object":C.attributes.item=0,i=O.updatePlaylist(Object(c.a)(t),e||{});break;case"number":i=O.setItemIndex(t);break;default:return}i.catch((function(t){O.triggerError(Object(j.u)(t,j.c))})),i.then(E.async).catch(Se)},this.play=function(t){return Z(t).catch(Se)},this.pause=it,this.seek=function(t,e){var i=C.get("state");if(i!==d.lb){B.position=t;var n=i===d.mb;C.get("scrubbing")||!n&&i!==d.kb||(n&&((e=e||{}).startTime=t),this.play(e))}},this.stop=tt,this.playlistItem=nt,this.playlistNext=ot,this.playlistPrev=function(t){nt(C.get("item")-1,t)},this.setCurrentCaptions=rt,this.setCurrentQuality=function(t){B.quality=t},this.setFullscreen=lt,this.getCurrentQuality=function(){return B.quality},this.getQualityLevels=function(){return B.qualities},this.setCurrentAudioTrack=function(t){B.audioTrack=t},this.getCurrentAudioTrack=function(){return B.audioTrack},this.getAudioTracks=function(){return B.audioTracks},this.getCurrentCaptions=function(){return y.getCurrentIndex()},this.getCaptionsList=st,this.getVisualQuality=function(){var t=this._model.get("mediaModel");return t?t.get(d.U):null},this.getConfig=function(){return this._model?this._model.getConfiguration():void 0},this.getState=J,this.next=Se,this.completeHandler=at,this.completeCancelled=function(){return(t=C.get("state"))!==d.mb&&t!==d.kb&&t!==d.lb||!!M&&(M=!1,!0);var t},this.shouldAutoAdvance=function(){return C.get("item")!==C.get("playlist").length-1},this.nextItem=function(){ot({reason:"playlist"})},this.setConfig=function(t){!function(t,e){var i=t._model,n=i.attributes;e.height&&(e.height=Object(r.b)(e.height),e.width=e.width||n.width),e.width&&(e.width=Object(r.b)(e.width),e.aspectratio?(n.width=e.width,delete e.width):e.height=n.height),e.width&&e.height&&!e.aspectratio&&t._view.resize(e.width,e.height),Object.keys(e).forEach((function(o){var a=e[o];if(void 0!==a)switch(o){case"aspectratio":i.set(o,Object(r.a)(a,n.width));break;case"autostart":!function(t,e,i){t.setAutoStart(i),"idle"===t.get("state")&&!0===i&&e.play({reason:"autostart"})}(i,t,a);break;case"mute":t.setMute(a);break;case"volume":t.setVolume(a);break;case"playbackRateControls":case"playbackRates":case"repeat":case"stretching":i.set(o,a)}}))}(O,t)},this.setItemIndex=function(t){B.stopVideo();var e=C.get("playlist").length;return(t=(parseInt(t,10)||0)%e)<0&&(t+=e),B.setActiveItem(t).catch((function(t){t.code>=151&&t.code<=162&&(t=Object(j.u)(t,j.e)),T.triggerError(Object(j.v)(j.k,j.d,t))}))},this.detachMedia=function(){if(_&&(x=!0),C.get("autoPause").viewability&&Q(C,C.get("viewable")),!R)return B.setAttached(!1);B.backgroundActiveMedia()},this.attachMedia=function(){R?B.restoreBackgroundMedia():B.setAttached(!0),"function"==typeof k&&k()},this.routeEvents=function(t){return B.routeEvents(t)},this.forwardEvents=function(){return B.forwardEvents()},this.playVideo=function(t){return B.playVideo(t)},this.stopVideo=function(){return B.stopVideo()},this.castVideo=function(t,e){return B.castVideo(t,e)},this.stopCast=function(){return B.stopCast()},this.backgroundActiveMedia=function(){return B.backgroundActiveMedia()},this.restoreBackgroundMedia=function(){return B.restoreBackgroundMedia()},this.preloadNextItem=function(){B.background.currentMedia&&B.preloadVideo()},this.isBeforeComplete=function(){return B.beforeComplete},this.setVolume=function(t){C.setVolume(t),ut()},this.setMute=function(t){C.setMute(t),ut()},this.setPlaybackRate=function(t){C.setPlaybackRate(t)},this.getProvider=function(){return C.get("provider")},this.getWidth=function(){return C.get("containerWidth")},this.getHeight=function(){return C.get("containerHeight")},this.getItemQoe=function(){return C._qoeItem},this.addButton=function(t,e,i,n,o){var a=C.get("customButtons")||[],r=!1,s={img:t,tooltip:e,callback:i,id:n,btnClass:o};a=a.reduce((function(t,e){return e.id===n?(r=!0,t.push(s)):t.push(e),t}),[]),r||a.unshift(s),C.set("customButtons",a)},this.removeButton=function(t){var e=C.get("customButtons")||[];e=e.filter((function(e){return e.id!==t})),C.set("customButtons",e)},this.resize=v.resize,this.getSafeRegion=v.getSafeRegion,this.setCaptions=v.setCaptions,this.checkBeforePlay=function(){return _},this.setControls=function(t){Object(n.n)(t)||(t=!C.get("controls")),C.set("controls",t),B.controls=t},this.addCues=function(t){this.setCues(C.get("cues").concat(t))},this.setCues=function(t){C.set("cues",t)},this.updatePlaylist=function(t,e){try{var i=Object(c.b)(t,C,e);Object(c.e)(i);var o=Object(n.g)({},e);delete o.playlist,C.set("feedData",o),C.set("playlist",i)}catch(t){return Promise.reject(t)}return this.setItemIndex(C.get("item"))},this.setPlaylistItem=function(t,e){(e=Object(c.d)(C,new u.a(e),e.feedData||{}))&&(C.get("playlist")[t]=e,t===C.get("item")&&"idle"===C.get("state")&&this.setItemIndex(t))},this.playerDestroy=function(){this.off(),this.stop(),Object(a.b)(this,this.originalContainer),v&&v.destroy(),C&&C.destroy(),dt&&dt.destroy(),y&&y.destroy(),B&&B.destroy(),this.instreamDestroy()},this.isBeforePlay=this.checkBeforePlay,this.createInstream=function(){return this.instreamDestroy(),this._instreamAdapter=new ct(this,C,v,b),this._instreamAdapter},this.instreamDestroy=function(){O._instreamAdapter&&(O._instreamAdapter.destroy(),O._instreamAdapter=null)};var dt=new s.a(this,["play","pause","setCurrentAudioTrack","setCurrentCaptions","setCurrentQuality","setFullscreen"],(function(){return!T._model.get("itemReady")||L}));dt.queue.push.apply(dt.queue,w),v.setup()},get:function(t){if(t in y.a){var e=this._model.get("mediaModel");return e?e.get(t):y.a[t]}return this._model.get(t)},getContainer:function(){return this.currentContainer||this.originalContainer},getMute:function(){return this._model.getMute()},triggerError:function(t){var e=this._model;t.message=e.get("localization").errors[t.key],delete t.key,e.set("errorEvent",t),e.set("state",d.lb),e.once("change:state",(function(){this.set("errorEvent",void 0)}),e),this.trigger(d.w,t)}});e.default=Me},,,,,,,,,,,,function(t,e){!function(t,e){"use strict";if("IntersectionObserver"in t&&"IntersectionObserverEntry"in t&&"intersectionRatio"in t.IntersectionObserverEntry.prototype)"isIntersecting"in t.IntersectionObserverEntry.prototype||Object.defineProperty(t.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var i=[];o.prototype.THROTTLE_TIMEOUT=100,o.prototype.POLL_INTERVAL=null,o.prototype.USE_MUTATION_OBSERVER=!0,o.prototype.observe=function(t){if(!this._observationTargets.some((function(e){return e.element==t}))){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(),this._checkForIntersections()}},o.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._observationTargets.length||(this._unmonitorIntersections(),this._unregisterInstance())},o.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorIntersections(),this._unregisterInstance()},o.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},o.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,i){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==i[e-1]}))},o.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},o.prototype._monitorIntersections=function(){this._monitoringIntersections||(this._monitoringIntersections=!0,this.POLL_INTERVAL?this._monitoringInterval=setInterval(this._checkForIntersections,this.POLL_INTERVAL):(a(t,"resize",this._checkForIntersections,!0),a(e,"scroll",this._checkForIntersections,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in t&&(this._domObserver=new MutationObserver(this._checkForIntersections),this._domObserver.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))))},o.prototype._unmonitorIntersections=function(){this._monitoringIntersections&&(this._monitoringIntersections=!1,clearInterval(this._monitoringInterval),this._monitoringInterval=null,r(t,"resize",this._checkForIntersections,!0),r(e,"scroll",this._checkForIntersections,!0),this._domObserver&&(this._domObserver.disconnect(),this._domObserver=null))},o.prototype._checkForIntersections=function(){var e=this._rootIsInDom(),i=e?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach((function(o){var a=o.element,r=s(a),l=this._rootContainsTarget(a),c=o.entry,u=e&&l&&this._computeTargetAndRootIntersection(a,i),d=o.entry=new n({time:t.performance&&performance.now&&performance.now(),target:a,boundingClientRect:r,rootBounds:i,intersectionRect:u});c?e&&l?this._hasCrossedThreshold(c,d)&&this._queuedEntries.push(d):c&&c.isIntersecting&&this._queuedEntries.push(d):this._queuedEntries.push(d)}),this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)},o.prototype._computeTargetAndRootIntersection=function(i,n){if("none"!=t.getComputedStyle(i).display){for(var o,a,r,l,u,d,p,h,f=s(i),w=c(i),g=!1;!g;){var j=null,b=1==w.nodeType?t.getComputedStyle(w):{};if("none"==b.display)return;if(w==this.root||w==e?(g=!0,j=n):w!=e.body&&w!=e.documentElement&&"visible"!=b.overflow&&(j=s(w)),j&&(o=j,a=f,r=void 0,l=void 0,u=void 0,d=void 0,p=void 0,h=void 0,r=Math.max(o.top,a.top),l=Math.min(o.bottom,a.bottom),u=Math.max(o.left,a.left),d=Math.min(o.right,a.right),h=l-r,!(f=(p=d-u)>=0&&h>=0&&{top:r,bottom:l,left:u,right:d,width:p,height:h})))break;w=c(w)}return f}},o.prototype._getRootRect=function(){var t;if(this.root)t=s(this.root);else{var i=e.documentElement,n=e.body;t={top:0,left:0,right:i.clientWidth||n.clientWidth,width:i.clientWidth||n.clientWidth,bottom:i.clientHeight||n.clientHeight,height:i.clientHeight||n.clientHeight}}return this._expandRectByRootMargin(t)},o.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map((function(e,i){return"px"==e.unit?e.value:e.value*(i%2?t.width:t.height)/100})),i={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return i.width=i.right-i.left,i.height=i.bottom-i.top,i},o.prototype._hasCrossedThreshold=function(t,e){var i=t&&t.isIntersecting?t.intersectionRatio||0:-1,n=e.isIntersecting?e.intersectionRatio||0:-1;if(i!==n)for(var o=0;o=0&&(n.metadata.mpegts=o+e)}var a=this.getLiveLatency();null!==a&&(n.latency=a),(this.state===r.pb||this.seeking)&&this.trigger(r.S,n)}},click:function(t){this.trigger(r.n,t)},volumechange:function(){var t=this.video;this.trigger(r.V,{volume:Math.round(100*t.volume)}),this.trigger(r.M,{mute:t.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(r.R))},playing:function(){-1===this.stallTime&&this.setState(r.pb),this.trigger(r.fb)},pause:function(){this.state!==r.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(r.ob))},progress:function(){var t=this.getDuration();if(!(t<=0||t===1/0)){var e=this.video.buffered;if(e&&0!==e.length){var i=Object(s.a)(e.end(e.length-1)/t,0,1);this.trigger(r.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:t,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(r.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==r.mb&&this.state!==r.kb&&this.trigger(r.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},c=i(10);function u(t){return t&&t.length?t.end(t.length-1):0}var d={container:null,volume:function(t){this.video.volume=Math.min(Math.max(0,t/100),1)},mute:function(t){this.video.muted=!!t,this.video.muted||this.video.removeAttribute("muted")},resize:function(t,e,i){var n=this.video,a=n.videoWidth,r=n.videoHeight;if(t&&e&&a&&r){var s={objectFit:"",width:"",height:""};if("uniform"===i){var l=t/e,u=a/r,d=Math.abs(l-u);d<.09&&d>.0025&&(s.objectFit="fill",i="exactfit")}if(o.Browser.ie||o.OS.iOS&&o.OS.version.major<9||o.Browser.androidNative)if("uniform"!==i){s.objectFit="contain";var p=t/e,h=a/r,f=1,w=1;"none"===i?f=w=p>h?Math.ceil(100*r/e)/100:Math.ceil(100*a/t)/100:"fill"===i?f=w=p>h?p/h:h/p:"exactfit"===i&&(p>h?(f=p/h,w=1):(f=1,w=h/p)),Object(c.e)(n,"matrix(".concat(f.toFixed(2),", 0, 0, ").concat(w.toFixed(2),", 0, 0)"))}else s.top=s.left=s.margin="",Object(c.e)(n,"");Object(c.d)(n,s)}},getContainer:function(){return this.container},setContainer:function(t){this.container=t,this.video.parentNode!==t&&t.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var t=this.container;t&&t===this.video.parentNode&&t.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return u(this.video.buffered)-this.video.currentTime<=2}},p={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},h=i(65),f=i(5),w=i(53),g=i(7),j=i(66),b=i(63),m={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function v(t,e){for(var i,n,o,a=t.length,r="",s=e||0;s>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:r+=String.fromCharCode(i);break;case 12:case 13:n=t[s++],r+=String.fromCharCode((31&i)<<6|63&n);break;case 14:n=t[s++],o=t[s++],r+=String.fromCharCode((15&i)<<12|(63&n)<<6|(63&o)<<0)}return r}function y(t){var e=function(t){for(var e="0x",i=0;i>1|(8323072&e)>>2|(2130706432&e)>>3}function k(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(t,e){if(!("value"in e)&&"data"in e&&e.data instanceof ArrayBuffer){var i=new Uint8Array(e.data),n=i.length;e={value:{key:"",data:""}};for(var o=10;o<14&&o0){var c=v(i.subarray(a,a+=s),0);if("PRIV"===e.value.key){if("com.apple.streaming.transportStreamTimestamp"===c){var u=1&y(i.subarray(a,a+=4)),d=y(i.subarray(a,a+=4))+(u?4294967296:0);e.value.data=d}else e.value.data=v(i,a+1);e.value.info=c}else e.value.info=c,e.value.data=v(i,a+1)}else{var p=i[a];e.value.data=1===p||2===p?function(t,e){for(var i=t.length-1,n="",o=e||0;o=0&&o[a].startTime>e.startTime;a--)i.unshift(o[a]),t.removeCue(o[a]);try{t.addCue(e),i.forEach((function(e){return t.addCue(e)}))}catch(t){console.error(t)}t.mode=n}(e,n)}else try{e.addCue(i)}catch(t){console.error(t)}}function M(t,e){e&&e.length&&Object(n.f)(e,(function(e){if(!(o.Browser.ie&&t&&/^(native|subtitle|cc)/.test(e._id))){o.Browser.ie&&"disabled"===e.mode||(e.mode="disabled",e.mode="hidden");for(var i=e.cues.length;i--;)e.removeCue(e.cues[i]);e.embedded||(e.mode="disabled"),e.inuse=!1}}))}function S(t){return"subtitles"===t||"captions"===t}function E(t){var e,i=Object(b.b)(t,this._unknownCount),o=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===t.kind){var a=this.video.textTracks;(e=Object(n.j)(a,{label:o}))||(e=this.video.addTextTrack(t.kind,o,t.language||"")),e.default=t.default,e.mode="disabled",e.inuse=!0}else(e=t).data=e.data||[];return e._id||(e._id=Object(b.a)(t,this._textTracks.length)),e}function I(t){this._textTracks.push(t),this._tracksById[t._id]=t}function L(){if(this._textTracks){var t=this._textTracks.filter((function(t){return t.embedded||"subs"===t.groupid}));this._initTextTracks(),t.forEach((function(t){this._tracksById[t._id]=t})),this._textTracks=t}}function A(t){this.triggerActiveCues(t.currentTarget.activeCues)}function P(t,e,i){var n=t.kind;this._cachedVTTCues[t._id]||(this._cachedVTTCues[t._id]={});var o,a=this._cachedVTTCues[t._id];switch(n){case"captions":case"subtitles":o=i||Math.floor(20*e.startTime);var r="_"+e.line,s=Math.floor(20*e.endTime),l=a[o+r]||a[o+1+r]||a[o-1+r];return!(l&&Math.abs(l-s)<=1)&&(a[o+r]=s,!0);case"metadata":var c=e.data?new Uint8Array(e.data).join(""):e.text;return!a[o=i||e.startTime+c]&&(a[o]=e.endTime,!0);default:return!1}}function R(t){if(t.length>this._textTracks.length)return!0;for(var e=0;e=0&&(w.retries=0);var t=w.getVideoCurrentTime();w.currentTime=t,M&&C!==t&&$(t),l.timeupdate.call(w),ft(),o.Browser.ie&&G()},resize:G,ended:function(){_=-1,wt(),l.ended.call(w)},loadedmetadata:function(){var t=w.getDuration();R&&t===1/0&&(t=0);var e={metadataType:"media",duration:t,height:v.videoHeight,width:v.videoWidth,seekRange:w.getSeekRange()};w.trigger(r.K,e),G()},durationchange:function(){R||l.progress.call(w)},loadeddata:function(){var t;!function(){if(v.getStartDate){var t=v.getStartDate(),e=t.getTime?t.getTime():NaN;if(e!==w.startDateTime&&!isNaN(e)){w.startDateTime=e;var i=t.toISOString(),n=w.getSeekRange(),o=n.start,a=n.end,s={metadataType:"program-date-time",programDateTime:i,start:o,end:a},l=w.createCue(o,a,JSON.stringify(s));w.addVTTCue({type:"metadata",cue:l}),delete s.metadataType,w.trigger(r.L,{metadataType:"program-date-time",metadata:s})}}}(),l.loadeddata.call(w),function(t){if(E=null,!t)return;if(t.length){for(var e=0;e0&&(e=t.map((function(t,e){return{label:t.label||e}}))),e}function it(t){w.currentTime=-1,j=t.minDvrWindow,m=t.sources,_=function(t){var i=Math.max(0,_),n=e.qualityLabel;if(t)for(var o=0;o0&&(T=-1,w.seek(t)),t>0&&w.getVideoCurrentTime()!==t&&w.seek(t);var n=et(m);n&&w.trigger(r.I,{levels:n,currentQuality:_}),m.length&&"hls"!==m[0].type&&ht()}function at(t){E=null,I=-1,y.reason||(y.reason="initial choice",y.level={}),x=!1;var e=document.createElement("source");e.src=t.file,v.src!==e.src&&(v.src=t.file)}function rt(){v&&(w.disableTextTrack(),v.removeAttribute("preload"),v.removeAttribute("src"),Object(f.h)(v),Object(c.d)(v,{objectFit:""}),_=-1,!o.Browser.msie&&"load"in v&&v.load())}function st(){var t=1/0;return["buffered","seekable"].forEach((function(e){for(var i=v[e],o=i?i.length:0;o--;){var a=Math.min(t,i.start(o));Object(n.o)(a)&&(t=a)}})),t}function lt(){var t=0;return["buffered","seekable"].forEach((function(e){for(var i=v[e],o=i?i.length:0;o--;){var a=Math.max(t,i.end(o));Object(n.o)(a)&&(t=a)}})),t}function ct(){for(var t=-1,e=0;e-1&&t1)&&function(t){X=t.end,J=Math.min(0,w.getVideoCurrentTime()-X),Z=Object(V.a)()}(e),Object(h.a)(e.end-e.start,j))return J}return t}(w.getVideoCurrentTime())},w.getDuration=function(){if(e.getDurationHook)return e.getDurationHook();var t=v.duration;if(R&&t===1/0&&0===w.getVideoCurrentTime()||isNaN(t))return 0;var i=lt();if(v.duration===1/0&&i){var n=i-st();Object(h.a)(n,j)&&(t=-n)}return t},w.getSeekRange=function(){var t={start:0,end:w.getDuration()};return v.seekable.length&&(t.end=lt(),t.start=st()),t},w.getLiveLatency=function(){var t=null,e=lt();return w.isLive()&&e&&(t=e+(Object(V.a)()-Z)/1e3-w.getVideoCurrentTime()),t},this.stop=function(){wt(),rt(),this.clearTracks(),o.Browser.ie&&v.pause(),this.setState(r.mb)},this.destroy=function(){S=Q,Y(b,v),this.removeTracksListener(v.audioTracks,"change",ct),this.removeTracksListener(v.textTracks,"change",w.textTrackChangeHandler),this.off()},this.init=function(t){w.retries=0,w.maxRetries=t.adType?0:3,it(t);var e=m[_];(R=Object(a.a)(e))&&(w.supportsPlaybackRate=!1,b.waiting=Q),w.eventsOn_(),m.length&&"hls"!==m[0].type&&this.sendMediaType(m),y.reason=""},this.preload=function(t){it(t);var e=m[_],i=e.preload||"metadata";"none"!==i&&(v.setAttribute("preload",i),at(e))},this.load=function(t){it(t),ot(t.starttime),this.setupSideloadedTracks(t.tracks)},this.play=function(){return S(),nt()},this.pause=function(){wt(),S=function(){if(v.paused&&w.getVideoCurrentTime()&&w.isLive()){var t=lt(),e=t-st(),i=!Object(h.a)(e,j),o=t-w.getVideoCurrentTime();if(i&&t&&(o>15||o<0)){if(O=Math.max(t-10,t-e),!Object(n.o)(O))return;$(w.getVideoCurrentTime()),v.currentTime=O}}},v.pause()},this.seek=function(t){if(e.seekHook)return e.seekHook(t,v);var i=w.getSeekRange(),n=t;if(t<0&&(n+=i.end),x||(x=!!lt()),x){T=0;try{if(w.seeking=!0,w.isLive()&&Object(h.a)(i.end-i.start,j))if(J=Math.min(0,n-X),t<0)n+=Math.min(12,(Object(V.a)()-Z)/1e3);O=n,$(w.getVideoCurrentTime()),v.currentTime=n}catch(t){w.seeking=!1,T=n}}else T=n,o.Browser.firefox&&v.paused&&nt()},this.setVisibility=function(t){(t=!!t)||o.OS.android?Object(c.d)(w.container,{visibility:"visible",opacity:1}):Object(c.d)(w.container,{visibility:"",opacity:0})},this.setFullscreen=function(t){if(t=!!t){try{var e=v.webkitEnterFullscreen||v.webkitEnterFullScreen;e&&e.apply(v)}catch(t){return!1}return w.getFullScreen()}var i=v.webkitExitFullscreen||v.webkitExitFullScreen;return i&&i.apply(v),t},w.getFullScreen=function(){return M||!!v.webkitDisplayingFullscreen},this.setCurrentQuality=function(t){_!==t&&t>=0&&m&&m.length>t&&(_=t,y.reason="api",y.level={},this.trigger(r.J,{currentQuality:t,levels:et(m)}),e.qualityLabel=m[t].label,ot(w.getVideoCurrentTime()||0),nt())},this.setPlaybackRate=function(t){v.playbackRate=v.defaultPlaybackRate=t},this.getPlaybackRate=function(){return v.playbackRate},this.getCurrentQuality=function(){return _},this.getQualityLevels=function(){return Array.isArray(m)?m.map((function(t){return function(t){return{bitrate:t.bitrate,label:t.label,width:t.width,height:t.height}}(t)})):[]},this.getName=function(){return{name:W}},this.setCurrentAudioTrack=dt,this.getAudioTracks=function(){return E||[]},this.getCurrentAudioTrack=function(){return I}}Object(n.g)(X.prototype,w.a),X.getName=function(){return{name:"html5"}};e.default=X;var K=220001},,,,,,,,,,,,,,,,,,,,,,,,,,function(t,e,i){"use strict";i.d(e,"a",(function(){return o}));var n=i(2);function o(t){var e=[],i=(t=Object(n.i)(t)).split("\r\n\r\n");1===i.length&&(i=t.split("\n\n"));for(var o=0;o0&&(o=0),i.length>o+1&&i[o+1]){var a=i[o],r=a.indexOf(" --\x3e ");r>0&&(e.begin=Object(n.g)(a.substr(0,r)),e.end=Object(n.g)(a.substr(r+5)),e.text=i.slice(o+1).join("\r\n"))}return e}},function(t,e,i){"use strict";i.d(e,"a",(function(){return o})),i.d(e,"b",(function(){return a}));var n=i(5);function o(t){var e=-1;return t>=1280?e=7:t>=960?e=6:t>=800?e=5:t>=640?e=4:t>=540?e=3:t>=420?e=2:t>=320?e=1:t>=250&&(e=0),e}function a(t,e){var i="jw-breakpoint-"+e;Object(n.p)(t,/jw-breakpoint--?\d+/,i)}},function(t,e,i){"use strict";i.d(e,"a",(function(){return d}));var n,o=i(0),a=i(8),r=i(16),s=i(7),l=i(3),c=i(10),u=i(5),d={back:!0,backgroundOpacity:50,edgeStyle:null,fontSize:14,fontOpacity:100,fontScale:.05,preprocessor:o.k,windowOpacity:0},p=function(t){var e,s,p,h,f,w,g,j,b,m=this,v=t.player;function y(){Object(o.o)(e.fontSize)&&(v.get("containerHeight")?j=d.fontScale*(e.userFontScale||1)*e.fontSize/d.fontSize:v.once("change:containerHeight",y,this))}function k(){var t=v.get("containerHeight");if(t){var e;if(v.get("fullscreen")&&a.OS.iOS)e=null;else{var i=t*j;e=Math.round(10*function(t){var e=v.get("mediaElement");if(e&&e.videoHeight){var i=e.videoWidth,n=e.videoHeight,o=i/n,r=v.get("containerHeight"),s=v.get("containerWidth");if(v.get("fullscreen")&&a.OS.mobile){var l=window.screen;l.orientation&&(r=l.availHeight,s=l.availWidth)}if(s&&r&&i&&n)return(s/r>o?r:n*s/i)*j}return t}(i))/10}v.get("renderCaptionsNatively")?function(t,e){var i="#".concat(t," .jw-video::-webkit-media-text-track-display");e&&(e+="px",a.OS.iOS&&Object(c.b)(i,{fontSize:"inherit"},t,!0));b.fontSize=e,Object(c.b)(i,b,t,!0)}(v.get("id"),e):Object(c.d)(f,{fontSize:e})}}function x(t,e,i){var n=Object(c.c)("#000000",i);"dropshadow"===t?e.textShadow="0 2px 1px "+n:"raised"===t?e.textShadow="0 0 5px "+n+", 0 1px 5px "+n+", 0 2px 5px "+n:"depressed"===t?e.textShadow="0 -2px 1px "+n:"uniform"===t&&(e.textShadow="-2px 0 1px "+n+",2px 0 1px "+n+",0 -2px 1px "+n+",0 2px 1px "+n+",-1px 1px 1px "+n+",1px 1px 1px "+n+",1px -1px 1px "+n+",1px 1px 1px "+n)}(f=document.createElement("div")).className="jw-captions jw-reset",this.show=function(){Object(u.a)(f,"jw-captions-enabled")},this.hide=function(){Object(u.o)(f,"jw-captions-enabled")},this.populate=function(t){v.get("renderCaptionsNatively")||(p=[],s=t,t?this.selectCues(t,h):this.renderCues())},this.resize=function(){k(),this.renderCues(!0)},this.renderCues=function(t){t=!!t,n&&n.processCues(window,p,f,t)},this.selectCues=function(t,e){if(t&&t.data&&e&&!v.get("renderCaptionsNatively")){var i=this.getAlignmentPosition(t,e);!1!==i&&(p=this.getCurrentCues(t.data,i),this.renderCues(!0))}},this.getCurrentCues=function(t,e){return Object(o.h)(t,(function(t){return e>=t.startTime&&(!t.endTime||e<=t.endTime)}))},this.getAlignmentPosition=function(t,e){var i=t.source,n=e.metadata,a=e.currentTime;return i&&n&&Object(o.r)(n[i])&&(a=n[i]),a},this.clear=function(){Object(u.g)(f)},this.setup=function(t,i){w=document.createElement("div"),g=document.createElement("span"),w.className="jw-captions-window jw-reset",g.className="jw-captions-text jw-reset",e=Object(o.g)({},d,i),j=d.fontScale;var n=function(){if(!v.get("renderCaptionsNatively")){y(e.fontSize);var i=e.windowColor,n=e.windowOpacity,o=e.edgeStyle;b={};var r={};!function(t,e){var i=e.color,n=e.fontOpacity;(i||n!==d.fontOpacity)&&(t.color=Object(c.c)(i||"#ffffff",n));if(e.back){var o=e.backgroundColor,a=e.backgroundOpacity;o===d.backgroundColor&&a===d.backgroundOpacity||(t.backgroundColor=Object(c.c)(o,a))}else t.background="transparent";e.fontFamily&&(t.fontFamily=e.fontFamily);e.fontStyle&&(t.fontStyle=e.fontStyle);e.fontWeight&&(t.fontWeight=e.fontWeight);e.textDecoration&&(t.textDecoration=e.textDecoration)}(r,e),(i||n!==d.windowOpacity)&&(b.backgroundColor=Object(c.c)(i||"#000000",n)),x(o,r,e.fontOpacity),e.back||null!==o||x("uniform",r),Object(c.d)(w,b),Object(c.d)(g,r),function(t,e){k(),function(t,e){a.Browser.safari&&Object(c.b)("#"+t+" .jw-video::-webkit-media-text-track-display-backdrop",{backgroundColor:e.backgroundColor},t,!0);Object(c.b)("#"+t+" .jw-video::-webkit-media-text-track-display",b,t,!0),Object(c.b)("#"+t+" .jw-video::cue",e,t,!0)}(t,e),function(t,e){Object(c.b)("#"+t+" .jw-text-track-display",b,t),Object(c.b)("#"+t+" .jw-text-track-cue",e,t)}(t,e)}(t,r)}};n(),w.appendChild(g),f.appendChild(w),v.change("captionsTrack",(function(t,e){this.populate(e)}),this),v.set("captions",e),v.on("change:captions",(function(t,i){e=i,n()}))},this.element=function(){return f},this.destroy=function(){v.off(null,null,this),this.off()};var T=function(t){h=t,m.selectCues(s,h)};v.on("change:playlistItem",(function(){h=null,p=[]}),this),v.on(l.Q,(function(t){p=[],T(t)}),this),v.on(l.S,T,this),v.on("subtitlesTrackData",(function(){this.selectCues(s,h)}),this),v.on("change:captionsList",(function t(e,o){var a=this;1!==o.length&&(e.get("renderCaptionsNatively")||n||(i.e(8).then(function(t){n=i(68).default}.bind(null,i)).catch(Object(r.c)(301121)).catch((function(t){a.trigger(l.tb,t)})),e.off("change:captionsList",t,this)))}),this)};Object(o.g)(p.prototype,s.a),e.b=p},function(t,e,i){"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i=function(t,e){var i=t[1]||"",n=t[3];if(!n)return i;if(e&&"function"==typeof btoa){var o=(r=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */"),a=n.sources.map((function(t){return"/*# sourceURL="+n.sourceRoot+t+" */"}));return[i].concat(a).concat([o]).join("\n")}var r;return[i].join("\n")}(e,t);return e[2]?"@media "+e[2]+"{"+i+"}":i})).join("")},e.i=function(t,i){"string"==typeof t&&(t=[[null,t,""]]);for(var n={},o=0;o'},function(t,e,i){"use strict";function n(t,e){var i=t.kind||"cc";return t.default||t.defaulttrack?"default":t._id||t.file||i+e}function o(t,e){var i=t.label||t.name||t.language;return i||(i="Unknown CC",(e+=1)>1&&(i+=" ["+e+"]")),{label:i,unknownCount:e}}i.d(e,"a",(function(){return n})),i.d(e,"b",(function(){return o}))},function(t,e,i){"use strict";function n(t){return new Promise((function(e,i){if(t.paused)return i(o("NotAllowedError",0,"play() failed."));var n=function(){t.removeEventListener("play",a),t.removeEventListener("playing",r),t.removeEventListener("pause",r),t.removeEventListener("abort",r),t.removeEventListener("error",r)},a=function(){t.addEventListener("playing",r),t.addEventListener("abort",r),t.addEventListener("error",r),t.addEventListener("pause",r)},r=function(t){if(n(),"playing"===t.type)e();else{var a='The play() request was interrupted by a "'.concat(t.type,'" event.');"error"===t.type?i(o("NotSupportedError",9,a)):i(o("AbortError",20,a))}};t.addEventListener("play",a)}))}function o(t,e,i){var n=new Error(i);return n.name=t,n.code=e,n}i.d(e,"a",(function(){return n}))},function(t,e,i){"use strict";function n(t,e){return t!==1/0&&Math.abs(t)>=Math.max(a(e),0)}function o(t,e){var i="VOD";return t===1/0?i="LIVE":t<0&&(i=n(t,a(e))?"DVR":"LIVE"),i}function a(t){return void 0===t?120:Math.max(t,0)}i.d(e,"a",(function(){return n})),i.d(e,"b",(function(){return o}))},function(t,e,i){"use strict";var n=i(67),o=i(16),a=i(22),r=i(4),s=i(57),l=i(2),c=i(1);function u(t){throw new c.n(null,t)}function d(t,e,n){t.xhr=Object(a.a)(t.file,(function(a){!function(t,e,n,a){var d,p,f=t.responseXML?t.responseXML.firstChild:null;if(f)for("xml"===Object(r.b)(f)&&(f=f.nextSibling);f.nodeType===f.COMMENT_NODE;)f=f.nextSibling;try{if(f&&"tt"===Object(r.b)(f))d=function(t){t||u(306007);var e=[],i=t.getElementsByTagName("p"),n=30,o=t.getElementsByTagName("tt");if(o&&o[0]){var a=parseFloat(o[0].getAttribute("ttp:frameRate"));isNaN(a)||(n=a)}i||u(306005),i.length||(i=t.getElementsByTagName("tt:p")).length||(i=t.getElementsByTagName("tts:p"));for(var r=0;r\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(f){var w=s.getAttribute("begin"),g=s.getAttribute("dur"),j=s.getAttribute("end"),b={begin:Object(l.g)(w,n),text:f};j?b.end=Object(l.g)(j,n):g&&(b.end=b.begin+Object(l.g)(g,n)),e.push(b)}}return e.length||u(306005),e}(t.responseXML),p=h(d),delete e.xhr,n(p);else{var w=t.responseText;w.indexOf("WEBVTT")>=0?i.e(10).then(function(t){return i(97).default}.bind(null,i)).catch(Object(o.c)(301131)).then((function(t){var i=new t(window);p=[],i.oncue=function(t){p.push(t)},i.onflush=function(){delete e.xhr,n(p)},i.parse(w)})).catch((function(t){delete e.xhr,a(Object(c.v)(null,c.b,t))})):(d=Object(s.a)(w),p=h(d),delete e.xhr,n(p))}}catch(t){delete e.xhr,a(Object(c.v)(null,c.b,t))}}(a,t,e,n)}),(function(t,e,i,o){n(Object(c.u)(o,c.b))}))}function p(t){t&&t.forEach((function(t){var e=t.xhr;e&&(e.onload=null,e.onreadystatechange=null,e.onerror=null,"abort"in e&&e.abort()),delete t.xhr}))}function h(t){return t.map((function(t){return new n.a(t.begin,t.end,t.text)}))}i.d(e,"c",(function(){return d})),i.d(e,"a",(function(){return p})),i.d(e,"b",(function(){return h}))},function(t,e,i){"use strict";var n=window.VTTCue;function o(t){if("string"!=typeof t)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[t.toLowerCase()]&&t.toLowerCase()}if(!n){(n=function(t,e,i){var n=this;n.hasBeenReset=!1;var a="",r=!1,s=t,l=e,c=i,u=null,d="",p=!0,h="auto",f="start",w="auto",g=100,j="middle";Object.defineProperty(n,"id",{enumerable:!0,get:function(){return a},set:function(t){a=""+t}}),Object.defineProperty(n,"pauseOnExit",{enumerable:!0,get:function(){return r},set:function(t){r=!!t}}),Object.defineProperty(n,"startTime",{enumerable:!0,get:function(){return s},set:function(t){if("number"!=typeof t)throw new TypeError("Start time must be set to a number.");s=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"endTime",{enumerable:!0,get:function(){return l},set:function(t){if("number"!=typeof t)throw new TypeError("End time must be set to a number.");l=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"text",{enumerable:!0,get:function(){return c},set:function(t){c=""+t,this.hasBeenReset=!0}}),Object.defineProperty(n,"region",{enumerable:!0,get:function(){return u},set:function(t){u=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"vertical",{enumerable:!0,get:function(){return d},set:function(t){var e=function(t){return"string"==typeof t&&(!!{"":!0,lr:!0,rl:!0}[t.toLowerCase()]&&t.toLowerCase())}(t);if(!1===e)throw new SyntaxError("An invalid or illegal string was specified.");d=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"snapToLines",{enumerable:!0,get:function(){return p},set:function(t){p=!!t,this.hasBeenReset=!0}}),Object.defineProperty(n,"line",{enumerable:!0,get:function(){return h},set:function(t){if("number"!=typeof t&&"auto"!==t)throw new SyntaxError("An invalid number or illegal string was specified.");h=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"lineAlign",{enumerable:!0,get:function(){return f},set:function(t){var e=o(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"position",{enumerable:!0,get:function(){return w},set:function(t){if(t<0||t>100)throw new Error("Position must be between 0 and 100.");w=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"size",{enumerable:!0,get:function(){return g},set:function(t){if(t<0||t>100)throw new Error("Size must be between 0 and 100.");g=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"align",{enumerable:!0,get:function(){return j},set:function(t){var e=o(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");j=e,this.hasBeenReset=!0}}),n.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}e.a=n},,function(t,e,i){var n=i(70);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(t.exports=n.locals)},function(t,e,i){(t.exports=i(60)(!1)).push([t.i,'.jw-reset{text-align:left;direction:ltr}.jw-reset-text,.jw-reset{color:inherit;background-color:transparent;padding:0;margin:0;float:none;font-family:Arial,Helvetica,sans-serif;font-size:1em;line-height:1em;list-style:none;text-transform:none;vertical-align:baseline;border:0;font-variant:inherit;font-stretch:inherit;-webkit-tap-highlight-color:rgba(255,255,255,0)}body .jw-error,body .jwplayer.jw-state-error{height:100%;width:100%}.jw-title{position:absolute;top:0}.jw-background-color{background:rgba(0,0,0,0.4)}.jw-text{color:rgba(255,255,255,0.8)}.jw-knob{color:rgba(255,255,255,0.8);background-color:#fff}.jw-button-color{color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):focus,:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):hover{color:#fff}.jw-toggle{color:#fff}.jw-toggle.jw-off{color:rgba(255,255,255,0.8)}.jw-toggle.jw-off:focus{color:#fff}.jw-toggle:focus{outline:none}:not(.jw-flag-touch) .jw-toggle.jw-off:hover{color:#fff}.jw-rail{background:rgba(255,255,255,0.3)}.jw-buffer{background:rgba(255,255,255,0.3)}.jw-progress{background:#f2f2f2}.jw-time-tip,.jw-volume-tip{border:0}.jw-slider-volume.jw-volume-tip.jw-background-color.jw-slider-vertical{background:none}.jw-skip{padding:.5em;outline:none}.jw-skip .jw-skiptext,.jw-skip .jw-skip-icon{color:rgba(255,255,255,0.8)}.jw-skip.jw-skippable:hover .jw-skip-icon,.jw-skip.jw-skippable:focus .jw-skip-icon{color:#fff}.jw-icon-cast google-cast-launcher{--connected-color:#fff;--disconnected-color:rgba(255,255,255,0.8)}.jw-icon-cast google-cast-launcher:focus{outline:none}.jw-icon-cast google-cast-launcher.jw-off{--connected-color:rgba(255,255,255,0.8)}.jw-icon-cast:focus google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-icon-cast:hover google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-nextup-container{bottom:2.5em;padding:5px .5em}.jw-nextup{border-radius:0}.jw-color-active{color:#fff;stroke:#fff;border-color:#fff}:not(.jw-flag-touch) .jw-color-active-hover:hover,:not(.jw-flag-touch) .jw-color-active-hover:focus{color:#fff;stroke:#fff;border-color:#fff}.jw-color-inactive{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-color-inactive-hover:hover{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}.jw-option{color:rgba(255,255,255,0.8)}.jw-option.jw-active-option{color:#fff;background-color:rgba(255,255,255,0.1)}:not(.jw-flag-touch) .jw-option:hover{color:#fff}.jwplayer{width:100%;font-size:16px;position:relative;display:block;min-height:0;overflow:hidden;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:none}.jwplayer *{box-sizing:inherit}.jwplayer.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jwplayer.jw-flag-aspect-mode{height:auto !important}.jwplayer.jw-flag-aspect-mode .jw-aspect{display:block}.jwplayer .jw-aspect{display:none}.jwplayer .jw-swf{outline:none}.jw-media,.jw-preview{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}.jw-media{overflow:hidden;cursor:pointer}.jw-plugin{position:absolute;bottom:66px}.jw-breakpoint-7 .jw-plugin{bottom:132px}.jw-plugin .jw-banner{max-width:100%;opacity:0;cursor:pointer;position:absolute;margin:auto auto 0;left:0;right:0;bottom:0;display:block}.jw-preview,.jw-captions,.jw-title{pointer-events:none}.jw-media,.jw-logo{pointer-events:all}.jw-wrapper{background-color:#000;position:absolute;top:0;left:0;right:0;bottom:0}.jw-hidden-accessibility{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.jw-contract-trigger::before{content:"";overflow:hidden;width:200%;height:200%;display:block;position:absolute;top:0;left:0}.jwplayer .jw-media video{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;margin:auto;background:transparent}.jwplayer .jw-media video::-webkit-media-controls-start-playback-button{display:none}.jwplayer.jw-stretch-uniform .jw-media video{object-fit:contain}.jwplayer.jw-stretch-none .jw-media video{object-fit:none}.jwplayer.jw-stretch-fill .jw-media video{object-fit:cover}.jwplayer.jw-stretch-exactfit .jw-media video{object-fit:fill}.jw-preview{position:absolute;display:none;opacity:1;visibility:visible;width:100%;height:100%;background:#000 no-repeat 50% 50%}.jwplayer .jw-preview,.jw-error .jw-preview{background-size:contain}.jw-stretch-none .jw-preview{background-size:auto auto}.jw-stretch-fill .jw-preview{background-size:cover}.jw-stretch-exactfit .jw-preview{background-size:100% 100%}.jw-title{display:none;padding-top:20px;width:100%;z-index:1}.jw-title-primary,.jw-title-secondary{color:#fff;padding-left:20px;padding-right:20px;padding-bottom:.5em;overflow:hidden;text-overflow:ellipsis;direction:unset;white-space:nowrap;width:100%}.jw-title-primary{font-size:1.625em}.jw-breakpoint-2 .jw-title-primary,.jw-breakpoint-3 .jw-title-primary{font-size:1.5em}.jw-flag-small-player .jw-title-primary{font-size:1.25em}.jw-flag-small-player .jw-title-secondary,.jw-title-secondary:empty{display:none}.jw-captions{position:absolute;width:100%;height:100%;text-align:center;display:none;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-decoration:none;pointer-events:none;overflow:hidden;top:0}.jw-captions.jw-captions-enabled{display:block}.jw-captions-window{display:none;padding:.25em;border-radius:.25em}.jw-captions-window.jw-captions-window-active{display:inline-block}.jw-captions-text{display:inline-block;color:#fff;background-color:#000;word-wrap:normal;word-break:normal;white-space:pre-line;font-style:normal;font-weight:normal;text-align:center;text-decoration:none}.jw-text-track-display{font-size:inherit;line-height:1.5}.jw-text-track-cue{background-color:rgba(0,0,0,0.5);color:#fff;padding:.1em .3em}.jwplayer video::-webkit-media-controls{display:none;justify-content:flex-start}.jwplayer video::-webkit-media-text-track-display{min-width:-webkit-min-content}.jwplayer video::cue{background-color:rgba(0,0,0,0.5)}.jwplayer video::-webkit-media-controls-panel-container{display:none}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing) .jw-captions,.jwplayer.jw-flag-media-audio.jw-state-playing .jw-captions,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden) .jw-captions{max-height:calc(100% - 60px)}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-flag-media-audio.jw-state-playing:not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container{max-height:calc(100% - 60px)}.jw-logo{position:absolute;margin:20px;cursor:pointer;pointer-events:all;background-repeat:no-repeat;background-size:contain;top:auto;right:auto;left:auto;bottom:auto;outline:none}.jw-logo.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-flag-audio-player .jw-logo{display:none}.jw-logo-top-right{top:0;right:0}.jw-logo-top-left{top:0;left:0}.jw-logo-bottom-left{left:0}.jw-logo-bottom-right{right:0}.jw-logo-bottom-left,.jw-logo-bottom-right{bottom:44px;transition:bottom 150ms cubic-bezier(0, .25, .25, 1)}.jw-state-idle .jw-logo{z-index:1}.jw-state-setup .jw-wrapper{background-color:inherit}.jw-state-setup .jw-logo,.jw-state-setup .jw-controls,.jw-state-setup .jw-controls-backdrop{visibility:hidden}span.jw-break{display:block}body .jw-error,body .jwplayer.jw-state-error{background-color:#333;color:#fff;font-size:16px;display:table;opacity:1;position:relative}body .jw-error .jw-display,body .jwplayer.jw-state-error .jw-display{display:none}body .jw-error .jw-media,body .jwplayer.jw-state-error .jw-media{cursor:default}body .jw-error .jw-preview,body .jwplayer.jw-state-error .jw-preview{background-color:#333}body .jw-error .jw-error-msg,body .jwplayer.jw-state-error .jw-error-msg{background-color:#000;border-radius:2px;display:flex;flex-direction:row;align-items:stretch;padding:20px}body .jw-error .jw-error-msg .jw-icon,body .jwplayer.jw-state-error .jw-error-msg .jw-icon{height:30px;width:30px;margin-right:20px;flex:0 0 auto;align-self:center}body .jw-error .jw-error-msg .jw-icon:empty,body .jwplayer.jw-state-error .jw-error-msg .jw-icon:empty{display:none}body .jw-error .jw-error-msg .jw-info-container,body .jwplayer.jw-state-error .jw-error-msg .jw-info-container{margin:0;padding:0}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg{flex-direction:column}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text{text-align:center}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon{flex:.5 0 auto;margin-right:0;margin-bottom:20px}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break{display:inline}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break:before{content:" "}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg{height:100%;width:100%;top:0;position:absolute;left:0;background:#000;-webkit-transform:none;transform:none;padding:4px 16px;z-index:1}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg.jw-info-overlay{max-width:none;max-height:none}body .jwplayer.jw-state-error .jw-title,.jw-state-idle .jw-title,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-title{display:block}body .jwplayer.jw-state-error .jw-preview,.jw-state-idle .jw-preview,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-preview{display:block}.jw-state-idle .jw-captions,.jwplayer.jw-state-complete .jw-captions,body .jwplayer.jw-state-error .jw-captions{display:none}.jw-state-idle video::-webkit-media-text-track-container,.jwplayer.jw-state-complete video::-webkit-media-text-track-container,body .jwplayer.jw-state-error video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-fullscreen{width:100% !important;height:100% !important;top:0;right:0;bottom:0;left:0;z-index:1000;margin:0;position:fixed}body .jwplayer.jw-flag-flash-blocked .jw-title{display:block}.jwplayer.jw-flag-controls-hidden .jw-media{cursor:default}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:45px}.jw-flag-floating{background-size:cover;background-color:#000}.jw-flag-floating .jw-wrapper{position:fixed;z-index:2147483647;-webkit-animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;top:auto;bottom:1rem;left:auto;right:1rem;max-width:400px;max-height:400px;margin:0 auto}@media screen and (max-width:480px){.jw-flag-floating .jw-wrapper{width:100%;left:0;right:0}}.jw-flag-floating .jw-wrapper .jw-media{touch-action:none}@media screen and (max-device-width:480px) and (orientation:portrait){.jw-flag-touch.jw-flag-floating .jw-wrapper{-webkit-animation:none;animation:none;top:62px;bottom:auto;left:0;right:0;max-width:none;max-height:none}}.jw-flag-floating .jw-float-icon{pointer-events:all;cursor:pointer;display:none}.jw-flag-floating .jw-float-icon .jw-svg-icon{-webkit-filter:drop-shadow(0 0 1px #000);filter:drop-shadow(0 0 1px #000)}.jw-flag-floating.jw-floating-dismissible .jw-dismiss-icon{display:none}.jw-flag-floating.jw-floating-dismissible.jw-flag-ads .jw-float-icon{display:flex}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-logo,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-logo{display:none}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-float-icon,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-float-icon{display:flex}.jw-float-icon{display:none;position:absolute;top:3px;right:5px;align-items:center;justify-content:center}@-webkit-keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.jw-flag-top{margin-top:2em;overflow:visible}.jw-top{height:2em;line-height:2;pointer-events:none;text-align:center;opacity:.8;position:absolute;top:-2em;width:100%}.jw-top .jw-icon{cursor:pointer;pointer-events:all;height:auto;width:auto}.jw-top .jw-text{color:#555}',""])},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e,i){var n=i(96);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(t.exports=n.locals)},function(t,e,i){(t.exports=i(60)(!1)).push([t.i,'.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-flag-small-player .jw-settings-menu,.jw-settings-submenu{height:100%;width:100%}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;right:0}.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-settings-item-active::before{top:0;position:absolute;left:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;bottom:0;left:0}.jw-nextup-close{position:absolute;top:0;right:0}.jw-overlays,.jw-controls,.jw-flag-small-player .jw-settings-menu{position:absolute;bottom:0;right:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-time-tip::after,.jw-settings-menu .jw-icon.jw-button-color::after,.jw-text-live::before,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{content:"";display:block}.jw-svg-icon{height:24px;width:24px;fill:currentColor;pointer-events:none}.jw-icon{height:44px;width:44px;background-color:transparent;outline:none}.jw-icon.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-icon-airplay .jw-svg-icon-airplay-off{display:none}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-off{display:block}.jw-icon-airplay .jw-svg-icon-airplay-on{display:block}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-on{display:none}.jw-icon-cc .jw-svg-icon-cc-off{display:none}.jw-off.jw-icon-cc .jw-svg-icon-cc-off{display:block}.jw-icon-cc .jw-svg-icon-cc-on{display:block}.jw-off.jw-icon-cc .jw-svg-icon-cc-on{display:none}.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:none}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:block}.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:block}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:none}.jw-icon-volume .jw-svg-icon-volume-0{display:none}.jw-off.jw-icon-volume .jw-svg-icon-volume-0{display:block}.jw-icon-volume .jw-svg-icon-volume-100{display:none}.jw-full.jw-icon-volume .jw-svg-icon-volume-100{display:block}.jw-icon-volume .jw-svg-icon-volume-50{display:block}.jw-off.jw-icon-volume .jw-svg-icon-volume-50,.jw-full.jw-icon-volume .jw-svg-icon-volume-50{display:none}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon[aria-checked="true"]::after,.jw-settings-open .jw-icon-settings::after,.jw-icon-volume.jw-open::after{opacity:1}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-cc,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-settings,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-audio-tracks,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-hd,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-settings-sharing,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-fullscreen,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-airplay,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-cast{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-text-live{bottom:6px}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume::after{display:none}.jw-overlays,.jw-controls{pointer-events:none}.jw-controls-backdrop{display:block;background:linear-gradient(to bottom, transparent, rgba(0,0,0,0.4) 77%, rgba(0,0,0,0.4) 100%) 100% 100% / 100% 240px no-repeat transparent;transition:opacity 250ms cubic-bezier(0, .25, .25, 1),background-size 250ms cubic-bezier(0, .25, .25, 1);pointer-events:none}.jw-overlays{cursor:auto}.jw-controls{overflow:hidden}.jw-flag-small-player .jw-controls{text-align:center}.jw-text{height:1em;font-family:Arial,Helvetica,sans-serif;font-size:.75em;font-style:normal;font-weight:normal;color:#fff;text-align:center;font-variant:normal;font-stretch:normal}.jw-controlbar,.jw-skip,.jw-display-icon-container .jw-icon,.jw-nextup-container,.jw-autostart-mute,.jw-overlays .jw-plugin{pointer-events:all}.jwplayer .jw-display-icon-container,.jw-error .jw-display-icon-container{width:auto;height:auto;box-sizing:content-box}.jw-display{display:table;height:100%;padding:57px 0;position:relative;width:100%}.jw-flag-dragging .jw-display{display:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-display-container{display:table-cell;height:100%;text-align:center;vertical-align:middle}.jw-display-controls{display:inline-block}.jwplayer .jw-display-icon-container{float:left}.jw-display-icon-container{display:inline-block;padding:5.5px;margin:0 22px}.jw-display-icon-container .jw-icon{height:75px;width:75px;cursor:pointer;display:flex;justify-content:center;align-items:center}.jw-display-icon-container .jw-icon .jw-svg-icon{height:33px;width:33px;padding:0;position:relative}.jw-display-icon-container .jw-icon .jw-svg-icon-rewind{padding:.2em .05em}.jw-breakpoint--1 .jw-nextup-container{display:none}.jw-breakpoint-0 .jw-display-icon-next,.jw-breakpoint--1 .jw-display-icon-next,.jw-breakpoint-0 .jw-display-icon-rewind,.jw-breakpoint--1 .jw-display-icon-rewind{display:none}.jw-breakpoint-0 .jw-display .jw-icon,.jw-breakpoint--1 .jw-display .jw-icon,.jw-breakpoint-0 .jw-display .jw-svg-icon,.jw-breakpoint--1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-0 .jw-display .jw-icon:before,.jw-breakpoint--1 .jw-display .jw-icon:before,.jw-breakpoint-0 .jw-display .jw-svg-icon:before,.jw-breakpoint--1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon,.jw-breakpoint-1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-1 .jw-display .jw-icon:before,.jw-breakpoint-1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon.jw-icon-rewind:before{width:33px;height:33px}.jw-breakpoint-2 .jw-display .jw-icon,.jw-breakpoint-3 .jw-display .jw-icon,.jw-breakpoint-2 .jw-display .jw-svg-icon,.jw-breakpoint-3 .jw-display .jw-svg-icon{width:77px;height:77px;line-height:77px}.jw-breakpoint-2 .jw-display .jw-icon:before,.jw-breakpoint-3 .jw-display .jw-icon:before,.jw-breakpoint-2 .jw-display .jw-svg-icon:before,.jw-breakpoint-3 .jw-display .jw-svg-icon:before{width:38.5px;height:38.5px}.jw-breakpoint-4 .jw-display .jw-icon,.jw-breakpoint-5 .jw-display .jw-icon,.jw-breakpoint-6 .jw-display .jw-icon,.jw-breakpoint-7 .jw-display .jw-icon,.jw-breakpoint-4 .jw-display .jw-svg-icon,.jw-breakpoint-5 .jw-display .jw-svg-icon,.jw-breakpoint-6 .jw-display .jw-svg-icon,.jw-breakpoint-7 .jw-display .jw-svg-icon{width:88px;height:88px;line-height:88px}.jw-breakpoint-4 .jw-display .jw-icon:before,.jw-breakpoint-5 .jw-display .jw-icon:before,.jw-breakpoint-6 .jw-display .jw-icon:before,.jw-breakpoint-7 .jw-display .jw-icon:before,.jw-breakpoint-4 .jw-display .jw-svg-icon:before,.jw-breakpoint-5 .jw-display .jw-svg-icon:before,.jw-breakpoint-6 .jw-display .jw-svg-icon:before,.jw-breakpoint-7 .jw-display .jw-svg-icon:before{width:44px;height:44px}.jw-controlbar{display:flex;flex-flow:row wrap;align-items:center;justify-content:center;position:absolute;left:0;bottom:0;width:100%;border:none;border-radius:0;background-size:auto;box-shadow:none;max-height:72px;transition:250ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s}.jw-breakpoint-7 .jw-controlbar{max-height:140px}.jw-breakpoint-7 .jw-controlbar .jw-button-container{padding:0 48px 20px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-tooltip{margin-bottom:-7px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-overlay{padding-bottom:40%}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text{font-size:1em}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text.jw-text-elapsed{justify-content:flex-end}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume{height:60px;width:60px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline .jw-svg-icon,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time{padding:0 60px;height:34px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time .jw-slider-container{height:10px}.jw-controlbar .jw-button-image{background:no-repeat 50% 50%;background-size:contain;max-height:24px}.jw-controlbar .jw-spacer{flex:1 1 auto;align-self:stretch}.jw-controlbar .jw-icon.jw-button-color:hover{color:#fff}.jw-button-container{display:flex;flex-flow:row nowrap;flex:1 1 auto;align-items:center;justify-content:center;width:100%;padding:0 12px}.jw-slider-horizontal{background-color:transparent}.jw-icon-inline{position:relative}.jw-icon-inline,.jw-icon-tooltip{height:44px;width:44px;align-items:center;display:flex;justify-content:center}.jw-icon-inline:not(.jw-text),.jw-icon-tooltip,.jw-slider-horizontal{cursor:pointer}.jw-text-elapsed,.jw-text-duration{justify-content:flex-start;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.jw-icon-tooltip{position:relative}.jw-knob:hover,.jw-icon-inline:hover,.jw-icon-tooltip:hover,.jw-icon-display:hover,.jw-option:before:hover{color:#fff}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{pointer-events:none}.jw-icon-cast{display:none;margin:0;padding:0}.jw-icon-cast google-cast-launcher{background-color:transparent;border:none;padding:0;width:24px;height:24px;cursor:pointer}.jw-icon-inline.jw-icon-volume{display:none}.jwplayer .jw-text-countdown{display:none}.jw-flag-small-player .jw-display{padding-top:0;padding-bottom:0}.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-rewind,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-next,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-playback{display:none}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop{opacity:0}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-countdown{display:flex}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-duration,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-duration{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-text-countdown,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-related-btn,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-slider-volume{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-controlbar{flex-direction:column-reverse}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-button-container{height:30px}.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-volume,.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-fullscreen{display:none}.jwplayer:not(.jw-breakpoint-0) .jw-text-duration:before,.jwplayer:not(.jw-breakpoint--1) .jw-text-duration:before{content:"/";padding-right:1ch;padding-left:1ch}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar{will-change:transform}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar .jw-text{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.jw-slider-container{display:flex;align-items:center;position:relative;touch-action:none}.jw-rail,.jw-buffer,.jw-progress{position:absolute;cursor:pointer}.jw-progress{background-color:#f2f2f2}.jw-rail{background-color:rgba(255,255,255,0.3)}.jw-buffer{background-color:rgba(255,255,255,0.3)}.jw-knob{height:13px;width:13px;background-color:#fff;border-radius:50%;box-shadow:0 0 10px rgba(0,0,0,0.4);opacity:1;pointer-events:none;position:absolute;-webkit-transform:translate(-50%, -50%) scale(0);transform:translate(-50%, -50%) scale(0);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform}.jw-flag-dragging .jw-slider-time .jw-knob,.jw-icon-volume:active .jw-slider-volume .jw-knob{box-shadow:0 0 26px rgba(0,0,0,0.2),0 0 10px rgba(0,0,0,0.4),0 0 0 6px rgba(255,255,255,0.2)}.jw-slider-horizontal,.jw-slider-vertical{display:flex}.jw-slider-horizontal .jw-slider-container{height:5px;width:100%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue,.jw-slider-horizontal .jw-knob{top:50%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue{-webkit-transform:translate(0, -50%);transform:translate(0, -50%)}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress{height:5px}.jw-slider-horizontal .jw-rail{width:100%}.jw-slider-vertical{align-items:center;flex-direction:column}.jw-slider-vertical .jw-slider-container{height:88px;width:5px}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress,.jw-slider-vertical .jw-knob{left:50%}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress{height:100%;width:5px;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out;bottom:0}.jw-slider-vertical .jw-knob{-webkit-transform:translate(-50%, 50%);transform:translate(-50%, 50%)}.jw-slider-time.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-slider-time,.jw-flag-audio-player .jw-slider-volume{height:17px;width:100%;align-items:center;background:transparent none;padding:0 12px}.jw-slider-time .jw-cue{background-color:rgba(33,33,33,0.8);cursor:pointer;position:absolute;width:6px}.jw-slider-time,.jw-horizontal-volume-container{z-index:1;outline:none}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail,.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer,.jw-slider-time .jw-progress,.jw-horizontal-volume-container .jw-progress,.jw-slider-time .jw-cue,.jw-horizontal-volume-container .jw-cue{-webkit-backface-visibility:hidden;backface-visibility:hidden;height:100%;-webkit-transform:translate(0, -50%) scale(1, .6);transform:translate(0, -50%) scale(1, .6);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out}.jw-slider-time:hover .jw-rail,.jw-horizontal-volume-container:hover .jw-rail,.jw-slider-time:focus .jw-rail,.jw-horizontal-volume-container:focus .jw-rail,.jw-flag-dragging .jw-slider-time .jw-rail,.jw-flag-dragging .jw-horizontal-volume-container .jw-rail,.jw-flag-touch .jw-slider-time .jw-rail,.jw-flag-touch .jw-horizontal-volume-container .jw-rail,.jw-slider-time:hover .jw-buffer,.jw-horizontal-volume-container:hover .jw-buffer,.jw-slider-time:focus .jw-buffer,.jw-horizontal-volume-container:focus .jw-buffer,.jw-flag-dragging .jw-slider-time .jw-buffer,.jw-flag-dragging .jw-horizontal-volume-container .jw-buffer,.jw-flag-touch .jw-slider-time .jw-buffer,.jw-flag-touch .jw-horizontal-volume-container .jw-buffer,.jw-slider-time:hover .jw-progress,.jw-horizontal-volume-container:hover .jw-progress,.jw-slider-time:focus .jw-progress,.jw-horizontal-volume-container:focus .jw-progress,.jw-flag-dragging .jw-slider-time .jw-progress,.jw-flag-dragging .jw-horizontal-volume-container .jw-progress,.jw-flag-touch .jw-slider-time .jw-progress,.jw-flag-touch .jw-horizontal-volume-container .jw-progress,.jw-slider-time:hover .jw-cue,.jw-horizontal-volume-container:hover .jw-cue,.jw-slider-time:focus .jw-cue,.jw-horizontal-volume-container:focus .jw-cue,.jw-flag-dragging .jw-slider-time .jw-cue,.jw-flag-dragging .jw-horizontal-volume-container .jw-cue,.jw-flag-touch .jw-slider-time .jw-cue,.jw-flag-touch .jw-horizontal-volume-container .jw-cue{-webkit-transform:translate(0, -50%) scale(1, 1);transform:translate(0, -50%) scale(1, 1)}.jw-slider-time:hover .jw-knob,.jw-horizontal-volume-container:hover .jw-knob,.jw-slider-time:focus .jw-knob,.jw-horizontal-volume-container:focus .jw-knob{-webkit-transform:translate(-50%, -50%) scale(1);transform:translate(-50%, -50%) scale(1)}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail{background-color:rgba(255,255,255,0.2)}.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer{background-color:rgba(255,255,255,0.4)}.jw-flag-touch .jw-slider-time::before,.jw-flag-touch .jw-horizontal-volume-container::before{height:44px;width:100%;content:"";position:absolute;display:block;bottom:calc(100% - 17px);left:0}.jw-slider-time.jw-tab-focus:focus .jw-rail,.jw-horizontal-volume-container.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time{height:17px;padding:0}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-slider-container{height:10px}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-knob{border-radius:0;border:1px solid rgba(0,0,0,0.75);height:12px;width:10px}.jw-modal{width:284px}.jw-breakpoint-7 .jw-modal,.jw-breakpoint-6 .jw-modal,.jw-breakpoint-5 .jw-modal{height:232px}.jw-breakpoint-4 .jw-modal,.jw-breakpoint-3 .jw-modal{height:192px}.jw-breakpoint-2 .jw-modal,.jw-flag-small-player .jw-modal{bottom:0;right:0;height:100%;width:100%;max-height:none;max-width:none;z-index:2}.jwplayer .jw-rightclick{display:none;position:absolute;white-space:nowrap}.jwplayer .jw-rightclick.jw-open{display:block}.jwplayer .jw-rightclick .jw-rightclick-list{border-radius:1px;list-style:none;margin:0;padding:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item{background-color:rgba(0,0,0,0.8);border-bottom:1px solid #444;margin:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo{color:#fff;display:inline-flex;padding:0 10px 0 0;vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo .jw-svg-icon{height:20px;width:20px}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-link{border:none;color:#fff;display:block;font-size:11px;line-height:1em;padding:15px 23px;text-align:start;text-decoration:none;width:100%}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:last-child{border-bottom:none}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:hover{cursor:pointer}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured{vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link{color:#fff}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link span{color:#fff}.jwplayer .jw-rightclick .jw-info-overlay-item,.jwplayer .jw-rightclick .jw-share-item,.jwplayer .jw-rightclick .jw-shortcuts-item{border:none;background-color:transparent;outline:none;cursor:pointer}.jw-icon-tooltip.jw-open .jw-overlay{opacity:1;pointer-events:auto;transition-delay:0s}.jw-icon-tooltip.jw-open .jw-overlay:focus{outline:none}.jw-icon-tooltip.jw-open .jw-overlay:focus.jw-tab-focus{outline:solid 2px #4d90fe}.jw-slider-time .jw-overlay:before{height:1em;top:auto}.jw-slider-time .jw-icon-tooltip.jw-open .jw-overlay{pointer-events:none}.jw-volume-tip{padding:13px 0 26px}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{height:auto;width:100%;box-shadow:0 0 10px rgba(0,0,0,0.4);color:#fff;display:block;margin:0 0 14px;pointer-events:none;position:relative;z-index:0}.jw-time-tip::after,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{top:100%;position:absolute;left:50%;height:14px;width:14px;border-radius:1px;background-color:currentColor;-webkit-transform-origin:75% 50%;transform-origin:75% 50%;-webkit-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg);z-index:-1}.jw-time-tip .jw-text,.jw-controlbar .jw-tooltip .jw-text,.jw-settings-menu .jw-tooltip .jw-text{background-color:#fff;border-radius:1px;color:#000;font-size:10px;height:auto;line-height:1;padding:7px 10px;display:inline-block;min-width:100%;vertical-align:middle}.jw-controlbar .jw-overlay{position:absolute;bottom:100%;left:50%;margin:0;min-height:44px;min-width:44px;opacity:0;pointer-events:none;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s, 150ms;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);width:100%;z-index:1}.jw-controlbar .jw-overlay .jw-contents{position:relative}.jw-controlbar .jw-option{position:relative;white-space:nowrap;cursor:pointer;list-style:none;height:1.5em;font-family:inherit;line-height:1.5em;padding:0 .5em;font-size:.8em;margin:0}.jw-controlbar .jw-option::before{padding-right:.125em}.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{position:absolute;bottom:100%;left:50%;opacity:0;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:100ms 0s cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility, -webkit-transform;transition-property:opacity, transform, visibility;transition-property:opacity, transform, visibility, -webkit-transform;visibility:hidden;white-space:nowrap;width:auto;z-index:1}.jw-controlbar .jw-tooltip.jw-open,.jw-settings-menu .jw-tooltip.jw-open{opacity:1;-webkit-transform:translate(-50%, -10px);transform:translate(-50%, -10px);transition-duration:150ms;transition-delay:500ms,0s,500ms;visibility:visible}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen{left:auto;right:0;-webkit-transform:translate(0, 0);transform:translate(0, 0)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen.jw-open,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen.jw-open{-webkit-transform:translate(0, -10px);transform:translate(0, -10px)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen::after,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen::after{left:auto;right:9px}.jw-tooltip-time{height:auto;width:0;bottom:100%;line-height:normal;padding:0;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jw-tooltip-time .jw-overlay{bottom:0;min-height:0;width:auto}.jw-tooltip{bottom:57px;display:none;position:absolute}.jw-tooltip .jw-text{height:100%;white-space:nowrap;text-overflow:ellipsis;direction:unset;max-width:246px;overflow:hidden}.jw-flag-audio-player .jw-tooltip{display:none}.jw-flag-small-player .jw-time-thumb{display:none}.jwplayer .jw-shortcuts-tooltip{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column;z-index:1}.jwplayer .jw-shortcuts-tooltip.jw-open{display:flex}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-close{flex:0 0 auto;margin:5px 5px 5px auto}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container{display:flex;flex:1 1 auto;flex-flow:column;font-size:12px;margin:0 20px 20px;overflow-y:auto;padding:5px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar{background-color:transparent;width:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-title{font-weight:bold}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list{display:flex;max-width:340px;margin:0 10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-tooltip-descriptions{width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row{display:flex;align-items:center;justify-content:space-between;margin:10px 0;width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-description{margin-right:10px;max-width:70%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-key{background:#fefefe;color:#333;overflow:hidden;padding:7px 10px;text-overflow:ellipsis;white-space:nowrap}.jw-skip{color:rgba(255,255,255,0.8);cursor:default;position:absolute;display:flex;right:.75em;bottom:56px;padding:.5em;border:1px solid #333;background-color:#000;align-items:center;height:2em}.jw-skip.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-skip.jw-skippable{cursor:pointer;padding:.25em .75em}.jw-skip.jw-skippable:hover{cursor:pointer;color:#fff}.jw-skip.jw-skippable .jw-skip-icon{display:inline;height:24px;width:24px;margin:0}.jw-breakpoint-7 .jw-skip{padding:1.35em 1em;bottom:130px}.jw-breakpoint-7 .jw-skip .jw-text{font-size:1em;font-weight:normal}.jw-breakpoint-7 .jw-skip .jw-icon-inline{height:30px;width:30px}.jw-breakpoint-7 .jw-skip .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-skip .jw-skip-icon{display:none;margin-left:-0.75em;padding:0 .5em;pointer-events:none}.jw-skip .jw-skip-icon .jw-svg-icon-next{display:block;padding:0}.jw-skip .jw-text,.jw-skip .jw-skip-icon{vertical-align:middle;font-size:.7em}.jw-skip .jw-text{font-weight:bold}.jw-cast{background-size:cover;display:none;height:100%;position:relative;width:100%}.jw-cast-container{background:linear-gradient(180deg, rgba(25,25,25,0.75), rgba(25,25,25,0.25), rgba(25,25,25,0));left:0;padding:20px 20px 80px;position:absolute;top:0;width:100%}.jw-cast-text{color:#fff;font-size:1.6em}.jw-breakpoint--1 .jw-cast-text,.jw-breakpoint-0 .jw-cast-text{font-size:1.15em}.jw-breakpoint-1 .jw-cast-text,.jw-breakpoint-2 .jw-cast-text,.jw-breakpoint-3 .jw-cast-text{font-size:1.3em}.jw-nextup-container{position:absolute;bottom:66px;left:0;background-color:transparent;cursor:pointer;margin:0 auto;padding:12px;pointer-events:none;right:0;text-align:right;visibility:hidden;width:100%}.jw-settings-open .jw-nextup-container,.jw-info-open .jw-nextup-container{display:none}.jw-breakpoint-7 .jw-nextup-container{padding:60px}.jw-flag-small-player .jw-nextup-container{padding:0 12px 0 0}.jw-flag-small-player .jw-nextup-container .jw-nextup-title,.jw-flag-small-player .jw-nextup-container .jw-nextup-duration,.jw-flag-small-player .jw-nextup-container .jw-nextup-close{display:none}.jw-flag-small-player .jw-nextup-container .jw-nextup-tooltip{height:30px}.jw-flag-small-player .jw-nextup-container .jw-nextup-header{font-size:12px}.jw-flag-small-player .jw-nextup-container .jw-nextup-body{justify-content:center;align-items:center;padding:.75em .3em}.jw-flag-small-player .jw-nextup-container .jw-nextup-thumbnail{width:50%}.jw-flag-small-player .jw-nextup-container .jw-nextup{max-width:65px}.jw-flag-small-player .jw-nextup-container .jw-nextup.jw-nextup-thumbnail-visible{max-width:120px}.jw-nextup{background:#333;border-radius:0;box-shadow:0 0 10px rgba(0,0,0,0.5);color:rgba(255,255,255,0.8);display:inline-block;max-width:280px;overflow:hidden;opacity:0;position:relative;width:64%;pointer-events:all;-webkit-transform:translate(0, -5px);transform:translate(0, -5px);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform;transition-delay:0s}.jw-nextup:hover .jw-nextup-tooltip{color:#fff}.jw-nextup.jw-nextup-thumbnail-visible{max-width:400px}.jw-nextup.jw-nextup-thumbnail-visible .jw-nextup-thumbnail{display:block}.jw-nextup-container-visible{visibility:visible}.jw-nextup-container-visible .jw-nextup{opacity:1;-webkit-transform:translate(0, 0);transform:translate(0, 0);transition-delay:0s, 0s, 150ms}.jw-nextup-tooltip{display:flex;height:80px}.jw-nextup-thumbnail{width:120px;background-position:center;background-size:cover;flex:0 0 auto;display:none}.jw-nextup-body{flex:1 1 auto;overflow:hidden;padding:.75em .875em;display:flex;flex-flow:column wrap;justify-content:space-between}.jw-nextup-header,.jw-nextup-title{font-size:14px;line-height:1.35}.jw-nextup-header{font-weight:bold}.jw-nextup-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.jw-nextup-duration{align-self:flex-end;text-align:right;font-size:12px}.jw-nextup-close{height:24px;width:24px;border:none;color:rgba(255,255,255,0.8);cursor:pointer;margin:6px;visibility:hidden}.jw-nextup-close:hover{color:#fff}.jw-nextup-sticky .jw-nextup-close{visibility:visible}.jw-autostart-mute{position:absolute;bottom:0;right:12px;height:44px;width:44px;background-color:rgba(33,33,33,0.4);padding:5px 4px 5px 6px;display:none}.jwplayer.jw-flag-autostart:not(.jw-flag-media-audio) .jw-nextup{display:none}.jw-settings-menu{position:absolute;bottom:57px;right:12px;align-items:flex-start;background-color:#333;display:none;flex-flow:column nowrap;max-width:284px;pointer-events:auto}.jw-settings-open .jw-settings-menu{display:flex}.jw-breakpoint-7 .jw-settings-menu{bottom:130px;right:60px;max-height:none;max-width:none;height:35%;width:25%}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline{height:60px;width:60px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-tooltip .jw-text{font-size:1em}.jw-breakpoint-7 .jw-settings-menu .jw-settings-back{min-width:60px}.jw-breakpoint-6 .jw-settings-menu,.jw-breakpoint-5 .jw-settings-menu{height:232px;width:284px;max-height:232px}.jw-breakpoint-4 .jw-settings-menu,.jw-breakpoint-3 .jw-settings-menu{height:192px;width:284px;max-height:192px}.jw-breakpoint-2 .jw-settings-menu{height:179px;width:284px;max-height:179px}.jw-flag-small-player .jw-settings-menu{max-width:none}.jw-settings-menu .jw-icon.jw-button-color::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon.jw-button-color[aria-checked="true"]::after{opacity:1}.jw-settings-menu .jw-settings-reset{text-decoration:underline}.jw-settings-topbar{align-items:center;background-color:rgba(0,0,0,0.4);display:flex;flex:0 0 auto;padding:3px 5px 0;width:100%}.jw-settings-topbar.jw-nested-menu-open{padding:0}.jw-settings-topbar.jw-nested-menu-open .jw-icon:not(.jw-settings-close):not(.jw-settings-back){display:none}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-close{width:20px}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-arrow-left{height:12px}.jw-settings-topbar.jw-nested-menu-open .jw-settings-topbar-text{display:block;outline:none}.jw-settings-topbar .jw-settings-back{min-width:44px}.jw-settings-topbar .jw-settings-topbar-buttons{display:inherit;width:100%;height:100%}.jw-settings-topbar .jw-settings-topbar-text{display:none;color:#fff;font-size:13px;width:100%}.jw-settings-topbar .jw-settings-close{margin-left:auto}.jw-settings-submenu{display:none;flex:1 1 auto;overflow-y:auto;padding:8px 20px 0 5px}.jw-settings-submenu::-webkit-scrollbar{background-color:transparent;width:6px}.jw-settings-submenu::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-settings-submenu.jw-settings-submenu-active{display:block}.jw-settings-submenu .jw-submenu-topbar{box-shadow:0 2px 9px 0 #1d1d1d;background-color:#2f2d2d;margin:-8px -20px 0 -5px}.jw-settings-submenu .jw-submenu-topbar .jw-settings-content-item{cursor:pointer;text-align:right;padding-right:15px;text-decoration:underline}.jw-settings-submenu .jw-settings-value-wrapper{float:right;display:flex;align-items:center}.jw-settings-submenu .jw-settings-value-wrapper .jw-settings-content-item-arrow{display:flex}.jw-settings-submenu .jw-settings-value-wrapper .jw-svg-icon-arrow-right{width:8px;margin-left:5px;height:12px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item{font-size:1em;padding:11px 15px 11px 30px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-settings-item-active::before{justify-content:flex-end}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-auto-label{font-size:.85em;padding-left:10px}.jw-flag-touch .jw-settings-submenu{overflow-y:scroll;-webkit-overflow-scrolling:touch}.jw-auto-label{font-size:10px;font-weight:initial;opacity:.75;padding-left:5px}.jw-settings-content-item{position:relative;color:rgba(255,255,255,0.8);cursor:pointer;font-size:12px;line-height:1;padding:7px 0 7px 15px;width:100%;text-align:left;outline:none}.jw-settings-content-item:hover{color:#fff}.jw-settings-content-item:focus{font-weight:bold}.jw-flag-small-player .jw-settings-content-item{line-height:1.75}.jw-settings-content-item.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-settings-item-active{font-weight:bold;position:relative}.jw-settings-item-active::before{height:100%;width:1em;align-items:center;content:"\\2022";display:inline-flex;justify-content:center}.jw-breakpoint-2 .jw-settings-open .jw-display-container,.jw-flag-small-player .jw-settings-open .jw-display-container,.jw-flag-touch .jw-settings-open .jw-display-container{display:none}.jw-breakpoint-2 .jw-settings-open.jw-controls,.jw-flag-small-player .jw-settings-open.jw-controls,.jw-flag-touch .jw-settings-open.jw-controls{z-index:1}.jw-flag-small-player .jw-settings-open .jw-controlbar{display:none}.jw-settings-open .jw-icon-settings::after{opacity:1}.jw-settings-open .jw-tooltip-settings{display:none}.jw-sharing-link{cursor:pointer}.jw-shortcuts-container .jw-switch{position:relative;display:inline-block;transition:ease-out .15s;transition-property:opacity, background;border-radius:18px;width:80px;height:20px;padding:10px;background:rgba(80,80,80,0.8);cursor:pointer;font-size:inherit;vertical-align:middle}.jw-shortcuts-container .jw-switch.jw-tab-focus{outline:solid 2px #4d90fe}.jw-shortcuts-container .jw-switch .jw-switch-knob{position:absolute;top:2px;left:1px;transition:ease-out .15s;box-shadow:0 0 10px rgba(0,0,0,0.4);border-radius:13px;width:15px;height:15px;background:#fefefe}.jw-shortcuts-container .jw-switch:before,.jw-shortcuts-container .jw-switch:after{position:absolute;top:3px;transition:inherit;color:#fefefe}.jw-shortcuts-container .jw-switch:before{content:attr(data-jw-switch-disabled);right:8px}.jw-shortcuts-container .jw-switch:after{content:attr(data-jw-switch-enabled);left:8px;opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]{background:#475470}.jw-shortcuts-container .jw-switch[aria-checked="true"]:before{opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]:after{opacity:1}.jw-shortcuts-container .jw-switch[aria-checked="true"] .jw-switch-knob{left:60px}.jw-idle-icon-text{display:none;line-height:1;position:absolute;text-align:center;text-indent:.35em;top:100%;white-space:nowrap;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.jw-idle-label{border-radius:50%;color:#fff;-webkit-filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));font:normal 16px/1 Arial,Helvetica,sans-serif;position:relative;transition:background-color 150ms cubic-bezier(0, .25, .25, 1);transition-property:background-color,-webkit-filter;transition-property:background-color,filter;transition-property:background-color,filter,-webkit-filter;-webkit-font-smoothing:antialiased}.jw-state-idle .jw-icon-display.jw-idle-label .jw-idle-icon-text{display:block}.jw-state-idle .jw-icon-display.jw-idle-label .jw-svg-icon-play{-webkit-transform:scale(.7, .7);transform:scale(.7, .7)}.jw-breakpoint-0.jw-state-idle .jw-icon-display.jw-idle-label,.jw-breakpoint--1.jw-state-idle .jw-icon-display.jw-idle-label{font-size:12px}.jw-info-overlay{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column}.jw-info-overlay .jw-info-close{flex:0 0 auto;margin:5px 5px 5px auto}.jw-info-open .jw-info-overlay{display:flex}.jw-info-container{display:flex;flex:1 1 auto;flex-flow:column;margin:0 20px 20px;overflow-y:auto;padding:5px}.jw-info-container [class*="jw-info"]:not(:first-of-type){color:rgba(255,255,255,0.8);padding-top:10px;font-size:12px}.jw-info-container .jw-info-description{margin-bottom:30px;text-align:start}.jw-info-container .jw-info-description:empty{display:none}.jw-info-container .jw-info-duration{text-align:start}.jw-info-container .jw-info-title{text-align:start;font-size:12px;font-weight:bold}.jw-info-container::-webkit-scrollbar{background-color:transparent;width:6px}.jw-info-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-info-clientid{align-self:flex-end;font-size:12px;color:rgba(255,255,255,0.8);margin:0 20px 20px 44px;text-align:right}.jw-flag-touch .jw-info-open .jw-display-container{display:none}@supports ((-webkit-filter: drop-shadow(0 0 3px #000)) or (filter: drop-shadow(0 0 3px #000))){.jwplayer.jw-ab-drop-shadow .jw-controls .jw-svg-icon,.jwplayer.jw-ab-drop-shadow .jw-controls .jw-icon.jw-text,.jwplayer.jw-ab-drop-shadow .jw-slider-container .jw-rail,.jwplayer.jw-ab-drop-shadow .jw-title{text-shadow:none;box-shadow:none;-webkit-filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3));filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3))}.jwplayer.jw-ab-drop-shadow .jw-button-color{opacity:.8;transition-property:color, opacity}.jwplayer.jw-ab-drop-shadow .jw-button-color:not(:hover){color:#fff;opacity:.8}.jwplayer.jw-ab-drop-shadow .jw-button-color:hover{opacity:1}.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 0.00787) 10.79%, hsla(0, 0%, 0%, 0.02963) 21.99%, hsla(0, 0%, 0%, 0.0625) 33.34%, hsla(0, 0%, 0%, 0.1037) 44.59%, hsla(0, 0%, 0%, 0.15046) 55.48%, hsla(0, 0%, 0%, 0.2) 65.75%, hsla(0, 0%, 0%, 0.24954) 75.14%, hsla(0, 0%, 0%, 0.2963) 83.41%, hsla(0, 0%, 0%, 0.3375) 90.28%, hsla(0, 0%, 0%, 0.37037) 95.51%, hsla(0, 0%, 0%, 0.39213) 98.83%, hsla(0, 0%, 0%, 0.4));mix-blend-mode:multiply;transition-property:opacity}.jw-state-idle.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0.2), hsla(0, 0%, 0%, 0.19606) 1.17%, hsla(0, 0%, 0%, 0.18519) 4.49%, hsla(0, 0%, 0%, 0.16875) 9.72%, hsla(0, 0%, 0%, 0.14815) 16.59%, hsla(0, 0%, 0%, 0.12477) 24.86%, hsla(0, 0%, 0%, 0.1) 34.25%, hsla(0, 0%, 0%, 0.07523) 44.52%, hsla(0, 0%, 0%, 0.05185) 55.41%, hsla(0, 0%, 0%, 0.03125) 66.66%, hsla(0, 0%, 0%, 0.01481) 78.01%, hsla(0, 0%, 0%, 0.00394) 89.21%, hsla(0, 0%, 0%, 0));background-size:100% 7rem;background-position:50% 0}.jwplayer.jw-ab-drop-shadow.jw-state-idle .jw-controls{background-color:transparent}}.jw-video-thumbnail-container{position:relative;overflow:hidden}.jw-video-thumbnail-container:not(.jw-related-shelf-item-image){height:100%;width:100%}.jw-video-thumbnail-container.jw-video-thumbnail-generated{position:absolute;top:0;left:0}.jw-video-thumbnail-container:hover,.jw-related-item-content:hover .jw-video-thumbnail-container,.jw-related-shelf-item:hover .jw-video-thumbnail-container{cursor:pointer}.jw-video-thumbnail-container:hover .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-item-content:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-shelf-item:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail{position:absolute;top:50%;left:50%;bottom:unset;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:100%;height:auto;min-width:100%;min-height:100%;opacity:0;transition:opacity .3s ease;object-fit:cover;background:#000}.jw-related-item-next-up .jw-video-thumbnail-container .jw-video-thumbnail{height:100%;width:auto}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-visible:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-completed{opacity:0}.jw-video-thumbnail-container .jw-video-thumbnail~.jw-svg-icon-play{display:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-shelf-item-aspect{pointer-events:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-item-poster-content{pointer-events:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-state-idle .jw-controls{background:rgba(0,0,0,0.4)}.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay),.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay){display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon:focus{border:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon .jw-svg-icon-buffer{-webkit-animation:jw-spin 2s linear infinite;animation:jw-spin 2s linear infinite;display:block}@-webkit-keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jwplayer.jw-state-buffering .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-pause{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-pause{display:block}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-controls-backdrop{opacity:0}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-logo-bottom-left,.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio):not(.jw-flag-autostart) .jw-logo-bottom-right{bottom:0}.jwplayer .jw-icon-playback .jw-svg-icon-stop{display:none}.jwplayer.jw-state-paused .jw-svg-icon-pause,.jwplayer.jw-state-idle .jw-svg-icon-pause,.jwplayer.jw-state-error .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-svg-icon-pause{display:none}.jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-complete .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-play{display:none}.jwplayer:not(.jw-state-buffering) .jw-svg-icon-buffer{display:none}.jwplayer:not(.jw-state-complete) .jw-svg-icon-replay{display:none}.jwplayer:not(.jw-state-error) .jw-svg-icon-error{display:none}.jwplayer.jw-state-complete .jw-display .jw-icon-display .jw-svg-icon-replay{display:block}.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-state-complete .jw-controls{background:rgba(0,0,0,0.4);height:100%}.jw-state-idle .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-state-idle .jw-display-icon-rewind,.jwplayer.jw-state-buffering .jw-display-icon-rewind,.jwplayer.jw-state-complete .jw-display-icon-rewind,body .jw-error .jw-display-icon-rewind,body .jwplayer.jw-state-error .jw-display-icon-rewind,.jw-state-idle .jw-display-icon-next,.jwplayer.jw-state-buffering .jw-display-icon-next,.jwplayer.jw-state-complete .jw-display-icon-next,body .jw-error .jw-display-icon-next,body .jwplayer.jw-state-error .jw-display-icon-next{display:none}body .jw-error .jw-icon-display,body .jwplayer.jw-state-error .jw-icon-display{cursor:default}body .jw-error .jw-icon-display .jw-svg-icon-error,body .jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-error{display:block}body .jw-error .jw-icon-container{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-preview{display:none}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title{padding-top:4px}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-primary{width:auto;display:inline-block;padding-right:.5ch}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-secondary{width:auto;display:inline-block;padding-left:0}body .jwplayer.jw-state-error .jw-controlbar,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-controlbar{display:none}body .jwplayer.jw-state-error .jw-settings-menu,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-settings-menu{height:100%;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}body .jwplayer.jw-state-error .jw-display,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-display{padding:0}body .jwplayer.jw-state-error .jw-logo-bottom-left,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-left,body .jwplayer.jw-state-error .jw-logo-bottom-right,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-right{bottom:0}.jwplayer.jw-state-playing.jw-flag-user-inactive .jw-display{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-state-playing:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display,.jwplayer.jw-state-paused:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting):not(.jw-flag-play-rejected) .jw-display{display:none}.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-rewind,.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-next{display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-text,.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-flag-casting:not(.jw-flag-audio-player) .jw-cast{display:block}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-display-icon-container{display:none}.jwplayer.jw-flag-casting .jw-icon-hd,.jwplayer.jw-flag-casting .jw-captions,.jwplayer.jw-flag-casting .jw-icon-fullscreen,.jwplayer.jw-flag-casting .jw-icon-audio-tracks{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-volume{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-airplay{color:#fff}.jw-state-playing.jw-flag-casting:not(.jw-flag-audio-player) .jw-display,.jw-state-paused.jw-flag-casting:not(.jw-flag-audio-player) .jw-display{display:table}.jwplayer.jw-flag-cast-available .jw-icon-cast,.jwplayer.jw-flag-cast-available .jw-icon-airplay{display:flex}.jwplayer.jw-flag-cardboard-available .jw-icon-cardboard{display:flex}.jwplayer.jw-flag-live .jw-display-icon-rewind{visibility:hidden}.jwplayer.jw-flag-live .jw-controlbar .jw-text-elapsed,.jwplayer.jw-flag-live .jw-controlbar .jw-text-duration,.jwplayer.jw-flag-live .jw-controlbar .jw-text-countdown,.jwplayer.jw-flag-live .jw-controlbar .jw-slider-time{display:none}.jwplayer.jw-flag-live .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-live .jw-controlbar .jw-overlay:after{display:none}.jwplayer.jw-flag-live .jw-nextup-container{bottom:44px}.jwplayer.jw-flag-live .jw-text-elapsed,.jwplayer.jw-flag-live .jw-text-duration{display:none}.jwplayer.jw-flag-live .jw-text-live{cursor:default}.jwplayer.jw-flag-live .jw-text-live:hover{color:rgba(255,255,255,0.8)}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-stop,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-stop{display:block}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-text-live{height:24px;width:auto;align-items:center;border-radius:1px;color:rgba(255,255,255,0.8);display:flex;font-size:12px;font-weight:bold;margin-right:10px;padding:0 1ch;text-rendering:geometricPrecision;text-transform:uppercase;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:box-shadow,color}.jw-text-live::before{height:8px;width:8px;background-color:currentColor;border-radius:50%;margin-right:6px;opacity:1;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-text-live.jw-dvr-live{box-shadow:inset 0 0 0 2px currentColor}.jw-text-live.jw-dvr-live::before{opacity:.5}.jw-text-live.jw-dvr-live:hover{color:#fff}.jwplayer.jw-flag-controls-hidden .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-controls-hidden:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-controls-hidden .jw-plugin{bottom:.5em}.jwplayer.jw-flag-controls-hidden .jw-nextup-container{bottom:0}.jw-flag-controls-hidden .jw-controlbar,.jw-flag-controls-hidden .jw-display{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-controls-hidden .jw-controls-backdrop{opacity:0}.jw-flag-controls-hidden .jw-logo{visibility:visible}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-plugin{bottom:.5em}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-nextup-container{bottom:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-controls-hidden) .jw-media{cursor:none;-webkit-cursor-visibility:auto-hide}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing.jw-flag-casting .jw-display{display:table}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-ads) .jw-autostart-mute{display:flex}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting .jw-nextup-container{bottom:66px}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting.jw-state-idle .jw-nextup-container{display:none}.jw-flag-media-audio .jw-preview{display:block}.jwplayer.jw-flag-ads .jw-preview,.jwplayer.jw-flag-ads .jw-logo,.jwplayer.jw-flag-ads .jw-captions.jw-captions-enabled,.jwplayer.jw-flag-ads .jw-nextup-container,.jwplayer.jw-flag-ads .jw-text-duration,.jwplayer.jw-flag-ads .jw-text-elapsed{display:none}.jwplayer.jw-flag-ads video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-rewind,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-next,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-display{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player.jw-state-buffering .jw-display-icon-display{display:inline-block}.jwplayer.jw-flag-ads .jw-controlbar{flex-wrap:wrap-reverse}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time{height:auto;padding:0;pointer-events:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-slider-container{height:5px}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-rail,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-knob,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-buffer,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-cue,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-icon-settings{display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-progress{-webkit-transform:none;transform:none;top:auto}.jwplayer.jw-flag-ads .jw-controlbar .jw-tooltip,.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-tooltip:not(.jw-icon-volume),.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-inline:not(.jw-icon-playback):not(.jw-icon-fullscreen):not(.jw-icon-volume){display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-volume-tip{padding:13px 0}.jwplayer.jw-flag-ads .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid) .jw-controls .jw-controlbar,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart .jw-controls .jw-controlbar{display:flex;pointer-events:all;visibility:visible;opacity:1}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-user-inactive .jw-controls-backdrop,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart.jw-flag-user-inactive .jw-controls-backdrop{opacity:1;background-size:100% 60px}.jwplayer.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-ads-vpaid .jw-skip,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-skip{display:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls{background:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls::after{content:none}.jwplayer.jw-flag-ads-hide-controls .jw-controls-backdrop,.jwplayer.jw-flag-ads-hide-controls .jw-controls{display:none !important}.jw-flag-overlay-open-related .jw-controls,.jw-flag-overlay-open-related .jw-title,.jw-flag-overlay-open-related .jw-logo{display:none}.jwplayer.jw-flag-rightclick-open{overflow:visible}.jwplayer.jw-flag-rightclick-open .jw-rightclick{z-index:16777215}body .jwplayer.jw-flag-flash-blocked .jw-controls,body .jwplayer.jw-flag-flash-blocked .jw-overlays,body .jwplayer.jw-flag-flash-blocked .jw-controls-backdrop,body .jwplayer.jw-flag-flash-blocked .jw-preview{display:none}body .jwplayer.jw-flag-flash-blocked .jw-error-msg{top:25%}.jw-flag-touch.jw-breakpoint-7 .jw-captions,.jw-flag-touch.jw-breakpoint-6 .jw-captions,.jw-flag-touch.jw-breakpoint-5 .jw-captions,.jw-flag-touch.jw-breakpoint-4 .jw-captions,.jw-flag-touch.jw-breakpoint-7 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-6 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-5 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-4 .jw-nextup-container{bottom:4.25em}.jw-flag-touch .jw-controlbar .jw-icon-volume{display:flex}.jw-flag-touch .jw-display,.jw-flag-touch .jw-display-container,.jw-flag-touch .jw-display-controls{pointer-events:none}.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-rewind,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-rewind{display:none}.jw-flag-touch.jw-state-paused.jw-flag-dragging .jw-display{display:none}.jw-flag-audio-player{background-color:#000}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:44px}.jw-flag-audio-player:not(.jw-flag-live) .jw-spacer{display:none}.jw-flag-audio-player .jw-preview,.jw-flag-audio-player .jw-display,.jw-flag-audio-player .jw-title,.jw-flag-audio-player .jw-nextup-container{display:none}.jw-flag-audio-player .jw-controlbar{position:relative}.jw-flag-audio-player .jw-controlbar .jw-button-container{padding-right:3px;padding-left:0}.jw-flag-audio-player .jw-controlbar .jw-icon-tooltip,.jw-flag-audio-player .jw-controlbar .jw-icon-inline{display:none}.jw-flag-audio-player .jw-controlbar .jw-icon-volume,.jw-flag-audio-player .jw-controlbar .jw-icon-playback,.jw-flag-audio-player .jw-controlbar .jw-icon-next,.jw-flag-audio-player .jw-controlbar .jw-icon-rewind,.jw-flag-audio-player .jw-controlbar .jw-icon-cast,.jw-flag-audio-player .jw-controlbar .jw-text-live,.jw-flag-audio-player .jw-controlbar .jw-icon-airplay,.jw-flag-audio-player .jw-controlbar .jw-logo-button,.jw-flag-audio-player .jw-controlbar .jw-text-elapsed,.jw-flag-audio-player .jw-controlbar .jw-text-duration{display:flex;flex:0 0 auto}.jw-flag-audio-player .jw-controlbar .jw-text-duration,.jw-flag-audio-player .jw-controlbar .jw-text-countdown{padding-right:10px}.jw-flag-audio-player .jw-controlbar .jw-slider-time{flex:0 1 auto;align-items:center;display:flex;order:1}.jw-flag-audio-player .jw-controlbar .jw-icon-volume{margin-right:0;transition:margin-right 150ms cubic-bezier(0, .25, .25, 1)}.jw-flag-audio-player .jw-controlbar .jw-icon-volume .jw-overlay{display:none}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container{transition:width 300ms cubic-bezier(0, .25, .25, 1);width:0}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open{width:140px}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open .jw-slider-volume{padding-right:24px;transition:opacity 300ms;opacity:1}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open~.jw-slider-time{flex:1 1 auto;width:auto;transition:opacity 300ms, width 300ms}.jw-flag-audio-player .jw-controlbar .jw-slider-volume{opacity:0}.jw-flag-audio-player .jw-controlbar .jw-slider-volume .jw-knob{-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.jw-flag-audio-player .jw-controlbar .jw-slider-volume~.jw-icon-volume{margin-right:140px}.jw-flag-audio-player.jw-breakpoint-1 .jw-horizontal-volume-container.jw-open~.jw-slider-time,.jw-flag-audio-player.jw-breakpoint-2 .jw-horizontal-volume-container.jw-open~.jw-slider-time{opacity:0}.jw-flag-audio-player.jw-flag-small-player .jw-text-elapsed,.jw-flag-audio-player.jw-flag-small-player .jw-text-duration{display:none}.jw-flag-audio-player.jw-flag-ads .jw-slider-time{display:none}.jw-hidden{display:none}',""])}]]); \ No newline at end of file +(window.webpackJsonpjwplayer=window.webpackJsonpjwplayer||[]).push([[6,1,2,3,4,5,7,9],[,,,,,,,,,,,,,,,,,function(t,e,i){"use strict";i.r(e);var n,o=i(8),a=i(3),r=i(7),s=i(43),l=i(5),c=i(15),u=i(40);function d(t){return n||(n=new DOMParser),Object(l.r)(Object(l.s)(n.parseFromString(t,"image/svg+xml").documentElement))}var p=function(t,e,i,n){var o=document.createElement("div");o.className="jw-icon jw-icon-inline jw-button-color jw-reset "+t,o.setAttribute("role","button"),o.setAttribute("tabindex","0"),i&&o.setAttribute("aria-label",i),o.style.display="none";var a=new u.a(o).on("click tap enter",e||function(){});return n&&Array.prototype.forEach.call(n,(function(t){"string"==typeof t?o.appendChild(d(t)):o.appendChild(t)})),{ui:a,element:function(){return o},toggle:function(t){t?this.show():this.hide()},show:function(){o.style.display=""},hide:function(){o.style.display="none"}}},h=i(0),f=i(71),w=i.n(f),g=i(72),j=i.n(g),b=i(73),m=i.n(b),v=i(74),y=i.n(v),k=i(75),x=i.n(k),T=i(76),O=i.n(T),C=i(77),_=i.n(C),M=i(78),S=i.n(M),E=i(79),I=i.n(E),L=i(80),A=i.n(L),P=i(81),R=i.n(P),z=i(82),B=i.n(z),V=i(83),N=i.n(V),H=i(84),F=i.n(H),D=i(85),q=i.n(D),U=i(86),W=i.n(U),Q=i(62),Y=i.n(Q),X=i(87),K=i.n(X),J=i(88),Z=i.n(J),G=i(89),$=i.n(G),tt=i(90),et=i.n(tt),it=i(91),nt=i.n(it),ot=i(92),at=i.n(ot),rt=i(93),st=i.n(rt),lt=i(94),ct=i.n(lt),ut=null;function dt(t){var e=wt().querySelector(ht(t));if(e)return ft(e);throw new Error("Icon not found "+t)}function pt(t){var e=wt().querySelectorAll(t.split(",").map(ht).join(","));if(!e.length)throw new Error("Icons not found "+t);return Array.prototype.map.call(e,(function(t){return ft(t)}))}function ht(t){return".jw-svg-icon-".concat(t)}function ft(t){return t.cloneNode(!0)}function wt(){return ut||(ut=d(""+w.a+j.a+m.a+y.a+x.a+O.a+_.a+S.a+I.a+A.a+R.a+B.a+N.a+F.a+q.a+W.a+Y.a+K.a+Z.a+$.a+et.a+nt.a+at.a+st.a+ct.a+"")),ut}var gt=i(10);function jt(t,e){for(var i=0;i10&&delete bt[e[0]];var i=d(t);bt[t]=i}return bt[t].cloneNode(!0)}(e):((r=document.createElement("div")).className="jw-icon jw-button-image jw-button-color jw-reset",e&&Object(gt.d)(r,{backgroundImage:"url(".concat(e,")")})),s.appendChild(r),new u.a(s).on("click tap enter",n,this),s.addEventListener("mousedown",(function(t){t.preventDefault()})),this.id=o,this.buttonElement=s}var e,i,n;return e=t,(i=[{key:"element",value:function(){return this.buttonElement}},{key:"toggle",value:function(t){t?this.show():this.hide()}},{key:"show",value:function(){this.buttonElement.style.display=""}},{key:"hide",value:function(){this.buttonElement.style.display="none"}}])&&jt(e.prototype,i),n&&jt(e,n),t}(),vt=i(11);function yt(t,e){for(var i=0;i=0&&(e.left-=i,e.right-=i),e},xt=function(){function t(e,i){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),Object(h.g)(this,r.a),this.className=e+" jw-background-color jw-reset",this.orientation=i}var e,i,n;return e=t,(i=[{key:"setup",value:function(){this.el=Object(l.e)(function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return''}(this.className,"jw-slider-"+this.orientation)),this.elementRail=this.el.getElementsByClassName("jw-slider-container")[0],this.elementBuffer=this.el.getElementsByClassName("jw-buffer")[0],this.elementProgress=this.el.getElementsByClassName("jw-progress")[0],this.elementThumb=this.el.getElementsByClassName("jw-knob")[0],this.ui=new u.a(this.element(),{preventScrolling:!0}).on("dragStart",this.dragStart,this).on("drag",this.dragMove,this).on("dragEnd",this.dragEnd,this).on("click tap",this.tap,this)}},{key:"dragStart",value:function(){this.trigger("dragStart"),this.railBounds=kt(this.elementRail)}},{key:"dragEnd",value:function(t){this.dragMove(t),this.trigger("dragEnd")}},{key:"dragMove",value:function(t){var e,i,n=this.railBounds=this.railBounds?this.railBounds:kt(this.elementRail);return i="horizontal"===this.orientation?(e=t.pageX)n.right?100:100*Object(s.a)((e-n.left)/n.width,0,1):(e=t.pageY)>=n.bottom?0:e<=n.top?100:100*Object(s.a)((n.height-(e-n.top))/n.height,0,1),this.render(i),this.update(i),!1}},{key:"tap",value:function(t){this.railBounds=kt(this.elementRail),this.dragMove(t)}},{key:"limit",value:function(t){return t}},{key:"update",value:function(t){this.trigger("update",{percentage:t})}},{key:"render",value:function(t){t=Math.max(0,Math.min(t,100)),"horizontal"===this.orientation?(this.elementThumb.style.left=t+"%",this.elementProgress.style.width=t+"%"):(this.elementThumb.style.bottom=t+"%",this.elementProgress.style.height=t+"%")}},{key:"updateBuffer",value:function(t){this.elementBuffer.style.width=t+"%"}},{key:"element",value:function(){return this.el}}])&&yt(e.prototype,i),n&&yt(e,n),t}(),Tt=function(t,e){t&&e&&(t.setAttribute("aria-label",e),t.setAttribute("role","button"),t.setAttribute("tabindex","0"))};function Ot(t,e){for(var i=0;i0&&Array.prototype.forEach.call(o,(function(t){"string"==typeof t?a.el.appendChild(d(t)):a.el.appendChild(t)}))}var e,i,n;return e=t,(i=[{key:"addContent",value:function(t){this.content&&this.removeContent(),this.content=t,this.tooltip.appendChild(t)}},{key:"removeContent",value:function(){this.content&&(this.tooltip.removeChild(this.content),this.content=null)}},{key:"hasContent",value:function(){return!!this.content}},{key:"element",value:function(){return this.el}},{key:"openTooltip",value:function(t){this.isOpen||(this.trigger("open-"+this.componentType,t,{isOpen:!0}),this.isOpen=!0,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"closeTooltip",value:function(t){this.isOpen&&(this.trigger("close-"+this.componentType,t,{isOpen:!1}),this.isOpen=!1,Object(l.v)(this.el,this.openClass,this.isOpen))}},{key:"toggleOpenState",value:function(t){this.isOpen?this.closeTooltip(t):this.openTooltip(t)}}])&&Ot(e.prototype,i),n&&Ot(e,n),t}(),_t=i(22),Mt=i(57);function St(t,e){for(var i=0;i=this.thumbnails.length&&(e=this.thumbnails.length-1);var i=this.thumbnails[e].img;return i.indexOf("://")<0&&(i=this.vttPath?this.vttPath+"/"+i:i),i},loadThumbnail:function(t){var e=this.chooseThumbnail(t),i={margin:"0 auto",backgroundPosition:"0 0"};if(e.indexOf("#xywh")>0)try{var n=/(.+)#xywh=(\d+),(\d+),(\d+),(\d+)/.exec(e);e=n[1],i.backgroundPosition=-1*n[2]+"px "+-1*n[3]+"px",i.width=n[4],this.timeTip.setWidth(+i.width),i.height=n[5]}catch(t){return}else this.individualImage||(this.individualImage=new Image,this.individualImage.onload=Object(h.a)((function(){this.individualImage.onload=null,this.timeTip.image({width:this.individualImage.width,height:this.individualImage.height}),this.timeTip.setWidth(this.individualImage.width)}),this),this.individualImage.src=e);return i.backgroundImage='url("'+e+'")',i},showThumbnail:function(t){this._model.get("containerWidth")<=420||this.thumbnails.length<1||this.timeTip.image(this.loadThumbnail(t))},resetThumbnails:function(){this.timeTip.image({backgroundImage:"",width:0,height:0}),this.thumbnails=[]}};function Pt(t,e,i){return(Pt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(t,e,i){var n=function(t,e){for(;!Object.prototype.hasOwnProperty.call(t,e)&&null!==(t=Ht(t)););return t}(t,e);if(n){var o=Object.getOwnPropertyDescriptor(n,e);return o.get?o.get.call(i):o.value}})(t,e,i||t)}function Rt(t){return(Rt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function zt(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Bt(t,e){for(var i=0;i-1&&(n="Live")}var d=this.timeTip;d.update(n),this.textLength!==n.length&&(this.textLength=n.length,d.resetWidth()),this.showThumbnail(u),Object(l.a)(d.el,"jw-open");var p=d.getWidth(),h=a.width/100,f=o-a.width,w=0;p>f&&(w=(p-f)/(200*h));var g=100*Math.min(1-w,Math.max(w,c)).toFixed(3);Object(gt.d)(d.el,{left:g+"%"})}}},{key:"hideTimeTooltip",value:function(){Object(l.o)(this.timeTip.el,"jw-open")}},{key:"updateCues",value:function(t,e){var i=this;this.resetCues(),e&&e.length&&(e.forEach((function(t){i.addCue(t)})),this.drawCues())}},{key:"updateAriaText",value:function(){var t=this._model;if(!t.get("seeking")){var e=t.get("position"),i=t.get("duration"),n=Object(vt.timeFormat)(e);"DVR"!==this.streamType&&(n+=" of ".concat(Object(vt.timeFormat)(i)));var o=this.el;document.activeElement!==o&&(this.timeUpdateKeeper.textContent=n),Object(l.t)(o,"aria-valuenow",e),Object(l.t)(o,"aria-valuetext",n)}}},{key:"reset",value:function(){this.resetThumbnails(),this.timeTip.resetWidth(),this.textLength=0}}]),e}(xt);Object(h.g)(Ut.prototype,It,At);var Wt=Ut;function Qt(t,e){for(var i=0;i=75&&!t),Object(l.t)(r,"aria-valuenow",o),Object(l.t)(s,"aria-valuenow",o);var c="Volume ".concat(o,"%");Object(l.t)(r,"aria-valuetext",c),Object(l.t)(s,"aria-valuetext",c),document.activeElement!==r&&document.activeElement!==s&&(this._volumeAnnouncer.textContent=c)}}},{key:"onCastAvailable",value:function(t,e){this.elements.cast.toggle(e)}},{key:"onCastActive",value:function(t,e){this.elements.fullscreen.toggle(!e),this.elements.cast.button&&Object(l.v)(this.elements.cast.button,"jw-off",!e)}},{key:"onElapsed",value:function(t,e){var i,n,o=t.get("duration");if("DVR"===t.get("streamType")){var a=Math.ceil(e),r=this._model.get("dvrSeekLimit");i=n=a>=-r?"":"-"+Object(vt.timeFormat)(-(e+r)),t.set("dvrLive",a>=-r)}else i=Object(vt.timeFormat)(e),n=Object(vt.timeFormat)(o-e);this.elements.elapsed.textContent=i,this.elements.countdown.textContent=n}},{key:"onDuration",value:function(t,e){this.elements.duration.textContent=Object(vt.timeFormat)(Math.abs(e))}},{key:"onAudioMode",value:function(t,e){var i=this.elements.time.element();e?this.elements.buttonContainer.insertBefore(i,this.elements.elapsed):Object(l.m)(this.el,i)}},{key:"element",value:function(){return this.el}},{key:"setAltText",value:function(t,e){this.elements.alt.textContent=e}},{key:"closeMenus",value:function(t){this.menus.forEach((function(e){t&&t.target===e.el||e.closeTooltip(t)}))}},{key:"rewind",value:function(){var t,e=0,i=this._model.get("currentTime");i?t=i-10:(t=this._model.get("position")-10,"DVR"===this._model.get("streamType")&&(e=this._model.get("duration"))),this._api.seek(Math.max(t,e),{reason:"interaction"})}},{key:"onState",value:function(t,e){var i=t.get("localization"),n=i.play;this.setPlayText(n),e===a.pb&&("LIVE"!==t.get("streamType")?(n=i.pause,this.setPlayText(n)):(n=i.stop,this.setPlayText(n))),Object(l.t)(this.elements.play.element(),"aria-label",n)}},{key:"onStreamTypeChange",value:function(t,e){var i="LIVE"===e,n="DVR"===e;this.elements.rewind.toggle(!i),this.elements.live.toggle(i||n),Object(l.t)(this.elements.live.element(),"tabindex",i?"-1":"0"),this.elements.duration.style.display=n?"none":"",this.onDuration(t,t.get("duration")),this.onState(t,t.get("state"))}},{key:"addLogo",value:function(t){var e=this.elements.buttonContainer,i=new mt(t.file,this._model.get("localization").logo,(function(){t.link&&Object(l.l)(t.link,"_blank",{rel:"noreferrer"})}),"logo","jw-logo-button");t.link||Object(l.t)(i.element(),"tabindex","-1"),e.insertBefore(i.element(),e.querySelector(".jw-spacer").nextSibling)}},{key:"goToLiveEdge",value:function(){if("DVR"===this._model.get("streamType")){var t=Math.min(this._model.get("position"),-1),e=this._model.get("dvrSeekLimit");this._api.seek(Math.max(-e,t),{reason:"interaction"}),this._api.play({reason:"interaction"})}}},{key:"updateButtons",value:function(t,e,i){if(e){var n,o,a=this.elements.buttonContainer;e!==i&&i?(n=ce(e,i),o=ce(i,e),this.removeButtons(a,o)):n=e;for(var r=n.length-1;r>=0;r--){var s=n[r],l=new mt(s.img,s.tooltip,s.callback,s.id,s.btnClass);s.tooltip&&ne(l.element(),s.id,s.tooltip);var c=void 0;"related"===l.id?c=this.elements.settingsButton.element():"share"===l.id?c=a.querySelector('[button="related"]')||this.elements.settingsButton.element():(c=this.elements.spacer.nextSibling)&&"logo"===c.getAttribute("button")&&(c=c.nextSibling),a.insertBefore(l.element(),c)}}}},{key:"removeButtons",value:function(t,e){for(var i=e.length;i--;){var n=t.querySelector('[button="'.concat(e[i].id,'"]'));n&&t.removeChild(n)}}},{key:"toggleCaptionsButtonState",value:function(t){var e=this.elements.captionsButton;e&&Object(l.v)(e.element(),"jw-off",!t)}},{key:"destroy",value:function(){var t=this;this._model.off(null,null,this),Object.keys(this.elements).forEach((function(e){var i=t.elements[e];i&&"function"==typeof i.destroy&&t.elements[e].destroy()})),this.ui.forEach((function(t){t.destroy()})),this.ui=[]}}])&&ae(e.prototype,i),n&&ae(e,n),t}(),pe=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return'
    ')+'
    ')+"
    "},he=function(t){return'
    '+pe("rewind",t.rewind)+pe("display",t.playback)+pe("next",t.next)+"
    "};function fe(t,e){for(var i=0;i'.concat(a.playback,"")),Object(l.a)(o.icon,"jw-idle-label"),o.icon.appendChild(s))}return o}var i,n,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&ve(t,e)}(e,t),i=e,(n=[{key:"element",value:function(){return this.el}}])&&je(i.prototype,n),o&&je(i,o),e}(r.a);function ke(t,e){for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:"",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"";return'
    '+'
    '.concat(t,"
    ")+'
    '.concat(e,"
    ")+'
    '.concat(i,"
    ")+"
    "+'')+"
    "}());e.querySelector(".jw-nextup-close").appendChild(dt("close")),this.addContent(e),this.closeButton=this.content.querySelector(".jw-nextup-close"),this.closeButton.setAttribute("aria-label",this.localization.close),this.tooltip=this.content.querySelector(".jw-nextup-tooltip");var i=this._model,n=i.player;this.enabled=!1,i.on("change:nextUp",this.onNextUp,this),n.change("duration",this.onDuration,this),n.change("position",this.onElapsed,this),n.change("streamType",this.onStreamType,this),n.change("state",(function(t,e){"complete"===e&&this.toggle(!1)}),this),this.closeUi=new u.a(this.closeButton,{directSelect:!0}).on("click tap enter",(function(){this.nextUpSticky=!1,this.toggle(!1)}),this),this.tooltipUi=new u.a(this.tooltip).on("click tap",this.click,this)}},{key:"loadThumbnail",value:function(t){return this.nextUpImage=new Image,this.nextUpImage.onload=function(){this.nextUpImage.onload=null}.bind(this),this.nextUpImage.src=t,{backgroundImage:'url("'+t+'")'}}},{key:"click",value:function(){var t=this.feedShownId;this.reset(),this._api.next({feedShownId:t,reason:"interaction"})}},{key:"toggle",value:function(t,e){if(this.enabled&&(Object(l.v)(this.container,"jw-nextup-sticky",!!this.nextUpSticky),this.shown!==t)){this.shown=t,Object(l.v)(this.container,"jw-nextup-container-visible",t),Object(l.v)(this._playerElement,"jw-flag-nextup",t);var i=this._model.get("nextUp");t&&i?(this.feedShownId=Object(oe.b)(oe.a),this.trigger("nextShown",{mode:i.mode,ui:"nextup",itemsShown:[i],feedData:i.feedData,reason:e,feedShownId:this.feedShownId})):this.feedShownId=""}}},{key:"setNextUpItem",value:function(t){var e=this;setTimeout((function(){if(e.thumbnail=e.content.querySelector(".jw-nextup-thumbnail"),Object(l.v)(e.content,"jw-nextup-thumbnail-visible",!!t.image),t.image){var i=e.loadThumbnail(t.image);Object(gt.d)(e.thumbnail,i)}e.header=e.content.querySelector(".jw-nextup-header"),e.header.textContent=Object(l.e)(e.localization.nextUp).textContent,e.title=e.content.querySelector(".jw-nextup-title");var n=t.title;e.title.textContent=n?Object(l.e)(n).textContent:"";var o=t.duration;o&&(e.duration=e.content.querySelector(".jw-nextup-duration"),e.duration.textContent="number"==typeof o?Object(vt.timeFormat)(o):o)}),500)}},{key:"onNextUp",value:function(t,e){this.reset(),e||(e={showNextUp:!1}),this.enabled=!(!e.title&&!e.image),this.enabled&&(e.showNextUp||(this.nextUpSticky=!1,this.toggle(!1)),this.setNextUpItem(e))}},{key:"onDuration",value:function(t,e){if(e){var i=t.get("nextupoffset"),n=-10;i&&(n=Object(_e.d)(i,e)),n<0&&(n+=e),Object(_e.c)(i)&&e-5=this.offset;n&&void 0===i?(this.nextUpSticky=n,this.toggle(n,"time")):!n&&i&&this.reset()}}},{key:"onStreamType",value:function(t,e){"VOD"!==e&&(this.nextUpSticky=!1,this.toggle(!1))}},{key:"element",value:function(){return this.container}},{key:"addContent",value:function(t){this.content&&this.removeContent(),this.content=t,this.container.appendChild(t)}},{key:"removeContent",value:function(){this.content&&(this.container.removeChild(this.content),this.content=null)}},{key:"reset",value:function(){this.nextUpSticky=void 0,this.toggle(!1)}},{key:"destroy",value:function(){this.off(),this._model.off(null,null,this),this.closeUi&&this.closeUi.destroy(),this.tooltipUi&&this.tooltipUi.destroy()}}])&&Me(e.prototype,i),n&&Me(e,n),t}(),Ee=function(t,e){var i=t.featured,n=t.showLogo,o=t.type;return t.logo=n?'':"",'
  • ').concat(Ie[o](t,e),"
  • ")},Ie={link:function(t){var e=t.link,i=t.title,n=t.logo;return'').concat(n).concat(i||"","")},info:function(t,e){return'")},share:function(t,e){return'")},keyboardShortcuts:function(t,e){return'")}},Le=i(23),Ae=i(6),Pe=i(13);function Re(t,e){for(var i=0;iJW Player '.concat(t,""),a={items:[{type:"info"},{title:Object(Pe.e)(n)?"".concat(o," ").concat(n):"".concat(n," ").concat(o),type:"link",featured:!0,showLogo:!0,link:"https://jwplayer.com/learn-more?e=".concat(ze[i])}]},r=e.get("provider"),s=a.items;if(r&&r.name.indexOf("flash")>=0){var l="Flash Version "+Object(Ae.a)();s.push({title:l,type:"link",link:"http://www.adobe.com/software/flash/about/"})}return this.shortcutsTooltip&&s.splice(s.length-1,0,{type:"keyboardShortcuts"}),a}},{key:"rightClick",value:function(t){if(this.lazySetup(),this.mouseOverContext)return!1;this.hideMenu(),this.showMenu(t),this.addHideMenuHandlers()}},{key:"getOffset",value:function(t){var e=Object(l.c)(this.wrapperElement),i=t.pageX-e.left,n=t.pageY-e.top;return this.model.get("touchMode")&&(n-=100),{x:i,y:n}}},{key:"showMenu",value:function(t){var e=this,i=this.getOffset(t);return this.el.style.left=i.x+"px",this.el.style.top=i.y+"px",this.outCount=0,Object(l.a)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.a)(this.el,"jw-open"),clearTimeout(this._menuTimeout),this._menuTimeout=setTimeout((function(){return e.hideMenu()}),3e3),!1}},{key:"hideMenu",value:function(t){t&&this.el&&this.el.contains(t.target)||(Object(l.o)(this.playerContainer,"jw-flag-rightclick-open"),Object(l.o)(this.el,"jw-open"))}},{key:"lazySetup",value:function(){var t,e,i,n,o=this,a=(t=this.buildArray(),e=this.model.get("localization"),i=t.items,n=(void 0===i?[]:i).map((function(t){return Ee(t,e)})),'
    '+'
      '.concat(n.join(""),"
    ")+"
    ");if(this.el){if(this.html!==a){this.html=a;var r=Be(a);Object(l.h)(this.el);for(var s=r.childNodes.length;s--;)this.el.appendChild(r.firstChild)}}else this.html=a,this.el=Be(this.html),this.wrapperElement.appendChild(this.el),this.hideMenuHandler=function(t){return o.hideMenu(t)},this.overHandler=function(){o.mouseOverContext=!0},this.outHandler=function(t){o.mouseOverContext=!1,t.relatedTarget&&!o.el.contains(t.relatedTarget)&&++o.outCount>1&&o.hideMenu()},this.infoOverlayHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.infoOverlay.open()},this.shortcutsTooltipHandler=function(){o.mouseOverContext=!1,o.hideMenu(),o.shortcutsTooltip.open()}}},{key:"setup",value:function(t,e,i){this.wrapperElement=i,this.model=t,this.mouseOverContext=!1,this.playerContainer=e,this.ui=new u.a(i).on("longPress",this.rightClick,this)}},{key:"addHideMenuHandlers",value:function(){this.removeHideMenuHandlers(),this.wrapperElement.addEventListener("touchstart",this.hideMenuHandler),document.addEventListener("touchstart",this.hideMenuHandler),o.OS.mobile||(this.wrapperElement.addEventListener("click",this.hideMenuHandler),document.addEventListener("click",this.hideMenuHandler),this.el.addEventListener("mouseover",this.overHandler),this.el.addEventListener("mouseout",this.outHandler)),this.el.querySelector(".jw-info-overlay-item").addEventListener("click",this.infoOverlayHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").addEventListener("click",this.shortcutsTooltipHandler)}},{key:"removeHideMenuHandlers",value:function(){this.wrapperElement&&(this.wrapperElement.removeEventListener("click",this.hideMenuHandler),this.wrapperElement.removeEventListener("touchstart",this.hideMenuHandler)),this.el&&(this.el.querySelector(".jw-info-overlay-item").removeEventListener("click",this.infoOverlayHandler),this.el.removeEventListener("mouseover",this.overHandler),this.el.removeEventListener("mouseout",this.outHandler),this.shortcutsTooltip&&this.el.querySelector(".jw-shortcuts-item").removeEventListener("click",this.shortcutsTooltipHandler)),document.removeEventListener("click",this.hideMenuHandler),document.removeEventListener("touchstart",this.hideMenuHandler)}},{key:"destroy",value:function(){clearTimeout(this._menuTimeout),this.removeHideMenuHandlers(),this.el&&(this.hideMenu(),this.hideMenuHandler=null,this.el=null),this.wrapperElement&&(this.wrapperElement.oncontextmenu=null,this.wrapperElement=null),this.model&&(this.model=null),this.ui&&(this.ui.destroy(),this.ui=null)}}])&&Re(e.prototype,i),n&&Re(e,n),t}(),Ne=function(t){return'")},He=function(t){return'"},Fe=function(t){return'"};function De(t){return(De="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function qe(t,e){return!e||"object"!==De(e)&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}function Ue(t){return(Ue=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function We(t,e){return(We=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function Qe(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Ye(t,e){for(var i=0;i2&&void 0!==arguments[2]?arguments[2]:Ne;Qe(this,t),this.el=Object(l.e)(n(e)),this.ui=new u.a(this.el).on("click tap enter",i,this)}return Xe(t,[{key:"destroy",value:function(){this.ui.destroy()}}]),t}(),Ze=function(t){function e(t,i){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Fe;return Qe(this,e),qe(this,Ue(e).call(this,t,i,n))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&We(t,e)}(e,t),Xe(e,[{key:"activate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!0),this.el.setAttribute("aria-checked","true"),this.active=!0}},{key:"deactivate",value:function(){Object(l.v)(this.el,"jw-settings-item-active",!1),this.el.setAttribute("aria-checked","false"),this.active=!1}}]),e}(Je),Ge=function(t,e){return t?'':''},$e=function(t,e){var i=t.name,n={captions:"cc-off",audioTracks:"audio-tracks",quality:"quality-100",playbackRates:"playback-rate"}[i];if(n||t.icon){var o=p("jw-settings-".concat(i," jw-submenu-").concat(i),(function(e){t.open(e)}),i,[t.icon&&Object(l.e)(t.icon)||dt(n)]),a=o.element();return a.setAttribute("role","menuitemradio"),a.setAttribute("aria-checked","false"),a.setAttribute("aria-label",e),"ontouchstart"in window||(o.tooltip=ne(a,i,e)),o}};function ti(t){return(ti="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ei(t,e){for(var i=0;i3&&void 0!==arguments[3]?arguments[3]:Ge;return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),a=this,(o=!(r=ii(e).call(this))||"object"!==ti(r)&&"function"!=typeof r?oi(a):r).open=o.open.bind(oi(oi(o))),o.close=o.close.bind(oi(oi(o))),o.toggle=o.toggle.bind(oi(oi(o))),o.onDocumentClick=o.onDocumentClick.bind(oi(oi(o))),o.name=t,o.isSubmenu=!!i,o.el=Object(l.e)(s(o.isSubmenu,t)),o.topbar=o.el.querySelector(".jw-".concat(o.name,"-topbar")),o.buttonContainer=o.el.querySelector(".jw-".concat(o.name,"-topbar-buttons")),o.children={},o.openMenus=[],o.items=[],o.visible=!1,o.parentMenu=i,o.mainMenu=o.parentMenu?o.parentMenu.mainMenu:oi(oi(o)),o.categoryButton=null,o.closeButton=o.parentMenu&&o.parentMenu.closeButton||o.createCloseButton(n),o.isSubmenu?(o.categoryButton=o.parentMenu.categoryButton||o.createCategoryButton(n),o.parentMenu.parentMenu&&!o.mainMenu.backButton&&(o.mainMenu.backButton=o.createBackButton(n)),o.itemsContainer=o.createItemsContainer(),o.parentMenu.appendMenu(oi(oi(o)))):o.ui=ri(oi(oi(o))),o}var i,n,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&ni(t,e)}(e,t),i=e,(n=[{key:"createItemsContainer",value:function(){var t,e,i=this,n=this.el.querySelector(".jw-settings-submenu-items"),o=new u.a(n),a=this.categoryButton&&this.categoryButton.element()||this.parentMenu.categoryButton&&this.parentMenu.categoryButton.element()||this.mainMenu.buttonContainer.firstChild;return this.parentMenu.isSubmenu&&(t=this.mainMenu.closeButton.element(),e=this.mainMenu.backButton.element()),o.on("keydown",(function(o){if(o.target.parentNode===n){var r=function(t,e){t?t.focus():void 0!==e&&n.childNodes[e].focus()},s=o.sourceEvent,c=s.target,u=n.firstChild===c,d=n.lastChild===c,p=i.topbar,h=t||Object(l.k)(a),f=e||Object(l.n)(a),w=Object(l.k)(s.target),g=Object(l.n)(s.target),j=s.key.replace(/(Arrow|ape)/,"");switch(j){case"Tab":r(s.shiftKey?f:h);break;case"Left":r(f||Object(l.n)(document.getElementsByClassName("jw-icon-settings")[0]));break;case"Up":p&&u?r(p.firstChild):r(g,n.childNodes.length-1);break;case"Right":r(h);break;case"Down":p&&d?r(p.firstChild):r(w,0)}s.preventDefault(),"Esc"!==j&&s.stopPropagation()}})),o}},{key:"createCloseButton",value:function(t){var e=p("jw-settings-close",this.close,t.close,[dt("close")]);return this.topbar.appendChild(e.element()),e.show(),e.ui.on("keydown",(function(t){var e=t.sourceEvent,i=e.key.replace(/(Arrow|ape)/,"");("Enter"===i||"Right"===i||"Tab"===i&&!e.shiftKey)&&this.close(t)}),this),this.buttonContainer.appendChild(e.element()),e}},{key:"createCategoryButton",value:function(t){var e=t[{captions:"cc",audioTracks:"audioTracks",quality:"hd",playbackRates:"playbackRates"}[this.name]];"sharing"===this.name&&(e=t.sharing.heading);var i=$e(this,e);return i.element().setAttribute("name",this.name),i}},{key:"createBackButton",value:function(t){var e=p("jw-settings-back",(function(t){Ke&&Ke.open(t)}),t.close,[dt("arrow-left")]);return Object(l.m)(this.mainMenu.topbar,e.element()),e}},{key:"createTopbar",value:function(){var t=Object(l.e)('
    ');return Object(l.m)(this.el,t),t}},{key:"createItems",value:function(t,e){var i=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:Ze,a=this.name,r=t.map((function(t,r){var s,l;switch(a){case"quality":s="Auto"===t.label&&0===r?"".concat(n.defaultText,' '):t.label;break;case"captions":s="Off"!==t.label&&"off"!==t.id||0!==r?t.label:n.defaultText;break;case"playbackRates":l=t,s=Object(Pe.e)(n.tooltipText)?"x"+t:t+"x";break;case"audioTracks":s=t.name}s||(s=t,"object"===ti(t)&&(s.options=n));var c=new o(s,function(t){c.active||(e(l||r),c.deactivate&&(i.items.filter((function(t){return!0===t.active})).forEach((function(t){t.deactivate()})),Ke?Ke.open(t):i.mainMenu.close(t)),c.activate&&c.activate())}.bind(i));return c}));return r}},{key:"setMenuItems",value:function(t,e){var i=this;t?(this.items=[],Object(l.h)(this.itemsContainer.el),t.forEach((function(t){i.items.push(t),i.itemsContainer.el.appendChild(t.el)})),e>-1&&t[e].activate(),this.categoryButton.show()):this.removeMenu()}},{key:"appendMenu",value:function(t){if(t){var e=t.el,i=t.name,n=t.categoryButton;if(this.children[i]=t,n){var o=this.mainMenu.buttonContainer,a=o.querySelector(".jw-settings-sharing"),r="quality"===i?o.firstChild:a||this.closeButton.element();o.insertBefore(n.element(),r)}this.mainMenu.el.appendChild(e)}}},{key:"removeMenu",value:function(t){if(!t)return this.parentMenu.removeMenu(this.name);var e=this.children[t];e&&(delete this.children[t],e.destroy())}},{key:"open",value:function(t){if(!this.visible||this.openMenus){var e;if(Ke=null,this.isSubmenu){var i=this.mainMenu,n=this.parentMenu,o=this.categoryButton;if(n.openMenus.length&&n.closeChildren(),o&&o.element().setAttribute("aria-checked","true"),n.isSubmenu){n.el.classList.remove("jw-settings-submenu-active"),i.topbar.classList.add("jw-nested-menu-open");var a=i.topbar.querySelector(".jw-settings-topbar-text");a.setAttribute("name",this.name),a.innerText=this.title||this.name,i.backButton.show(),Ke=this.parentMenu,e=this.topbar?this.topbar.firstChild:t&&"enter"===t.type?this.items[0].el:a}else i.topbar.classList.remove("jw-nested-menu-open"),i.backButton&&i.backButton.hide();this.el.classList.add("jw-settings-submenu-active"),n.openMenus.push(this.name),i.visible||(i.open(t),this.items&&t&&"enter"===t.type?e=this.topbar?this.topbar.firstChild.focus():this.items[0].el:o.tooltip&&(o.tooltip.suppress=!0,e=o.element())),this.openMenus.length&&this.closeChildren(),e&&e.focus(),this.el.scrollTop=0}else this.el.parentNode.classList.add("jw-settings-open"),this.trigger("menuVisibility",{visible:!0,evt:t}),document.addEventListener("click",this.onDocumentClick);this.visible=!0,this.el.setAttribute("aria-expanded","true")}}},{key:"close",value:function(t){var e=this;this.visible&&(this.visible=!1,this.el.setAttribute("aria-expanded","false"),this.isSubmenu?(this.el.classList.remove("jw-settings-submenu-active"),this.categoryButton.element().setAttribute("aria-checked","false"),this.parentMenu.openMenus=this.parentMenu.openMenus.filter((function(t){return t!==e.name})),!this.mainMenu.openMenus.length&&this.mainMenu.visible&&this.mainMenu.close(t)):(this.el.parentNode.classList.remove("jw-settings-open"),this.trigger("menuVisibility",{visible:!1,evt:t}),document.removeEventListener("click",this.onDocumentClick)),this.openMenus.length&&this.closeChildren())}},{key:"closeChildren",value:function(){var t=this;this.openMenus.forEach((function(e){var i=t.children[e];i&&i.close()}))}},{key:"toggle",value:function(t){this.visible?this.close(t):this.open(t)}},{key:"onDocumentClick",value:function(t){/jw-(settings|video|nextup-close|sharing-link|share-item)/.test(t.target.className)||this.close()}},{key:"destroy",value:function(){var t=this;if(document.removeEventListener("click",this.onDocumentClick),Object.keys(this.children).map((function(e){t.children[e].destroy()})),this.isSubmenu){this.parentMenu.name===this.mainMenu.name&&this.categoryButton&&(this.parentMenu.buttonContainer.removeChild(this.categoryButton.element()),this.categoryButton.ui.destroy()),this.itemsContainer&&this.itemsContainer.destroy();var e=this.parentMenu.openMenus,i=e.indexOf(this.name);e.length&&i>-1&&this.openMenus.splice(i,1),delete this.parentMenu}else this.ui.destroy();this.visible=!1,this.el.parentNode&&this.el.parentNode.removeChild(this.el)}},{key:"defaultChild",get:function(){var t=this.children,e=t.quality,i=t.captions,n=t.audioTracks,o=t.sharing,a=t.playbackRates;return e||i||n||o||a}}])&&ei(i.prototype,n),o&&ei(i,o),e}(r.a),ri=function(t){var e=t.closeButton,i=t.el;return new u.a(i).on("keydown",(function(i){var n=i.sourceEvent,o=i.target,a=Object(l.k)(o),r=Object(l.n)(o),s=n.key.replace(/(Arrow|ape)/,""),c=function(e){r?e||r.focus():t.close(i)};switch(s){case"Esc":t.close(i);break;case"Left":c();break;case"Right":a&&e.element()&&o!==e.element()&&a.focus();break;case"Tab":n.shiftKey&&c(!0);break;case"Up":case"Down":!function(){var e=t.children[o.getAttribute("name")];if(!e&&Ke&&(e=Ke.children[Ke.openMenus]),e)return e.open(i),void(e.topbar?e.topbar.firstChild.focus():e.items&&e.items.length&&e.items[0].el.focus());if(i.target.parentNode.classList.contains("jw-submenu-topbar")){var n=i.target.parentNode.parentNode.querySelector(".jw-settings-submenu-items");("Down"===s?n.childNodes[0]:n.childNodes[n.childNodes.length-1]).focus()}}()}if(n.stopPropagation(),/13|32|37|38|39|40/.test(n.keyCode))return n.preventDefault(),!1}))},si=i(59),li=function(t){return hi[t]},ci=function(t){for(var e,i=Object.keys(hi),n=0;n1;i.elements.settingsButton.toggle(c)};e.change("levels",(function(t,e){r(e)}),o);var s=function(t,i,n){var o=e.get("levels");if(o&&"Auto"===o[0].label&&i&&i.items.length){var a=i.items[0].el.querySelector(".jw-auto-label"),r=o[t.index]||{label:""};a.textContent=n?"":r.label}};e.on("change:visualQuality",(function(t,i){var n=o.children.quality;i&&n&&s(i.level,n,e.get("currentLevel"))})),e.on("change:currentLevel",(function(t,i){var n=o.children.quality,a=e.get("visualQuality");a&&n&&s(a.level,n,i)}),o),e.change("captionsList",(function(i,r){var s={defaultText:n.off},l=e.get("captionsIndex");a("captions",r,(function(e){return t.setCurrentCaptions(e)}),l,s);var c=o.children.captions;if(c&&!c.children.captionsSettings){c.topbar=c.topbar||c.createTopbar();var u=new ai("captionsSettings",c,n);u.title="Subtitle Settings";var d=new Je("Settings",u.open);c.topbar.appendChild(d.el);var p=new Ze("Reset",(function(){e.set("captions",si.a),w()}));p.el.classList.add("jw-settings-reset");var f=e.get("captions"),w=function(){var t=[];pi.forEach((function(i){f&&f[i.propertyName]&&(i.defaultVal=i.getOption(f[i.propertyName]));var o=new ai(i.name,u,n),a=new Je({label:i.name,value:i.defaultVal},o.open,He),r=o.createItems(i.options,(function(t){var n=a.el.querySelector(".jw-settings-content-item-value");!function(t,i){var n=e.get("captions"),o=t.propertyName,a=t.options&&t.options[i],r=t.getTypedValue(a),s=Object(h.g)({},n);s[o]=r,e.set("captions",s)}(i,t),n.innerText=i.options[t]}),null);o.setMenuItems(r,i.options.indexOf(i.defaultVal)||0),t.push(a)})),t.push(p),u.setMenuItems(t)};w()}}));var l=function(t,e){t&&e>-1&&t.items[e].activate()};e.change("captionsIndex",(function(t,e){var n=o.children.captions;n&&l(n,e),i.toggleCaptionsButtonState(!!e)}),o);var c=function(i){if(e.get("supportsPlaybackRate")&&"LIVE"!==e.get("streamType")&&e.get("playbackRateControls")){var r=i.indexOf(e.get("playbackRate")),s={tooltipText:n.playbackRates};a("playbackRates",i,(function(e){return t.setPlaybackRate(e)}),r,s)}else o.children.playbackRates&&o.removeMenu("playbackRates")};e.on("change:playbackRates",(function(t,e){c(e)}),o);var u=function(i){a("audioTracks",i,(function(e){return t.setCurrentAudioTrack(e)}),e.get("currentAudioTrack"))};return e.on("change:audioTracks",(function(t,e){u(e)}),o),e.on("change:playbackRate",(function(t,i){var n=e.get("playbackRates"),a=-1;n&&(a=n.indexOf(i)),l(o.children.playbackRates,a)}),o),e.on("change:currentAudioTrack",(function(t,e){o.children.audioTracks.items[e].activate()}),o),e.on("change:playlistItem",(function(){o.removeMenu("captions"),i.elements.captionsButton.hide(),o.visible&&o.close()}),o),e.on("change:playbackRateControls",(function(){c(e.get("playbackRates"))})),e.on("change:castActive",(function(t,i,n){i!==n&&(i?(o.removeMenu("audioTracks"),o.removeMenu("quality"),o.removeMenu("playbackRates")):(u(e.get("audioTracks")),r(e.get("levels")),c(e.get("playbackRates"))))}),o),e.on("change:streamType",(function(){c(e.get("playbackRates"))}),o),o},wi=i(58),gi=i(35),ji=i(12),bi=function(t,e,i,n){var o=Object(l.e)('
    '),r=!1,s=null,c=!1,u=function(t){/jw-info/.test(t.target.className)||h.close()},d=function(){var n,a,s,c,u,d=p("jw-info-close",(function(){h.close()}),e.get("localization").close,[dt("close")]);d.show(),Object(l.m)(o,d.element()),a=o.querySelector(".jw-info-title"),s=o.querySelector(".jw-info-duration"),c=o.querySelector(".jw-info-description"),u=o.querySelector(".jw-info-clientid"),e.change("playlistItem",(function(t,e){var i=e.description,n=e.title;Object(l.q)(c,i||""),Object(l.q)(a,n||"Unknown Title")})),e.change("duration",(function(t,i){var n="";switch(e.get("streamType")){case"LIVE":n="Live";break;case"DVR":n="DVR";break;default:i&&(n=Object(vt.timeFormat)(i))}s.textContent=n}),h),u.textContent=(n=i.getPlugin("jwpsrv"))&&"function"==typeof n.doNotTrackUser&&n.doNotTrackUser()?"":"Client ID: ".concat(function(){try{return window.localStorage.jwplayerLocalId}catch(t){return"none"}}()),t.appendChild(o),r=!0};var h={open:function(){r||d(),document.addEventListener("click",u),c=!0;var t=e.get("state");t===a.pb&&i.pause("infoOverlayInteraction"),s=t,n(!0)},close:function(){document.removeEventListener("click",u),c=!1,e.get("state")===a.ob&&s===a.pb&&i.play("infoOverlayInteraction"),s=null,n(!1)},destroy:function(){this.close(),e.off(null,null,this)}};return Object.defineProperties(h,{visible:{enumerable:!0,get:function(){return c}}}),h};var mi=function(t,e,i){var n,o=!1,r=null,s=i.get("localization").shortcuts,c=Object(l.e)(function(t,e){var i=t.map((function(t){return'
    '+''.concat(t.description,"")+''.concat(t.key,"")+"
    "})).join("");return'
    ')+'Press shift question mark to access a list of keyboard shortcuts
    '+''.concat(e,"")+'
    '+"".concat(i)+"
    "}(function(t){var e=t.playPause,i=t.volumeToggle,n=t.fullscreenToggle,o=t.seekPercent,a=t.increaseVolume,r=t.decreaseVolume,s=t.seekForward,l=t.seekBackward;return[{key:t.spacebar,description:e},{key:"↑",description:a},{key:"↓",description:r},{key:"→",description:s},{key:"←",description:l},{key:"c",description:t.captionsToggle},{key:"f",description:n},{key:"m",description:i},{key:"0-9",description:o}]}(s),s.keyboardShortcuts)),d={reason:"settingsInteraction"},h=new u.a(c.querySelector(".jw-switch")),f=function(){h.el.setAttribute("aria-checked",i.get("enableShortcuts")),Object(l.a)(c,"jw-open"),r=i.get("state"),c.querySelector(".jw-shortcuts-close").focus(),document.addEventListener("click",g),o=!0,e.pause(d)},w=function(){Object(l.o)(c,"jw-open"),document.removeEventListener("click",g),t.focus(),o=!1,r===a.pb&&e.play(d)},g=function(t){/jw-shortcuts|jw-switch/.test(t.target.className)||w()},j=function(t){var e=t.currentTarget,n="true"!==e.getAttribute("aria-checked");e.setAttribute("aria-checked",n),i.set("enableShortcuts",n)};return n=p("jw-shortcuts-close",w,i.get("localization").close,[dt("close")]),Object(l.m)(c,n.element()),n.show(),t.appendChild(c),h.on("click tap enter",j),{el:c,open:f,close:w,destroy:function(){w(),h.destroy()},toggleVisibility:function(){o?w():f()}}},vi=function(t){return'
    ')+"
    "};function yi(t){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ki(t,e){for(var i=0;i16?n.activeTimeout=setTimeout(n.userInactiveTimeout,t):n.playerContainer.querySelector(".jw-tab-focus")?n.resetActiveTimeout():n.userInactive()},n}var i,n,r;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&Ii(t,e)}(e,t),i=e,(n=[{key:"resetActiveTimeout",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.inactiveTime=0}},{key:"enable",value:function(t,e){var i=this,n=this.context.createElement("div");n.className="jw-controls jw-reset",this.div=n;var r=this.context.createElement("div");r.className="jw-controls-backdrop jw-reset",this.backdrop=r,this.logo=this.playerContainer.querySelector(".jw-logo");var c=e.get("touchMode"),u=function(){(e.get("isFloating")?i.wrapperElement:i.playerContainer).focus()};if(!this.displayContainer){var d=new Oe(e,t);d.buttons.display.on("click tap enter",(function(){i.trigger(a.p),i.userActive(1e3),t.playToggle(Pi()),u()})),this.div.appendChild(d.element()),this.displayContainer=d}this.infoOverlay=new bi(n,e,t,(function(t){Object(l.v)(i.div,"jw-info-open",t),t&&i.div.querySelector(".jw-info-close").focus()})),o.OS.mobile||(this.shortcutsTooltip=new mi(this.wrapperElement,t,e)),this.rightClickMenu=new Ve(this.infoOverlay,this.shortcutsTooltip),c?(Object(l.a)(this.playerContainer,"jw-flag-touch"),this.rightClickMenu.setup(e,this.playerContainer,this.wrapperElement)):e.change("flashBlocked",(function(t,e){e?i.rightClickMenu.destroy():i.rightClickMenu.setup(t,i.playerContainer,i.wrapperElement)}),this);var h=e.get("floating");if(h){var f=new Ci(n,e.get("localization").close);f.on(a.sb,(function(){return i.trigger("dismissFloating",{doNotForward:!0})})),!1!==h.dismissible&&Object(l.a)(this.playerContainer,"jw-floating-dismissible")}var w=this.controlbar=new de(t,e,this.playerContainer.querySelector(".jw-hidden-accessibility"));if(w.on(a.sb,(function(){return i.userActive()})),w.on("nextShown",(function(t){this.trigger("nextShown",t)}),this),w.on("adjustVolume",k,this),e.get("nextUpDisplay")&&!w.nextUpToolTip){var g=new Se(e,t,this.playerContainer);g.on("all",this.trigger,this),g.setup(this.context),w.nextUpToolTip=g,this.div.appendChild(g.element())}this.div.appendChild(w.element());var j=e.get("localization"),b=this.settingsMenu=fi(t,e.player,this.controlbar,j),m=null;this.controlbar.on("menuVisibility",(function(n){var o=n.visible,r=n.evt,s=e.get("state"),l={reason:"settingsInteraction"},c=i.controlbar.elements.settingsButton,d="keydown"===(r&&r.sourceEvent||r||{}).type,p=o||d?0:Li;i.userActive(p),m=s,Object(wi.a)(e.get("containerWidth"))<2&&(o&&s===a.pb?t.pause(l):o||s!==a.ob||m!==a.pb||t.play(l)),!o&&d&&c?c.element().focus():r&&u()})),b.on("menuVisibility",(function(t){return i.controlbar.trigger("menuVisibility",t)})),this.controlbar.on("settingsInteraction",(function(t,e,i){if(e)return b.defaultChild.toggle(i);b.children[t].toggle(i)})),o.OS.mobile?this.div.appendChild(b.el):(this.playerContainer.setAttribute("aria-describedby","jw-shortcuts-tooltip-explanation"),this.div.insertBefore(b.el,w.element()));var v=function(e){if(e.get("autostartMuted")){var n=function(){return i.unmuteAutoplay(t,e)},a=function(t,e){e||n()};o.OS.mobile&&(i.mute=p("jw-autostart-mute jw-off",n,e.get("localization").unmute,[dt("volume-0")]),i.mute.show(),i.div.appendChild(i.mute.element())),w.renderVolume(!0,e.get("volume")),Object(l.a)(i.playerContainer,"jw-flag-autostart"),e.on("change:autostartFailed",n,i),e.on("change:autostartMuted change:mute",a,i),i.muteChangeCallback=a,i.unmuteCallback=n}};function y(i){var n=0,o=e.get("duration"),a=e.get("position");if("DVR"===e.get("streamType")){var r=e.get("dvrSeekLimit");n=o,o=Math.max(a,-r)}var l=Object(s.a)(a+i,n,o);t.seek(l,Pi())}function k(i){var n=Object(s.a)(e.get("volume")+i,0,100);t.setVolume(n)}e.once("change:autostartMuted",v),v(e);var x=function(n){if(n.ctrlKey||n.metaKey)return!0;var o=!i.settingsMenu.visible,a=!0===e.get("enableShortcuts"),r=i.instreamState;if(a||-1!==Ai.indexOf(n.keyCode)){switch(n.keyCode){case 27:if(e.get("fullscreen"))t.setFullscreen(!1),i.playerContainer.blur(),i.userInactive();else{var s=t.getPlugin("related");s&&s.close({type:"escape"})}i.rightClickMenu.el&&i.rightClickMenu.hideMenuHandler(),i.infoOverlay.visible&&i.infoOverlay.close(),i.shortcutsTooltip&&i.shortcutsTooltip.close();break;case 13:case 32:if(document.activeElement.classList.contains("jw-switch")&&13===n.keyCode)return!0;t.playToggle(Pi());break;case 37:!r&&o&&y(-5);break;case 39:!r&&o&&y(5);break;case 38:o&&k(10);break;case 40:o&&k(-10);break;case 67:var l=t.getCaptionsList().length;if(l){var c=(t.getCurrentCaptions()+1)%l;t.setCurrentCaptions(c)}break;case 77:t.setMute();break;case 70:t.setFullscreen();break;case 191:i.shortcutsTooltip&&i.shortcutsTooltip.toggleVisibility();break;default:if(n.keyCode>=48&&n.keyCode<=59){var u=(n.keyCode-48)/10*e.get("duration");t.seek(u,Pi())}}return/13|32|37|38|39|40/.test(n.keyCode)?(n.preventDefault(),!1):void 0}};this.playerContainer.addEventListener("keydown",x),this.keydownCallback=x;var T=function(t){switch(t.keyCode){case 9:var e=i.playerContainer.contains(t.target)?0:Li;i.userActive(e);break;case 32:t.preventDefault()}};this.playerContainer.addEventListener("keyup",T),this.keyupCallback=T;var O=function(t){var e=t.relatedTarget||document.querySelector(":focus");e&&(i.playerContainer.contains(e)||i.userInactive())};this.playerContainer.addEventListener("blur",O,!0),this.blurCallback=O;var C=function t(){"jw-shortcuts-tooltip-explanation"===i.playerContainer.getAttribute("aria-describedby")&&i.playerContainer.removeAttribute("aria-describedby"),i.playerContainer.removeEventListener("blur",t,!0)};this.shortcutsTooltip&&(this.playerContainer.addEventListener("blur",C,!0),this.onRemoveShortcutsDescription=C),this.userActive(),this.addControls(),this.addBackdrop(),e.set("controlsEnabled",!0)}},{key:"addControls",value:function(){this.wrapperElement.appendChild(this.div)}},{key:"disable",value:function(t){var e=this.nextUpToolTip,i=this.settingsMenu,n=this.infoOverlay,o=this.controlbar,a=this.rightClickMenu,r=this.shortcutsTooltip,s=this.playerContainer,c=this.div;clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.off(),t.off(null,null,this),t.set("controlsEnabled",!1),c.parentNode&&(Object(l.o)(s,"jw-flag-touch"),c.parentNode.removeChild(c)),o&&o.destroy(),a&&a.destroy(),this.keydownCallback&&s.removeEventListener("keydown",this.keydownCallback),this.keyupCallback&&s.removeEventListener("keyup",this.keyupCallback),this.blurCallback&&s.removeEventListener("blur",this.blurCallback),this.onRemoveShortcutsDescription&&s.removeEventListener("blur",this.onRemoveShortcutsDescription),this.displayContainer&&this.displayContainer.destroy(),e&&e.destroy(),i&&i.destroy(),n&&n.destroy(),r&&r.destroy(),this.removeBackdrop()}},{key:"controlbarHeight",value:function(){return this.dimensions.cbHeight||(this.dimensions.cbHeight=this.controlbar.element().clientHeight),this.dimensions.cbHeight}},{key:"element",value:function(){return this.div}},{key:"resize",value:function(){this.dimensions={}}},{key:"unmuteAutoplay",value:function(t,e){var i=!e.get("autostartFailed"),n=e.get("mute");i?n=!1:e.set("playOnViewable",!1),this.muteChangeCallback&&(e.off("change:autostartMuted change:mute",this.muteChangeCallback),this.muteChangeCallback=null),this.unmuteCallback&&(e.off("change:autostartFailed",this.unmuteCallback),this.unmuteCallback=null),e.set("autostartFailed",void 0),e.set("autostartMuted",void 0),t.setMute(n),this.controlbar.renderVolume(n,e.get("volume")),this.mute&&this.mute.hide(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.userActive()}},{key:"mouseMove",value:function(t){var e=this.controlbar.element().contains(t.target),i=this.controlbar.nextUpToolTip&&this.controlbar.nextUpToolTip.element().contains(t.target),n=this.logo&&this.logo.contains(t.target),o=e||i||n?0:Li;this.userActive(o)}},{key:"userActive",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Li;t>0?(this.inactiveTime=Object(c.a)()+t,-1===this.activeTimeout&&(this.activeTimeout=setTimeout(this.userInactiveTimeout,t))):this.resetActiveTimeout(),this.showing||(Object(l.o)(this.playerContainer,"jw-flag-user-inactive"),this.showing=!0,this.trigger("userActive"))}},{key:"userInactive",value:function(){clearTimeout(this.activeTimeout),this.activeTimeout=-1,this.settingsMenu.visible||(this.inactiveTime=0,this.showing=!1,Object(l.a)(this.playerContainer,"jw-flag-user-inactive"),this.trigger("userInactive"))}},{key:"addBackdrop",value:function(){var t=this.instreamState?this.div:this.wrapperElement.querySelector(".jw-captions");this.wrapperElement.insertBefore(this.backdrop,t)}},{key:"removeBackdrop",value:function(){var t=this.backdrop.parentNode;t&&t.removeChild(this.backdrop)}},{key:"setupInstream",value:function(){this.instreamState=!0,this.userActive(),this.addBackdrop(),this.settingsMenu&&this.settingsMenu.close(),Object(l.o)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","-1")}},{key:"destroyInstream",value:function(t){this.instreamState=null,this.addBackdrop(),t.get("autostartMuted")&&Object(l.a)(this.playerContainer,"jw-flag-autostart"),this.controlbar.elements.time.element().setAttribute("tabindex","0")}}])&&Mi(i.prototype,n),r&&Mi(i,r),e}(r.a)},function(t,e,i){"use strict";i.r(e);var n=i(0),o=i(12),a=i(50),r=i(36);var s=i(44),l=i(51),c=i(26),u=i(25),d=i(3),p=i(46),h=i(2),f=i(7),w=i(34);function g(t){var e=!1;return{async:function(){var i=this,n=arguments;return Promise.resolve().then((function(){if(!e)return t.apply(i,n)}))},cancel:function(){e=!0},cancelled:function(){return e}}}var j=i(1);function b(t){return function(e,i){var o=t.mediaModel,a=Object(n.g)({},i,{type:e});switch(e){case d.T:if(o.get(d.T)===i.mediaType)return;o.set(d.T,i.mediaType);break;case d.U:return void o.set(d.U,Object(n.g)({},i));case d.M:if(i[e]===t.model.getMute())return;break;case d.bb:i.newstate===d.mb&&(t.thenPlayPromise.cancel(),o.srcReset());var r=o.attributes.mediaState;o.attributes.mediaState=i.newstate,o.trigger("change:mediaState",o,i.newstate,r);break;case d.F:return t.beforeComplete=!0,t.trigger(d.B,a),void(t.attached&&!t.background&&t._playbackComplete());case d.G:o.get("setup")?(t.thenPlayPromise.cancel(),o.srcReset()):(e=d.tb,a.code+=1e5);break;case d.K:a.metadataType||(a.metadataType="unknown");var s=i.duration;Object(n.u)(s)&&(o.set("seekRange",i.seekRange),o.set("duration",s));break;case d.D:o.set("buffer",i.bufferPercent);case d.S:o.set("seekRange",i.seekRange),o.set("position",i.position),o.set("currentTime",i.currentTime);var l=i.duration;Object(n.u)(l)&&o.set("duration",l),e===d.S&&Object(n.r)(t.item.starttime)&&delete t.item.starttime;break;case d.R:var c=t.mediaElement;c&&c.paused&&o.set("mediaState","paused");break;case d.I:o.set(d.I,i.levels);case d.J:var u=i.currentQuality,p=i.levels;u>-1&&p.length>1&&o.set("currentLevel",parseInt(u));break;case d.f:o.set(d.f,i.tracks);case d.g:var h=i.currentTrack,f=i.tracks;h>-1&&f.length>0&&h=Math.max(l,p.a)&&(t.preloadNextItem(),v=!0)}function L(t){var e={};b.tag&&(e.tag=b.tag),this.trigger(d.F,e),A.call(this,t)}function A(t){g={},a&&w+10?t:null,f&&f.model.set("skipOffset",s)}};Object(n.g)(lt.prototype,f.a);var ct=lt,ut=i(66),dt=i(63),pt=function(t){var e=this,i=[],n={},o=0,a=0;function r(t){if(t.data=t.data||[],t.name=t.label||t.name||t.language,t._id=Object(dt.a)(t,i.length),!t.name){var e=Object(dt.b)(t,o);t.name=e.label,o=e.unknownCount}n[t._id]=t,i.push(t)}function s(){for(var t=[{id:"off",label:"Off"}],e=0;e')+'
    '},wt=i(35),gt=44,jt=function(t){var e=t.get("height");if(t.get("aspectratio"))return!1;if("string"==typeof e&&e.indexOf("%")>-1)return!1;var i=1*e||NaN;return!!(i=isNaN(i)?t.get("containerHeight"):i)&&(i&&i<=gt)},bt=i(54);function mt(t,e){if(t.get("fullscreen"))return 1;if(!t.get("activeTab"))return 0;if(t.get("isFloating"))return 1;var i=t.get("intersectionRatio");return void 0===i&&(i=function(t){var e=document.documentElement,i=document.body,n={top:0,left:0,right:e.clientWidth||i.clientWidth,width:e.clientWidth||i.clientWidth,bottom:e.clientHeight||i.clientHeight,height:e.clientHeight||i.clientHeight};if(!i.contains(t))return 0;if("none"===window.getComputedStyle(t).display)return 0;var o=vt(t);if(!o)return 0;var a=o,r=t.parentNode,s=!1;for(;!s;){var l=null;if(r===i||r===e||1!==r.nodeType?(s=!0,l=n):"visible"!==window.getComputedStyle(r).overflow&&(l=vt(r)),l&&(c=l,u=a,d=void 0,p=void 0,h=void 0,f=void 0,w=void 0,g=void 0,d=Math.max(c.top,u.top),p=Math.min(c.bottom,u.bottom),h=Math.max(c.left,u.left),f=Math.min(c.right,u.right),g=p-d,!(a=(w=f-h)>=0&&g>=0&&{top:d,bottom:p,left:h,right:f,width:w,height:g})))return 0;r=r.parentNode}var c,u,d,p,h,f,w,g;var j=o.width*o.height,b=a.width*a.height;return j?b/j:0}(e),window.top!==window.self&&i)?0:i}function vt(t){try{return t.getBoundingClientRect()}catch(t){}}var yt=i(49),kt=i(42),xt=i(58),Tt=i(10);var Ot=i(32),Ct=i(5),_t=i(6),Mt=["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],St=function(t,e,i){for(var n=t.requestFullscreen||t.webkitRequestFullscreen||t.webkitRequestFullScreen||t.mozRequestFullScreen||t.msRequestFullscreen,o=e.exitFullscreen||e.webkitExitFullscreen||e.webkitCancelFullScreen||e.mozCancelFullScreen||e.msExitFullscreen,a=!(!n||!o),r=Mt.length;r--;)e.addEventListener(Mt[r],i);return{events:Mt,supportsDomFullscreen:function(){return a},requestFullscreen:function(){n.call(t,{navigationUI:"hide"})},exitFullscreen:function(){null!==this.fullscreenElement()&&o.apply(e)},fullscreenElement:function(){var t=e.fullscreenElement,i=e.webkitCurrentFullScreenElement,n=e.mozFullScreenElement,o=e.msFullscreenElement;return null===t?t:t||i||n||o},destroy:function(){for(var t=Mt.length;t--;)e.removeEventListener(Mt[t],i)}}},Et=i(40);function It(t,e){for(var i=0;i')},Rt={linktarget:"_blank",margin:8,hide:!1,position:"top-right"};function zt(t){var e,i;Object(n.g)(this,f.a);var o=new Image;this.setup=function(){(i=Object(n.g)({},Rt,t.get("logo"))).position=i.position||Rt.position,i.hide="true"===i.hide.toString(),i.file&&"control-bar"!==i.position&&(e||(e=Object(Ct.e)(Pt(i.position,i.hide))),t.set("logo",i),o.onload=function(){var n=this.height,o=this.width,a={backgroundImage:'url("'+this.src+'")'};if(i.margin!==Rt.margin){var r=/(\w+)-(\w+)/.exec(i.position);3===r.length&&(a["margin-"+r[1]]=i.margin,a["margin-"+r[2]]=i.margin)}var s=.15*t.get("containerHeight"),l=.15*t.get("containerWidth");if(n>s||o>l){var c=o/n;l/s>c?(n=s,o=s*c):(o=l,n=l/c)}a.width=Math.round(o),a.height=Math.round(n),Object(Tt.d)(e,a),t.set("logoWidth",a.width)},o.src=i.file,i.link&&(e.setAttribute("tabindex","0"),e.setAttribute("aria-label",t.get("localization").logo)),this.ui=new Et.a(e).on("click tap enter",(function(t){t&&t.stopPropagation&&t.stopPropagation(),this.trigger(d.A,{link:i.link,linktarget:i.linktarget})}),this))},this.setContainer=function(t){e&&t.appendChild(e)},this.element=function(){return e},this.position=function(){return i.position},this.destroy=function(){o.onload=null,this.ui&&this.ui.destroy()}}var Bt=function(t){this.model=t,this.image=null};Object(n.g)(Bt.prototype,{setup:function(t){this.el=t},setImage:function(t){var e=this.image;e&&(e.onload=null),this.image=null;var i="";"string"==typeof t&&(i='url("'+t+'")',(e=this.image=new Image).src=t),Object(Tt.d)(this.el,{backgroundImage:i})},resize:function(t,e,i){if("uniform"===i){if(t&&(this.playerAspectRatio=t/e),!this.playerAspectRatio||!this.image||"complete"!==(s=this.model.get("state"))&&"idle"!==s&&"error"!==s&&"buffering"!==s)return;var n=this.image,o=null;if(n){if(0===n.width){var a=this;return void(n.onload=function(){a.resize(t,e,i)})}var r=n.width/n.height;Math.abs(this.playerAspectRatio-r)<.09&&(o="cover")}Object(Tt.d)(this.el,{backgroundSize:o})}var s},element:function(){return this.el}});var Vt=Bt,Nt=function(t){this.model=t.player};Object(n.g)(Nt.prototype,{hide:function(){Object(Tt.d)(this.el,{display:"none"})},show:function(){Object(Tt.d)(this.el,{display:""})},setup:function(t){this.el=t;var e=this.el.getElementsByTagName("div");this.title=e[0],this.description=e[1],this.model.on("change:logoWidth",this.update,this),this.model.change("playlistItem",this.playlistItem,this)},update:function(t){var e={},i=t.get("logo");if(i){var n=1*(""+i.margin).replace("px",""),o=t.get("logoWidth")+(isNaN(n)?0:n+10);"top-left"===i.position?e.paddingLeft=o:"top-right"===i.position&&(e.paddingRight=o)}Object(Tt.d)(this.el,e)},playlistItem:function(t,e){if(e)if(t.get("displaytitle")||t.get("displaydescription")){var i="",n="";e.title&&t.get("displaytitle")&&(i=e.title),e.description&&t.get("displaydescription")&&(n=e.description),this.updateText(i,n)}else this.hide()},updateText:function(t,e){Object(Ct.q)(this.title,t),Object(Ct.q)(this.description,e),this.title.firstChild||this.description.firstChild?this.show():this.hide()},element:function(){return this.el}});var Ht=Nt;function Ft(t,e){for(var i=0;it)}if(e.get("controls")){var r=jt(e);Object(Ct.v)(u,"jw-flag-audio-player",r),e.set("audioMode",r)}}function z(){e.set("visibility",mt(e,u))}this.updateBounds=function(){Object(kt.a)(k);var t=e.get("isFloating")?p:u,i=document.body.contains(t),n=Object(Ct.c)(t),r=Math.round(n.width),s=Math.round(n.height);if(S=Object(Ct.c)(u),r===o&&s===a)return o&&a||A(),void e.set("inDom",i);r&&s||o&&a||A(),(r||s||i)&&(e.set("containerWidth",r),e.set("containerHeight",s)),e.set("inDom",i),i&&bt.a.observe(u)},this.updateStyles=function(){var t=e.get("containerWidth"),i=e.get("containerHeight");R(t,i),I&&I.resize(t,i),$(t,i),v.resize(),T&&F()},this.checkResized=function(){var t=e.get("containerWidth"),i=e.get("containerHeight"),n=e.get("isFloating");if(t!==o||i!==a){this.resizeListener||(this.resizeListener=new Ut.a(p,this,e)),o=t,a=i,l.trigger(d.hb,{width:t,height:i});var s=Object(xt.a)(t);E!==s&&(E=s,l.trigger(d.j,{breakpoint:E}))}n!==r&&(r=n,l.trigger(d.x,{floating:n}),z())},this.responsiveListener=A,this.setup=function(){j.setup(u.querySelector(".jw-preview")),b.setup(u.querySelector(".jw-title")),(i=new zt(e)).setup(),i.setContainer(p),i.on(d.A,J),v.setup(u.id,e.get("captions")),b.element().parentNode.insertBefore(v.element(),b.element()),O=function(t,e,i){var n=new Lt(e,i),o=e.get("controls");n.on({click:function(){l.trigger(d.p),I&&(ct()?I.settingsMenu.close():ut()?I.infoOverlay.close():t.playToggle({reason:"interaction"}))},tap:function(){l.trigger(d.p),ct()&&I.settingsMenu.close(),ut()&&I.infoOverlay.close();var i=e.get("state");if(o&&(i===d.mb||i===d.kb||e.get("instream")&&i===d.ob)&&t.playToggle({reason:"interaction"}),o&&i===d.ob){if(e.get("instream")||e.get("castActive")||"audio"===e.get("mediaType"))return;Object(Ct.v)(u,"jw-flag-controls-hidden"),l.dismissible&&Object(Ct.v)(u,"jw-floating-dismissible",Object(Ct.i)(u,"jw-flag-controls-hidden")),v.renderCues(!0)}else I&&(I.showing?I.userInactive():I.userActive())},doubleClick:function(){return I&&t.setFullscreen()}}),Wt||(u.addEventListener("mousemove",W),u.addEventListener("mouseover",Q),u.addEventListener("mouseout",Y));return n}(t,e,w),_=new Et.a(u).on("click",(function(){})),C=St(u,document,et),e.on("change:hideAdsControls",(function(t,e){Object(Ct.v)(u,"jw-flag-ads-hide-controls",e)})),e.on("change:scrubbing",(function(t,e){Object(Ct.v)(u,"jw-flag-dragging",e)})),e.on("change:playRejected",(function(t,e){Object(Ct.v)(u,"jw-flag-play-rejected",e)})),e.on(d.X,tt),e.on("change:".concat(d.U),(function(){$(),v.resize()})),e.player.on("change:errorEvent",at),e.change("stretching",X);var n=e.get("width"),o=e.get("height"),a=G(n,o);Object(Tt.d)(u,a),e.change("aspectratio",K),R(n,o),e.get("controls")||(Object(Ct.a)(u,"jw-flag-controls-hidden"),Object(Ct.o)(u,"jw-floating-dismissible")),Qt&&Object(Ct.a)(u,"jw-ie");var r=e.get("skin")||{};r.name&&Object(Ct.p)(u,/jw-skin-\S+/,"jw-skin-"+r.name);var s=function(t){t||(t={});var e=t.active,i=t.inactive,n=t.background,o={};return o.controlbar=function(t){if(t||e||i||n){var o={};return t=t||{},o.iconsActive=t.iconsActive||e,o.icons=t.icons||i,o.text=t.text||i,o.background=t.background||n,o}}(t.controlbar),o.timeslider=function(t){if(t||e){var i={};return t=t||{},i.progress=t.progress||e,i.rail=t.rail,i}}(t.timeslider),o.menus=function(t){if(t||e||i||n){var o={};return t=t||{},o.text=t.text||i,o.textActive=t.textActive||e,o.background=t.background||n,o}}(t.menus),o.tooltips=function(t){if(t||i||n){var e={};return t=t||{},e.text=t.text||i,e.background=t.background||n,e}}(t.tooltips),o}(r);!function(t,e){var i;function n(e,i,n,o){if(n){e=Object(h.f)(e,"#"+t+(o?"":" "));var a={};a[i]=n,Object(Tt.b)(e.join(", "),a,t)}}e&&(e.controlbar&&function(e){n([".jw-controlbar .jw-icon-inline.jw-text",".jw-title-primary",".jw-title-secondary"],"color",e.text),e.icons&&(n([".jw-button-color:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:not(.jw-icon-cast)"],"color",e.icons),n([".jw-display-icon-container .jw-button-color"],"color",e.icons),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.icons,"}"),t));e.iconsActive&&(n([".jw-display-icon-container .jw-button-color:hover",".jw-display-icon-container .jw-button-color:focus"],"color",e.iconsActive),n([".jw-button-color.jw-toggle:not(.jw-icon-cast)",".jw-button-color:hover:not(.jw-icon-cast)",".jw-button-color:focus:not(.jw-icon-cast)",".jw-button-color.jw-toggle.jw-off:hover:not(.jw-icon-cast)"],"color",e.iconsActive),n([".jw-svg-icon-buffer"],"fill",e.icons),Object(Tt.b)("#".concat(t," .jw-icon-cast:hover google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:focus google-cast-launcher.jw-off"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher.jw-off:focus"),"{--disconnected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast google-cast-launcher:focus"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:hover google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t),Object(Tt.b)("#".concat(t," .jw-icon-cast:focus google-cast-launcher"),"{--connected-color: ".concat(e.iconsActive,"}"),t));n([" .jw-settings-topbar",":not(.jw-state-idle) .jw-controlbar",".jw-flag-audio-player .jw-controlbar"],"background",e.background,!0)}(e.controlbar),e.timeslider&&function(t){var e=t.progress;"none"!==e&&(n([".jw-progress",".jw-knob"],"background-color",e),n([".jw-buffer"],"background-color",Object(Tt.c)(e,50)));n([".jw-rail"],"background-color",t.rail),n([".jw-background-color.jw-slider-time",".jw-slider-time .jw-cue"],"background-color",t.background)}(e.timeslider),e.menus&&(n([".jw-option",".jw-toggle.jw-off",".jw-skip .jw-skip-icon",".jw-nextup-tooltip",".jw-nextup-close",".jw-settings-content-item",".jw-related-title"],"color",(i=e.menus).text),n([".jw-option.jw-active-option",".jw-option:not(.jw-active-option):hover",".jw-option:not(.jw-active-option):focus",".jw-settings-content-item:hover",".jw-nextup-tooltip:hover",".jw-nextup-tooltip:focus",".jw-nextup-close:hover"],"color",i.textActive),n([".jw-nextup",".jw-settings-menu"],"background",i.background)),e.tooltips&&function(t){n([".jw-skip",".jw-tooltip .jw-text",".jw-time-tip .jw-text"],"background-color",t.background),n([".jw-time-tip",".jw-tooltip"],"color",t.background),n([".jw-skip"],"border","none"),n([".jw-skip .jw-text",".jw-skip .jw-icon",".jw-time-tip .jw-text",".jw-tooltip .jw-text"],"color",t.text)}(e.tooltips),e.menus&&function(e){if(e.textActive){var i={color:e.textActive,borderColor:e.textActive,stroke:e.textActive};Object(Tt.b)("#".concat(t," .jw-color-active"),i,t),Object(Tt.b)("#".concat(t," .jw-color-active-hover:hover"),i,t)}if(e.text){var n={color:e.text,borderColor:e.text,stroke:e.text};Object(Tt.b)("#".concat(t," .jw-color-inactive"),n,t),Object(Tt.b)("#".concat(t," .jw-color-inactive-hover:hover"),n,t)}}(e.menus))}(e.get("id"),s),e.set("mediaContainer",w),e.set("iFrame",m.Features.iframe),e.set("activeTab",Object(yt.a)()),e.set("touchMode",Wt&&("string"==typeof o||o>=gt)),bt.a.add(this),e.get("enableGradient")&&!Qt&&Object(Ct.a)(u,"jw-ab-drop-shadow"),this.isSetup=!0,e.trigger("viewSetup",u);var c=document.body.contains(u);c&&bt.a.observe(u),e.set("inDom",c)},this.init=function(){this.updateBounds(),e.on("change:fullscreen",Z),e.on("change:activeTab",z),e.on("change:fullscreen",z),e.on("change:intersectionRatio",z),e.on("change:visibility",U),e.on("instreamMode",(function(t){t?dt():pt()})),z(),1!==bt.a.size()||e.get("visibility")||U(e,1,0);var t=e.player;e.change("state",rt),t.change("controls",D),e.change("streamType",nt),e.change("mediaType",ot),t.change("playlistItem",(function(t,e){lt(t,e)})),o=a=null,T&&Wt&&bt.a.addScrollHandler(F),this.checkResized()};var B,V=62,N=!0;function H(){var t=e.get("isFloating"),i=S.top0&&void 0!==arguments[0])||arguments[0],e={x:0,y:0,width:o||0,height:a||0};return I&&t&&(e.height-=I.controlbarHeight()),e},this.setCaptions=function(t){v.clear(),v.setup(e.get("id"),t),v.resize()},this.setIntersection=function(t){var i=Math.round(100*t.intersectionRatio)/100;e.set("intersectionRatio",i),T&&!L()&&(M=M||i>=.5)&&ht(i)},this.stopFloating=function(t,i){if(t&&(T=null,bt.a.removeScrollHandler(F)),Yt===u){Yt=null,e.set("isFloating",!1);var n=function(){Object(Ct.o)(u,"jw-flag-floating"),K(e,e.get("aspectratio")),Object(Tt.d)(u,{backgroundImage:null}),Object(Tt.d)(p,{maxWidth:null,width:null,height:null,left:null,right:null,top:null,bottom:null,margin:null,transform:null,transition:null,"transition-timing-function":null})};i?(Object(Tt.d)(p,{transform:"translateY(-".concat(V-S.top,"px)"),"transition-timing-function":"ease-out"}),setTimeout(n,150)):n(),g.disable(),A()}},this.destroy=function(){e.destroy(),bt.a.unobserve(u),bt.a.remove(this),this.isSetup=!1,this.off(),Object(kt.a)(k),clearTimeout(y),Yt===u&&(Yt=null),_&&(_.destroy(),_=null),C&&(C.destroy(),C=null),I&&I.disable(e),O&&(O.destroy(),u.removeEventListener("mousemove",W),u.removeEventListener("mouseout",Y),u.removeEventListener("mouseover",Q),O=null),v.destroy(),i&&(i.destroy(),i=null),Object(Tt.a)(e.get("id")),this.resizeListener&&(this.resizeListener.destroy(),delete this.resizeListener),T&&Wt&&bt.a.removeScrollHandler(F)}};function Kt(t,e,i){return(Kt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(t,e,i){var n=function(t,e){for(;!Object.prototype.hasOwnProperty.call(t,e)&&null!==(t=ee(t)););return t}(t,e);if(n){var o=Object.getOwnPropertyDescriptor(n,e);return o.get?o.get.call(i):o.value}})(t,e,i||t)}function Jt(t){return(Jt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Zt(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function Gt(t,e){for(var i=0;ie&&t(),e=n}};function Oe(t,e){e.off(d.N,t._onPlayAttempt),e.off(d.fb,t._triggerFirstFrame),e.off(d.S,t._onTime),t.off("change:activeTab",t._onTabVisible)}var Ce=function(t,e){t.change("mediaModel",(function(t,i,n){t._qoeItem&&n&&t._qoeItem.end(n.get("mediaState")),t._qoeItem=new ye.a,t._qoeItem.getFirstFrame=function(){var t=this.between(d.N,d.H),e=this.between(xe,d.H);return e>0&&e0&&rt(e,t.tracks)}),O).on(d.F,(function(){Promise.resolve().then(at)}),O).on(d.G,O.triggerError,O),Ce(C,B),C.on(d.w,O.triggerError,O),C.on("change:state",(function(t,e,i){X()||K.call(T,t,e,i)}),this),C.on("change:castState",(function(t,e){O.trigger(d.m,e)})),C.on("change:fullscreen",(function(t,e){O.trigger(d.y,{fullscreen:e}),e&&t.set("playOnViewable",!1)})),C.on("change:volume",(function(t,e){O.trigger(d.V,{volume:e})})),C.on("change:mute",(function(t){O.trigger(d.M,{mute:t.getMute()})})),C.on("change:playbackRate",(function(t,e){O.trigger(d.ab,{playbackRate:e,position:t.get("position")})}));var V=function t(e,i){"clickthrough"!==i&&"interaction"!==i&&"external"!==i||(C.set("playOnViewable",!1),C.off("change:playReason change:pauseReason",t))};function N(t,e){Object(n.t)(e)||C.set("viewable",Math.round(e))}function H(){dt&&(!0!==C.get("autostart")||C.get("playOnViewable")||$("autostart"),dt.flush())}function F(t,e){O.trigger("viewable",{viewable:e}),D()}function D(){if((o.a[0]===e||1===C.get("viewable"))&&"idle"===C.get("state")&&!1===C.get("autostart"))if(!b.primed()&&m.OS.android){var t=b.getTestElement(),i=O.getMute();Promise.resolve().then((function(){return fe(t,{muted:i})})).then((function(){"idle"===C.get("state")&&B.preloadVideo()})).catch(Se)}else B.preloadVideo()}function q(t){O._instreamAdapter.noResume=!t,t||et({reason:"viewable"})}function U(t){t||(O.pause({reason:"viewable"}),C.set("playOnViewable",!t))}function W(t,e){var i=X();if(t.get("playOnViewable")){if(e){var n=t.get("autoPause").pauseAds,o=t.get("pauseReason");J()===d.mb?$("viewable"):i&&!n||"interaction"===o||Z({reason:"viewable"})}else m.OS.mobile&&!i&&(O.pause({reason:"autostart"}),C.set("playOnViewable",!0));m.OS.mobile&&i&&q(e)}}function Q(t,e){var i=t.get("state"),n=X(),o=t.get("playReason");n?t.get("autoPause").pauseAds?U(e):q(e):i===d.pb||i===d.jb?U(e):i===d.mb&&"playlist"===o&&t.once("change:state",(function(){U(e)}))}function X(){var t=O._instreamAdapter;return!!t&&t.getState()}function J(){var t=X();return t||C.get("state")}function Z(t){if(E.cancel(),M=!1,C.get("state")===d.lb)return Promise.resolve();var i=G(t);return C.set("playReason",i),X()?(e.pauseAd(!1,t),Promise.resolve()):(C.get("state")===d.kb&&(tt(!0),O.setItemIndex(0)),!_&&(_=!0,O.trigger(d.C,{playReason:i,startTime:t&&t.startTime?t.startTime:C.get("playlistItem").starttime}),_=!1,ve()&&!b.primed()&&b.prime(),"playlist"===i&&C.get("autoPause").viewability&&Q(C,C.get("viewable")),x)?(ve()&&!R&&C.get("mediaElement").load(),x=!1,k=null,Promise.resolve()):B.playVideo(i).then(b.played))}function G(t){return t&&t.reason?t.reason:"unknown"}function $(t){if(J()===d.mb){E=g(H);var e=C.get("advertising");(function(t,e){var i=e.cancelable,n=e.muted,o=void 0!==n&&n,a=e.allowMuted,r=void 0!==a&&a,s=e.timeout,l=void 0===s?1e4:s,c=t.getTestElement(),u=o?"muted":"".concat(r);be[u]||(be[u]=fe(c,{muted:o}).catch((function(t){if(!i.cancelled()&&!1===o&&r)return fe(c,{muted:o=!0});throw t})).then((function(){return o?(be[u]=null,ge):we})).catch((function(t){throw clearTimeout(d),be[u]=null,t.reason=je,t})));var d,p=be[u].then((function(t){if(clearTimeout(d),i.cancelled()){var e=new Error("Autoplay test was cancelled");throw e.reason="cancelled",e}return t})),h=new Promise((function(t,e){d=setTimeout((function(){be[u]=null;var t=new Error("Autoplay test timed out");t.reason="timeout",e(t)}),l)}));return Promise.race([p,h])})(b,{cancelable:E,muted:O.getMute(),allowMuted:!e||e.autoplayadsmuted}).then((function(e){return C.set("canAutoplay",e),e!==ge||O.getMute()||(C.set("autostartMuted",!0),ut(),C.once("change:autostartMuted",(function(t){t.off("change:viewable",W),O.trigger(d.M,{mute:C.getMute()})}))),O.getMute()&&C.get("enableDefaultCaptions")&&y.selectDefaultIndex(1),Z({reason:t}).catch((function(){O._instreamAdapter||C.set("autostartFailed",!0),k=null}))})).catch((function(t){if(C.set("canAutoplay",je),C.set("autostart",!1),!E.cancelled()){var e=Object(j.w)(t);O.trigger(d.h,{reason:t.reason,code:e,error:t})}}))}}function tt(t){if(E.cancel(),dt.empty(),X()){var e=O._instreamAdapter;return e&&(e.noResume=!0),void(k=function(){return B.stopVideo()})}k=null,!t&&(M=!0),_&&(x=!0),C.set("errorEvent",void 0),B.stopVideo()}function et(t){var e=G(t);C.set("pauseReason",e),C.set("playOnViewable","viewable"===e)}function it(t){k=null,E.cancel();var i=X();if(i&&i!==d.ob)return et(t),void e.pauseAd(!0,t);switch(C.get("state")){case d.lb:return;case d.pb:case d.jb:et(t),B.pause();break;default:_&&(x=!0)}}function nt(t,e){tt(!0),O.setItemIndex(t),O.play(e)}function ot(t){nt(C.get("item")+1,t)}function at(){O.completeCancelled()||(k=O.completeHandler,O.shouldAutoAdvance()?O.nextItem():C.get("repeat")?ot({reason:"repeat"}):(m.OS.iOS&<(!1),C.set("playOnViewable",!1),C.set("state",d.kb),O.trigger(d.cb,{})))}function rt(t,e){t=parseInt(t,10)||0,C.persistVideoSubtitleTrack(t,e),B.subtitles=t,O.trigger(d.k,{tracks:st(),track:t})}function st(){return y.getCaptionsList()}function lt(t){Object(n.n)(t)||(t=!C.get("fullscreen")),C.set("fullscreen",t),O._instreamAdapter&&O._instreamAdapter._adModel&&O._instreamAdapter._adModel.set("fullscreen",t)}function ut(){B.mute=C.getMute(),B.volume=C.get("volume")}C.on("change:playReason change:pauseReason",V),O.on(d.c,(function(t){return V(0,t.playReason)})),O.on(d.b,(function(t){return V(0,t.pauseReason)})),C.on("change:scrubbing",(function(t,e){e?(S=C.get("state")!==d.ob,it()):S&&Z({reason:"interaction"})})),C.on("change:captionsList",(function(t,e){O.trigger(d.l,{tracks:e,track:C.get("captionsIndex")||0})})),C.on("change:mediaModel",(function(t,e){var i=this;t.set("errorEvent",void 0),e.change("mediaState",(function(e,i){var n;t.get("errorEvent")||t.set(d.bb,(n=i)===d.nb||n===d.qb?d.jb:n)}),this),e.change("duration",(function(e,i){if(0!==i){var n=t.get("minDvrWindow"),o=Object(me.b)(i,n);t.setStreamType(o)}}),this);var n=t.get("item")+1,o="autoplay"===(t.get("related")||{}).oncomplete,a=t.get("playlist")[n];if((a||o)&&R){e.on("change:position",(function t(n,r){var s=a&&!a.daiSetting,l=e.get("duration");s&&r&&l>0&&r>=l-p.b?(e.off("change:position",t,i),B.backgroundLoad(a)):o&&(a=C.get("nextUp"))}),this)}})),(y=new ht(C)).on("all",P,O),z.on("viewSetup",(function(t){Object(a.b)(T,t)})),this.playerReady=function(){v.once(d.hb,(function(){try{!function(){C.change("visibility",N),L.off(),O.trigger(d.gb,{setupTime:0}),C.change("playlist",(function(t,e){if(e.length){var i={playlist:e},o=C.get("feedData");o&&(i.feedData=Object(n.g)({},o)),O.trigger(d.eb,i)}})),C.change("playlistItem",(function(t,e){if(e){var i=e.title,n=e.image;if("mediaSession"in navigator&&window.MediaMetadata&&(i||n))try{navigator.mediaSession.metadata=new window.MediaMetadata({title:i,artist:window.location.hostname,artwork:[{src:n||""}]})}catch(t){}t.set("cues",[]),O.trigger(d.db,{index:C.get("item"),item:e})}})),L.flush(),L.destroy(),L=null,C.change("viewable",F),C.change("viewable",W),C.get("autoPause").viewability?C.change("viewable",Q):C.once("change:autostartFailed change:mute",(function(t){t.off("change:viewable",W)}));H(),C.on("change:itemReady",(function(t,e){e&&dt.flush()}))}()}catch(t){O.triggerError(Object(j.v)(j.m,j.a,t))}})),v.init()},this.preload=D,this.load=function(t,e){var i,n=O._instreamAdapter;switch(n&&(n.noResume=!0),O.trigger("destroyPlugin",{}),tt(!0),E.cancel(),E=g(H),I.cancel(),ve()&&b.prime(),_e(t)){case"string":C.attributes.item=0,C.attributes.itemReady=!1,I=g((function(t){if(t)return O.updatePlaylist(Object(c.a)(t.playlist),t)})),i=function(t){var e=this;return new Promise((function(i,n){var o=new l.a;o.on(d.eb,(function(t){i(t)})),o.on(d.w,n,e),o.load(t)}))}(t).then(I.async);break;case"object":C.attributes.item=0,i=O.updatePlaylist(Object(c.a)(t),e||{});break;case"number":i=O.setItemIndex(t);break;default:return}i.catch((function(t){O.triggerError(Object(j.u)(t,j.c))})),i.then(E.async).catch(Se)},this.play=function(t){return Z(t).catch(Se)},this.pause=it,this.seek=function(t,e){var i=C.get("state");if(i!==d.lb){B.position=t;var n=i===d.mb;C.get("scrubbing")||!n&&i!==d.kb||(n&&((e=e||{}).startTime=t),this.play(e))}},this.stop=tt,this.playlistItem=nt,this.playlistNext=ot,this.playlistPrev=function(t){nt(C.get("item")-1,t)},this.setCurrentCaptions=rt,this.setCurrentQuality=function(t){B.quality=t},this.setFullscreen=lt,this.getCurrentQuality=function(){return B.quality},this.getQualityLevels=function(){return B.qualities},this.setCurrentAudioTrack=function(t){B.audioTrack=t},this.getCurrentAudioTrack=function(){return B.audioTrack},this.getAudioTracks=function(){return B.audioTracks},this.getCurrentCaptions=function(){return y.getCurrentIndex()},this.getCaptionsList=st,this.getVisualQuality=function(){var t=this._model.get("mediaModel");return t?t.get(d.U):null},this.getConfig=function(){return this._model?this._model.getConfiguration():void 0},this.getState=J,this.next=Se,this.completeHandler=at,this.completeCancelled=function(){return(t=C.get("state"))!==d.mb&&t!==d.kb&&t!==d.lb||!!M&&(M=!1,!0);var t},this.shouldAutoAdvance=function(){return C.get("item")!==C.get("playlist").length-1},this.nextItem=function(){ot({reason:"playlist"})},this.setConfig=function(t){!function(t,e){var i=t._model,n=i.attributes;e.height&&(e.height=Object(r.b)(e.height),e.width=e.width||n.width),e.width&&(e.width=Object(r.b)(e.width),e.aspectratio?(n.width=e.width,delete e.width):e.height=n.height),e.width&&e.height&&!e.aspectratio&&t._view.resize(e.width,e.height),Object.keys(e).forEach((function(o){var a=e[o];if(void 0!==a)switch(o){case"aspectratio":i.set(o,Object(r.a)(a,n.width));break;case"autostart":!function(t,e,i){t.setAutoStart(i),"idle"===t.get("state")&&!0===i&&e.play({reason:"autostart"})}(i,t,a);break;case"mute":t.setMute(a);break;case"volume":t.setVolume(a);break;case"playbackRateControls":case"playbackRates":case"repeat":case"stretching":i.set(o,a)}}))}(O,t)},this.setItemIndex=function(t){B.stopVideo();var e=C.get("playlist").length;return(t=(parseInt(t,10)||0)%e)<0&&(t+=e),B.setActiveItem(t).catch((function(t){t.code>=151&&t.code<=162&&(t=Object(j.u)(t,j.e)),T.triggerError(Object(j.v)(j.k,j.d,t))}))},this.detachMedia=function(){if(_&&(x=!0),C.get("autoPause").viewability&&Q(C,C.get("viewable")),!R)return B.setAttached(!1);B.backgroundActiveMedia()},this.attachMedia=function(){R?B.restoreBackgroundMedia():B.setAttached(!0),"function"==typeof k&&k()},this.routeEvents=function(t){return B.routeEvents(t)},this.forwardEvents=function(){return B.forwardEvents()},this.playVideo=function(t){return B.playVideo(t)},this.stopVideo=function(){return B.stopVideo()},this.castVideo=function(t,e){return B.castVideo(t,e)},this.stopCast=function(){return B.stopCast()},this.backgroundActiveMedia=function(){return B.backgroundActiveMedia()},this.restoreBackgroundMedia=function(){return B.restoreBackgroundMedia()},this.preloadNextItem=function(){B.background.currentMedia&&B.preloadVideo()},this.isBeforeComplete=function(){return B.beforeComplete},this.setVolume=function(t){C.setVolume(t),ut()},this.setMute=function(t){C.setMute(t),ut()},this.setPlaybackRate=function(t){C.setPlaybackRate(t)},this.getProvider=function(){return C.get("provider")},this.getWidth=function(){return C.get("containerWidth")},this.getHeight=function(){return C.get("containerHeight")},this.getItemQoe=function(){return C._qoeItem},this.addButton=function(t,e,i,n,o){var a=C.get("customButtons")||[],r=!1,s={img:t,tooltip:e,callback:i,id:n,btnClass:o};a=a.reduce((function(t,e){return e.id===n?(r=!0,t.push(s)):t.push(e),t}),[]),r||a.unshift(s),C.set("customButtons",a)},this.removeButton=function(t){var e=C.get("customButtons")||[];e=e.filter((function(e){return e.id!==t})),C.set("customButtons",e)},this.resize=v.resize,this.getSafeRegion=v.getSafeRegion,this.setCaptions=v.setCaptions,this.checkBeforePlay=function(){return _},this.setControls=function(t){Object(n.n)(t)||(t=!C.get("controls")),C.set("controls",t),B.controls=t},this.addCues=function(t){this.setCues(C.get("cues").concat(t))},this.setCues=function(t){C.set("cues",t)},this.updatePlaylist=function(t,e){try{var i=Object(c.b)(t,C,e);Object(c.e)(i);var o=Object(n.g)({},e);delete o.playlist,C.set("feedData",o),C.set("playlist",i)}catch(t){return Promise.reject(t)}return this.setItemIndex(C.get("item"))},this.setPlaylistItem=function(t,e){(e=Object(c.d)(C,new u.a(e),e.feedData||{}))&&(C.get("playlist")[t]=e,t===C.get("item")&&"idle"===C.get("state")&&this.setItemIndex(t))},this.playerDestroy=function(){this.off(),this.stop(),Object(a.b)(this,this.originalContainer),v&&v.destroy(),C&&C.destroy(),dt&&dt.destroy(),y&&y.destroy(),B&&B.destroy(),this.instreamDestroy()},this.isBeforePlay=this.checkBeforePlay,this.createInstream=function(){return this.instreamDestroy(),this._instreamAdapter=new ct(this,C,v,b),this._instreamAdapter},this.instreamDestroy=function(){O._instreamAdapter&&(O._instreamAdapter.destroy(),O._instreamAdapter=null)};var dt=new s.a(this,["play","pause","setCurrentAudioTrack","setCurrentCaptions","setCurrentQuality","setFullscreen"],(function(){return!T._model.get("itemReady")||L}));dt.queue.push.apply(dt.queue,w),v.setup()},get:function(t){if(t in y.a){var e=this._model.get("mediaModel");return e?e.get(t):y.a[t]}return this._model.get(t)},getContainer:function(){return this.currentContainer||this.originalContainer},getMute:function(){return this._model.getMute()},triggerError:function(t){var e=this._model;t.message=e.get("localization").errors[t.key],delete t.key,e.set("errorEvent",t),e.set("state",d.lb),e.once("change:state",(function(){this.set("errorEvent",void 0)}),e),this.trigger(d.w,t)}});e.default=Me},,,,,,,,,,,,function(t,e){!function(t,e){"use strict";if("IntersectionObserver"in t&&"IntersectionObserverEntry"in t&&"intersectionRatio"in t.IntersectionObserverEntry.prototype)"isIntersecting"in t.IntersectionObserverEntry.prototype||Object.defineProperty(t.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var i=[];o.prototype.THROTTLE_TIMEOUT=100,o.prototype.POLL_INTERVAL=null,o.prototype.USE_MUTATION_OBSERVER=!0,o.prototype.observe=function(t){if(!this._observationTargets.some((function(e){return e.element==t}))){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(),this._checkForIntersections()}},o.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._observationTargets.length||(this._unmonitorIntersections(),this._unregisterInstance())},o.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorIntersections(),this._unregisterInstance()},o.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},o.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,i){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==i[e-1]}))},o.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},o.prototype._monitorIntersections=function(){this._monitoringIntersections||(this._monitoringIntersections=!0,this.POLL_INTERVAL?this._monitoringInterval=setInterval(this._checkForIntersections,this.POLL_INTERVAL):(a(t,"resize",this._checkForIntersections,!0),a(e,"scroll",this._checkForIntersections,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in t&&(this._domObserver=new MutationObserver(this._checkForIntersections),this._domObserver.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))))},o.prototype._unmonitorIntersections=function(){this._monitoringIntersections&&(this._monitoringIntersections=!1,clearInterval(this._monitoringInterval),this._monitoringInterval=null,r(t,"resize",this._checkForIntersections,!0),r(e,"scroll",this._checkForIntersections,!0),this._domObserver&&(this._domObserver.disconnect(),this._domObserver=null))},o.prototype._checkForIntersections=function(){var e=this._rootIsInDom(),i=e?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach((function(o){var a=o.element,r=s(a),l=this._rootContainsTarget(a),c=o.entry,u=e&&l&&this._computeTargetAndRootIntersection(a,i),d=o.entry=new n({time:t.performance&&performance.now&&performance.now(),target:a,boundingClientRect:r,rootBounds:i,intersectionRect:u});c?e&&l?this._hasCrossedThreshold(c,d)&&this._queuedEntries.push(d):c&&c.isIntersecting&&this._queuedEntries.push(d):this._queuedEntries.push(d)}),this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)},o.prototype._computeTargetAndRootIntersection=function(i,n){if("none"!=t.getComputedStyle(i).display){for(var o,a,r,l,u,d,p,h,f=s(i),w=c(i),g=!1;!g;){var j=null,b=1==w.nodeType?t.getComputedStyle(w):{};if("none"==b.display)return;if(w==this.root||w==e?(g=!0,j=n):w!=e.body&&w!=e.documentElement&&"visible"!=b.overflow&&(j=s(w)),j&&(o=j,a=f,r=void 0,l=void 0,u=void 0,d=void 0,p=void 0,h=void 0,r=Math.max(o.top,a.top),l=Math.min(o.bottom,a.bottom),u=Math.max(o.left,a.left),d=Math.min(o.right,a.right),h=l-r,!(f=(p=d-u)>=0&&h>=0&&{top:r,bottom:l,left:u,right:d,width:p,height:h})))break;w=c(w)}return f}},o.prototype._getRootRect=function(){var t;if(this.root)t=s(this.root);else{var i=e.documentElement,n=e.body;t={top:0,left:0,right:i.clientWidth||n.clientWidth,width:i.clientWidth||n.clientWidth,bottom:i.clientHeight||n.clientHeight,height:i.clientHeight||n.clientHeight}}return this._expandRectByRootMargin(t)},o.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map((function(e,i){return"px"==e.unit?e.value:e.value*(i%2?t.width:t.height)/100})),i={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return i.width=i.right-i.left,i.height=i.bottom-i.top,i},o.prototype._hasCrossedThreshold=function(t,e){var i=t&&t.isIntersecting?t.intersectionRatio||0:-1,n=e.isIntersecting?e.intersectionRatio||0:-1;if(i!==n)for(var o=0;o=0&&(n.metadata.mpegts=o+e)}var a=this.getLiveLatency();null!==a&&(n.latency=a),(this.state===r.pb||this.seeking)&&this.trigger(r.S,n)}},click:function(t){this.trigger(r.n,t)},volumechange:function(){var t=this.video;this.trigger(r.V,{volume:Math.round(100*t.volume)}),this.trigger(r.M,{mute:t.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(r.R))},playing:function(){-1===this.stallTime&&this.setState(r.pb),this.trigger(r.fb)},pause:function(){this.state!==r.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(r.ob))},progress:function(){var t=this.getDuration();if(!(t<=0||t===1/0)){var e=this.video.buffered;if(e&&0!==e.length){var i=Object(s.a)(e.end(e.length-1)/t,0,1);this.trigger(r.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:t,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(r.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==r.mb&&this.state!==r.kb&&this.trigger(r.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},c=i(10);function u(t){return t&&t.length?t.end(t.length-1):0}var d={container:null,volume:function(t){this.video.volume=Math.min(Math.max(0,t/100),1)},mute:function(t){this.video.muted=!!t,this.video.muted||this.video.removeAttribute("muted")},resize:function(t,e,i){var n=this.video,a=n.videoWidth,r=n.videoHeight;if(t&&e&&a&&r){var s={objectFit:"",width:"",height:""};if("uniform"===i){var l=t/e,u=a/r,d=Math.abs(l-u);d<.09&&d>.0025&&(s.objectFit="fill",i="exactfit")}if(o.Browser.ie||o.OS.iOS&&o.OS.version.major<9||o.Browser.androidNative)if("uniform"!==i){s.objectFit="contain";var p=t/e,h=a/r,f=1,w=1;"none"===i?f=w=p>h?Math.ceil(100*r/e)/100:Math.ceil(100*a/t)/100:"fill"===i?f=w=p>h?p/h:h/p:"exactfit"===i&&(p>h?(f=p/h,w=1):(f=1,w=h/p)),Object(c.e)(n,"matrix(".concat(f.toFixed(2),", 0, 0, ").concat(w.toFixed(2),", 0, 0)"))}else s.top=s.left=s.margin="",Object(c.e)(n,"");Object(c.d)(n,s)}},getContainer:function(){return this.container},setContainer:function(t){this.container=t,this.video.parentNode!==t&&t.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var t=this.container;t&&t===this.video.parentNode&&t.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return u(this.video.buffered)-this.video.currentTime<=2}},p={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},h=i(65),f=i(5),w=i(53),g=i(7),j=i(66),b=i(63),m={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function v(t,e){for(var i,n,o,a=t.length,r="",s=e||0;s>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:r+=String.fromCharCode(i);break;case 12:case 13:n=t[s++],r+=String.fromCharCode((31&i)<<6|63&n);break;case 14:n=t[s++],o=t[s++],r+=String.fromCharCode((15&i)<<12|(63&n)<<6|(63&o)<<0)}return r}function y(t){var e=function(t){for(var e="0x",i=0;i>1|(8323072&e)>>2|(2130706432&e)>>3}function k(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(t,e){if(!("value"in e)&&"data"in e&&e.data instanceof ArrayBuffer){var i=new Uint8Array(e.data),n=i.length;e={value:{key:"",data:""}};for(var o=10;o<14&&o0){var c=v(i.subarray(a,a+=s),0);if("PRIV"===e.value.key){if("com.apple.streaming.transportStreamTimestamp"===c){var u=1&y(i.subarray(a,a+=4)),d=y(i.subarray(a,a+=4))+(u?4294967296:0);e.value.data=d}else e.value.data=v(i,a+1);e.value.info=c}else e.value.info=c,e.value.data=v(i,a+1)}else{var p=i[a];e.value.data=1===p||2===p?function(t,e){for(var i=t.length-1,n="",o=e||0;o=0&&o[a].startTime>e.startTime;a--)i.unshift(o[a]),t.removeCue(o[a]);try{t.addCue(e),i.forEach((function(e){return t.addCue(e)}))}catch(t){console.error(t)}t.mode=n}(e,n)}else try{e.addCue(i)}catch(t){console.error(t)}}function M(t,e){e&&e.length&&Object(n.f)(e,(function(e){if(!(o.Browser.ie&&t&&/^(native|subtitle|cc)/.test(e._id))){o.Browser.ie&&"disabled"===e.mode||(e.mode="disabled",e.mode="hidden");for(var i=e.cues.length;i--;)e.removeCue(e.cues[i]);e.embedded||(e.mode="disabled"),e.inuse=!1}}))}function S(t){return"subtitles"===t||"captions"===t}function E(t){var e,i=Object(b.b)(t,this._unknownCount),o=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===t.kind){var a=this.video.textTracks;(e=Object(n.j)(a,{label:o}))||(e=this.video.addTextTrack(t.kind,o,t.language||"")),e.default=t.default,e.mode="disabled",e.inuse=!0}else(e=t).data=e.data||[];return e._id||(e._id=Object(b.a)(t,this._textTracks.length)),e}function I(t){this._textTracks.push(t),this._tracksById[t._id]=t}function L(){if(this._textTracks){var t=this._textTracks.filter((function(t){return t.embedded||"subs"===t.groupid}));this._initTextTracks(),t.forEach((function(t){this._tracksById[t._id]=t})),this._textTracks=t}}function A(t){this.triggerActiveCues(t.currentTarget.activeCues)}function P(t,e,i){var n=t.kind;this._cachedVTTCues[t._id]||(this._cachedVTTCues[t._id]={});var o,a=this._cachedVTTCues[t._id];switch(n){case"captions":case"subtitles":o=i||Math.floor(20*e.startTime);var r="_"+e.line,s=Math.floor(20*e.endTime),l=a[o+r]||a[o+1+r]||a[o-1+r];return!(l&&Math.abs(l-s)<=1)&&(a[o+r]=s,!0);case"metadata":var c=e.data?new Uint8Array(e.data).join(""):e.text;return!a[o=i||e.startTime+c]&&(a[o]=e.endTime,!0);default:return!1}}function R(t){if(t.length>this._textTracks.length)return!0;for(var e=0;e=0&&(w.retries=0);var t=w.getVideoCurrentTime();w.currentTime=t,M&&C!==t&&$(t),l.timeupdate.call(w),ft(),o.Browser.ie&&G()},resize:G,ended:function(){_=-1,wt(),l.ended.call(w)},loadedmetadata:function(){var t=w.getDuration();R&&t===1/0&&(t=0);var e={metadataType:"media",duration:t,height:v.videoHeight,width:v.videoWidth,seekRange:w.getSeekRange()};w.trigger(r.K,e),G()},durationchange:function(){R||l.progress.call(w)},loadeddata:function(){var t;!function(){if(v.getStartDate){var t=v.getStartDate(),e=t.getTime?t.getTime():NaN;if(e!==w.startDateTime&&!isNaN(e)){w.startDateTime=e;var i=t.toISOString(),n=w.getSeekRange(),o=n.start,a=n.end,s={metadataType:"program-date-time",programDateTime:i,start:o,end:a},l=w.createCue(o,a,JSON.stringify(s));w.addVTTCue({type:"metadata",cue:l}),delete s.metadataType,w.trigger(r.L,{metadataType:"program-date-time",metadata:s})}}}(),l.loadeddata.call(w),function(t){if(E=null,!t)return;if(t.length){for(var e=0;e0&&(e=t.map((function(t,e){return{label:t.label||e}}))),e}function it(t){w.currentTime=-1,j=t.minDvrWindow,m=t.sources,_=function(t){var i=Math.max(0,_),n=e.qualityLabel;if(t)for(var o=0;o0&&(T=-1,w.seek(t)),t>0&&w.getVideoCurrentTime()!==t&&w.seek(t);var n=et(m);n&&w.trigger(r.I,{levels:n,currentQuality:_}),m.length&&"hls"!==m[0].type&&ht()}function at(t){E=null,I=-1,y.reason||(y.reason="initial choice",y.level={}),x=!1;var e=document.createElement("source");e.src=t.file,v.src!==e.src&&(v.src=t.file)}function rt(){v&&(w.disableTextTrack(),v.removeAttribute("preload"),v.removeAttribute("src"),Object(f.h)(v),Object(c.d)(v,{objectFit:""}),_=-1,!o.Browser.msie&&"load"in v&&v.load())}function st(){var t=1/0;return["buffered","seekable"].forEach((function(e){for(var i=v[e],o=i?i.length:0;o--;){var a=Math.min(t,i.start(o));Object(n.o)(a)&&(t=a)}})),t}function lt(){var t=0;return["buffered","seekable"].forEach((function(e){for(var i=v[e],o=i?i.length:0;o--;){var a=Math.max(t,i.end(o));Object(n.o)(a)&&(t=a)}})),t}function ct(){for(var t=-1,e=0;e-1&&t1)&&function(t){X=t.end,J=Math.min(0,w.getVideoCurrentTime()-X),Z=Object(V.a)()}(e),Object(h.a)(e.end-e.start,j))return J}return t}(w.getVideoCurrentTime())},w.getDuration=function(){if(e.getDurationHook)return e.getDurationHook();var t=v.duration;if(R&&t===1/0&&0===w.getVideoCurrentTime()||isNaN(t))return 0;var i=lt();if(v.duration===1/0&&i){var n=i-st();Object(h.a)(n,j)&&(t=-n)}return t},w.getSeekRange=function(){var t={start:0,end:w.getDuration()};return v.seekable.length&&(t.end=lt(),t.start=st()),t},w.getLiveLatency=function(){var t=null,e=lt();return w.isLive()&&e&&(t=e+(Object(V.a)()-Z)/1e3-w.getVideoCurrentTime()),t},this.stop=function(){wt(),rt(),this.clearTracks(),o.Browser.ie&&v.pause(),this.setState(r.mb)},this.destroy=function(){S=Q,Y(b,v),this.removeTracksListener(v.audioTracks,"change",ct),this.removeTracksListener(v.textTracks,"change",w.textTrackChangeHandler),this.off()},this.init=function(t){w.retries=0,w.maxRetries=t.adType?0:3,it(t);var e=m[_];(R=Object(a.a)(e))&&(w.supportsPlaybackRate=!1,b.waiting=Q),w.eventsOn_(),m.length&&"hls"!==m[0].type&&this.sendMediaType(m),y.reason=""},this.preload=function(t){it(t);var e=m[_],i=e.preload||"metadata";"none"!==i&&(v.setAttribute("preload",i),at(e))},this.load=function(t){it(t),ot(t.starttime),this.setupSideloadedTracks(t.tracks)},this.play=function(){return S(),nt()},this.pause=function(){wt(),S=function(){if(v.paused&&w.getVideoCurrentTime()&&w.isLive()){var t=lt(),e=t-st(),i=!Object(h.a)(e,j),o=t-w.getVideoCurrentTime();if(i&&t&&(o>15||o<0)){if(O=Math.max(t-10,t-e),!Object(n.o)(O))return;$(w.getVideoCurrentTime()),v.currentTime=O}}},v.pause()},this.seek=function(t){if(!e.seekHook||!e.seekHook(t,v)){var i=w.getSeekRange(),n=t;if(t<0&&(n+=i.end),x||(x=!!lt()),x){T=0;try{if(w.seeking=!0,w.isLive()&&Object(h.a)(i.end-i.start,j))if(J=Math.min(0,n-X),t<0)n+=Math.min(12,(Object(V.a)()-Z)/1e3);O=n,$(w.getVideoCurrentTime()),v.currentTime=n}catch(t){w.seeking=!1,T=n}}else T=n,o.Browser.firefox&&v.paused&&nt()}},this.setVisibility=function(t){(t=!!t)||o.OS.android?Object(c.d)(w.container,{visibility:"visible",opacity:1}):Object(c.d)(w.container,{visibility:"",opacity:0})},this.setFullscreen=function(t){if(t=!!t){try{var e=v.webkitEnterFullscreen||v.webkitEnterFullScreen;e&&e.apply(v)}catch(t){return!1}return w.getFullScreen()}var i=v.webkitExitFullscreen||v.webkitExitFullScreen;return i&&i.apply(v),t},w.getFullScreen=function(){return M||!!v.webkitDisplayingFullscreen},this.setCurrentQuality=function(t){_!==t&&t>=0&&m&&m.length>t&&(_=t,y.reason="api",y.level={},this.trigger(r.J,{currentQuality:t,levels:et(m)}),e.qualityLabel=m[t].label,ot(w.getVideoCurrentTime()||0),nt())},this.setPlaybackRate=function(t){v.playbackRate=v.defaultPlaybackRate=t},this.getPlaybackRate=function(){return v.playbackRate},this.getCurrentQuality=function(){return _},this.getQualityLevels=function(){return Array.isArray(m)?m.map((function(t){return function(t){return{bitrate:t.bitrate,label:t.label,width:t.width,height:t.height}}(t)})):[]},this.getName=function(){return{name:W}},this.setCurrentAudioTrack=dt,this.getAudioTracks=function(){return E||[]},this.getCurrentAudioTrack=function(){return I}}Object(n.g)(X.prototype,w.a),X.getName=function(){return{name:"html5"}};e.default=X;var K=220001},,,,,,,,,,,,,,,,,,,,,,,,,,function(t,e,i){"use strict";i.d(e,"a",(function(){return o}));var n=i(2);function o(t){var e=[],i=(t=Object(n.i)(t)).split("\r\n\r\n");1===i.length&&(i=t.split("\n\n"));for(var o=0;o0&&(o=0),i.length>o+1&&i[o+1]){var a=i[o],r=a.indexOf(" --\x3e ");r>0&&(e.begin=Object(n.g)(a.substr(0,r)),e.end=Object(n.g)(a.substr(r+5)),e.text=i.slice(o+1).join("\r\n"))}return e}},function(t,e,i){"use strict";i.d(e,"a",(function(){return o})),i.d(e,"b",(function(){return a}));var n=i(5);function o(t){var e=-1;return t>=1280?e=7:t>=960?e=6:t>=800?e=5:t>=640?e=4:t>=540?e=3:t>=420?e=2:t>=320?e=1:t>=250&&(e=0),e}function a(t,e){var i="jw-breakpoint-"+e;Object(n.p)(t,/jw-breakpoint--?\d+/,i)}},function(t,e,i){"use strict";i.d(e,"a",(function(){return d}));var n,o=i(0),a=i(8),r=i(16),s=i(7),l=i(3),c=i(10),u=i(5),d={back:!0,backgroundOpacity:50,edgeStyle:null,fontSize:14,fontOpacity:100,fontScale:.05,preprocessor:o.k,windowOpacity:0},p=function(t){var e,s,p,h,f,w,g,j,b,m=this,v=t.player;function y(){Object(o.o)(e.fontSize)&&(v.get("containerHeight")?j=d.fontScale*(e.userFontScale||1)*e.fontSize/d.fontSize:v.once("change:containerHeight",y,this))}function k(){var t=v.get("containerHeight");if(t){var e;if(v.get("fullscreen")&&a.OS.iOS)e=null;else{var i=t*j;e=Math.round(10*function(t){var e=v.get("mediaElement");if(e&&e.videoHeight){var i=e.videoWidth,n=e.videoHeight,o=i/n,r=v.get("containerHeight"),s=v.get("containerWidth");if(v.get("fullscreen")&&a.OS.mobile){var l=window.screen;l.orientation&&(r=l.availHeight,s=l.availWidth)}if(s&&r&&i&&n)return(s/r>o?r:n*s/i)*j}return t}(i))/10}v.get("renderCaptionsNatively")?function(t,e){var i="#".concat(t," .jw-video::-webkit-media-text-track-display");e&&(e+="px",a.OS.iOS&&Object(c.b)(i,{fontSize:"inherit"},t,!0));b.fontSize=e,Object(c.b)(i,b,t,!0)}(v.get("id"),e):Object(c.d)(f,{fontSize:e})}}function x(t,e,i){var n=Object(c.c)("#000000",i);"dropshadow"===t?e.textShadow="0 2px 1px "+n:"raised"===t?e.textShadow="0 0 5px "+n+", 0 1px 5px "+n+", 0 2px 5px "+n:"depressed"===t?e.textShadow="0 -2px 1px "+n:"uniform"===t&&(e.textShadow="-2px 0 1px "+n+",2px 0 1px "+n+",0 -2px 1px "+n+",0 2px 1px "+n+",-1px 1px 1px "+n+",1px 1px 1px "+n+",1px -1px 1px "+n+",1px 1px 1px "+n)}(f=document.createElement("div")).className="jw-captions jw-reset",this.show=function(){Object(u.a)(f,"jw-captions-enabled")},this.hide=function(){Object(u.o)(f,"jw-captions-enabled")},this.populate=function(t){v.get("renderCaptionsNatively")||(p=[],s=t,t?this.selectCues(t,h):this.renderCues())},this.resize=function(){k(),this.renderCues(!0)},this.renderCues=function(t){t=!!t,n&&n.processCues(window,p,f,t)},this.selectCues=function(t,e){if(t&&t.data&&e&&!v.get("renderCaptionsNatively")){var i=this.getAlignmentPosition(t,e);!1!==i&&(p=this.getCurrentCues(t.data,i),this.renderCues(!0))}},this.getCurrentCues=function(t,e){return Object(o.h)(t,(function(t){return e>=t.startTime&&(!t.endTime||e<=t.endTime)}))},this.getAlignmentPosition=function(t,e){var i=t.source,n=e.metadata,a=e.currentTime;return i&&n&&Object(o.r)(n[i])&&(a=n[i]),a},this.clear=function(){Object(u.g)(f)},this.setup=function(t,i){w=document.createElement("div"),g=document.createElement("span"),w.className="jw-captions-window jw-reset",g.className="jw-captions-text jw-reset",e=Object(o.g)({},d,i),j=d.fontScale;var n=function(){if(!v.get("renderCaptionsNatively")){y(e.fontSize);var i=e.windowColor,n=e.windowOpacity,o=e.edgeStyle;b={};var r={};!function(t,e){var i=e.color,n=e.fontOpacity;(i||n!==d.fontOpacity)&&(t.color=Object(c.c)(i||"#ffffff",n));if(e.back){var o=e.backgroundColor,a=e.backgroundOpacity;o===d.backgroundColor&&a===d.backgroundOpacity||(t.backgroundColor=Object(c.c)(o,a))}else t.background="transparent";e.fontFamily&&(t.fontFamily=e.fontFamily);e.fontStyle&&(t.fontStyle=e.fontStyle);e.fontWeight&&(t.fontWeight=e.fontWeight);e.textDecoration&&(t.textDecoration=e.textDecoration)}(r,e),(i||n!==d.windowOpacity)&&(b.backgroundColor=Object(c.c)(i||"#000000",n)),x(o,r,e.fontOpacity),e.back||null!==o||x("uniform",r),Object(c.d)(w,b),Object(c.d)(g,r),function(t,e){k(),function(t,e){a.Browser.safari&&Object(c.b)("#"+t+" .jw-video::-webkit-media-text-track-display-backdrop",{backgroundColor:e.backgroundColor},t,!0);Object(c.b)("#"+t+" .jw-video::-webkit-media-text-track-display",b,t,!0),Object(c.b)("#"+t+" .jw-video::cue",e,t,!0)}(t,e),function(t,e){Object(c.b)("#"+t+" .jw-text-track-display",b,t),Object(c.b)("#"+t+" .jw-text-track-cue",e,t)}(t,e)}(t,r)}};n(),w.appendChild(g),f.appendChild(w),v.change("captionsTrack",(function(t,e){this.populate(e)}),this),v.set("captions",e),v.on("change:captions",(function(t,i){e=i,n()}))},this.element=function(){return f},this.destroy=function(){v.off(null,null,this),this.off()};var T=function(t){h=t,m.selectCues(s,h)};v.on("change:playlistItem",(function(){h=null,p=[]}),this),v.on(l.Q,(function(t){p=[],T(t)}),this),v.on(l.S,T,this),v.on("subtitlesTrackData",(function(){this.selectCues(s,h)}),this),v.on("change:captionsList",(function t(e,o){var a=this;1!==o.length&&(e.get("renderCaptionsNatively")||n||(i.e(8).then(function(t){n=i(68).default}.bind(null,i)).catch(Object(r.c)(301121)).catch((function(t){a.trigger(l.tb,t)})),e.off("change:captionsList",t,this)))}),this)};Object(o.g)(p.prototype,s.a),e.b=p},function(t,e,i){"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i=function(t,e){var i=t[1]||"",n=t[3];if(!n)return i;if(e&&"function"==typeof btoa){var o=(r=n,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */"),a=n.sources.map((function(t){return"/*# sourceURL="+n.sourceRoot+t+" */"}));return[i].concat(a).concat([o]).join("\n")}var r;return[i].join("\n")}(e,t);return e[2]?"@media "+e[2]+"{"+i+"}":i})).join("")},e.i=function(t,i){"string"==typeof t&&(t=[[null,t,""]]);for(var n={},o=0;o'},function(t,e,i){"use strict";function n(t,e){var i=t.kind||"cc";return t.default||t.defaulttrack?"default":t._id||t.file||i+e}function o(t,e){var i=t.label||t.name||t.language;return i||(i="Unknown CC",(e+=1)>1&&(i+=" ["+e+"]")),{label:i,unknownCount:e}}i.d(e,"a",(function(){return n})),i.d(e,"b",(function(){return o}))},function(t,e,i){"use strict";function n(t){return new Promise((function(e,i){if(t.paused)return i(o("NotAllowedError",0,"play() failed."));var n=function(){t.removeEventListener("play",a),t.removeEventListener("playing",r),t.removeEventListener("pause",r),t.removeEventListener("abort",r),t.removeEventListener("error",r)},a=function(){t.addEventListener("playing",r),t.addEventListener("abort",r),t.addEventListener("error",r),t.addEventListener("pause",r)},r=function(t){if(n(),"playing"===t.type)e();else{var a='The play() request was interrupted by a "'.concat(t.type,'" event.');"error"===t.type?i(o("NotSupportedError",9,a)):i(o("AbortError",20,a))}};t.addEventListener("play",a)}))}function o(t,e,i){var n=new Error(i);return n.name=t,n.code=e,n}i.d(e,"a",(function(){return n}))},function(t,e,i){"use strict";function n(t,e){return t!==1/0&&Math.abs(t)>=Math.max(a(e),0)}function o(t,e){var i="VOD";return t===1/0?i="LIVE":t<0&&(i=n(t,a(e))?"DVR":"LIVE"),i}function a(t){return void 0===t?120:Math.max(t,0)}i.d(e,"a",(function(){return n})),i.d(e,"b",(function(){return o}))},function(t,e,i){"use strict";var n=i(67),o=i(16),a=i(22),r=i(4),s=i(57),l=i(2),c=i(1);function u(t){throw new c.n(null,t)}function d(t,e,n){t.xhr=Object(a.a)(t.file,(function(a){!function(t,e,n,a){var d,p,f=t.responseXML?t.responseXML.firstChild:null;if(f)for("xml"===Object(r.b)(f)&&(f=f.nextSibling);f.nodeType===f.COMMENT_NODE;)f=f.nextSibling;try{if(f&&"tt"===Object(r.b)(f))d=function(t){t||u(306007);var e=[],i=t.getElementsByTagName("p"),n=30,o=t.getElementsByTagName("tt");if(o&&o[0]){var a=parseFloat(o[0].getAttribute("ttp:frameRate"));isNaN(a)||(n=a)}i||u(306005),i.length||(i=t.getElementsByTagName("tt:p")).length||(i=t.getElementsByTagName("tts:p"));for(var r=0;r\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(f){var w=s.getAttribute("begin"),g=s.getAttribute("dur"),j=s.getAttribute("end"),b={begin:Object(l.g)(w,n),text:f};j?b.end=Object(l.g)(j,n):g&&(b.end=b.begin+Object(l.g)(g,n)),e.push(b)}}return e.length||u(306005),e}(t.responseXML),p=h(d),delete e.xhr,n(p);else{var w=t.responseText;w.indexOf("WEBVTT")>=0?i.e(10).then(function(t){return i(97).default}.bind(null,i)).catch(Object(o.c)(301131)).then((function(t){var i=new t(window);p=[],i.oncue=function(t){p.push(t)},i.onflush=function(){delete e.xhr,n(p)},i.parse(w)})).catch((function(t){delete e.xhr,a(Object(c.v)(null,c.b,t))})):(d=Object(s.a)(w),p=h(d),delete e.xhr,n(p))}}catch(t){delete e.xhr,a(Object(c.v)(null,c.b,t))}}(a,t,e,n)}),(function(t,e,i,o){n(Object(c.u)(o,c.b))}))}function p(t){t&&t.forEach((function(t){var e=t.xhr;e&&(e.onload=null,e.onreadystatechange=null,e.onerror=null,"abort"in e&&e.abort()),delete t.xhr}))}function h(t){return t.map((function(t){return new n.a(t.begin,t.end,t.text)}))}i.d(e,"c",(function(){return d})),i.d(e,"a",(function(){return p})),i.d(e,"b",(function(){return h}))},function(t,e,i){"use strict";var n=window.VTTCue;function o(t){if("string"!=typeof t)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[t.toLowerCase()]&&t.toLowerCase()}if(!n){(n=function(t,e,i){var n=this;n.hasBeenReset=!1;var a="",r=!1,s=t,l=e,c=i,u=null,d="",p=!0,h="auto",f="start",w="auto",g=100,j="middle";Object.defineProperty(n,"id",{enumerable:!0,get:function(){return a},set:function(t){a=""+t}}),Object.defineProperty(n,"pauseOnExit",{enumerable:!0,get:function(){return r},set:function(t){r=!!t}}),Object.defineProperty(n,"startTime",{enumerable:!0,get:function(){return s},set:function(t){if("number"!=typeof t)throw new TypeError("Start time must be set to a number.");s=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"endTime",{enumerable:!0,get:function(){return l},set:function(t){if("number"!=typeof t)throw new TypeError("End time must be set to a number.");l=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"text",{enumerable:!0,get:function(){return c},set:function(t){c=""+t,this.hasBeenReset=!0}}),Object.defineProperty(n,"region",{enumerable:!0,get:function(){return u},set:function(t){u=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"vertical",{enumerable:!0,get:function(){return d},set:function(t){var e=function(t){return"string"==typeof t&&(!!{"":!0,lr:!0,rl:!0}[t.toLowerCase()]&&t.toLowerCase())}(t);if(!1===e)throw new SyntaxError("An invalid or illegal string was specified.");d=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"snapToLines",{enumerable:!0,get:function(){return p},set:function(t){p=!!t,this.hasBeenReset=!0}}),Object.defineProperty(n,"line",{enumerable:!0,get:function(){return h},set:function(t){if("number"!=typeof t&&"auto"!==t)throw new SyntaxError("An invalid number or illegal string was specified.");h=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"lineAlign",{enumerable:!0,get:function(){return f},set:function(t){var e=o(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(n,"position",{enumerable:!0,get:function(){return w},set:function(t){if(t<0||t>100)throw new Error("Position must be between 0 and 100.");w=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"size",{enumerable:!0,get:function(){return g},set:function(t){if(t<0||t>100)throw new Error("Size must be between 0 and 100.");g=t,this.hasBeenReset=!0}}),Object.defineProperty(n,"align",{enumerable:!0,get:function(){return j},set:function(t){var e=o(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");j=e,this.hasBeenReset=!0}}),n.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}e.a=n},,function(t,e,i){var n=i(70);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(t.exports=n.locals)},function(t,e,i){(t.exports=i(60)(!1)).push([t.i,'.jw-reset{text-align:left;direction:ltr}.jw-reset-text,.jw-reset{color:inherit;background-color:transparent;padding:0;margin:0;float:none;font-family:Arial,Helvetica,sans-serif;font-size:1em;line-height:1em;list-style:none;text-transform:none;vertical-align:baseline;border:0;font-variant:inherit;font-stretch:inherit;-webkit-tap-highlight-color:rgba(255,255,255,0)}body .jw-error,body .jwplayer.jw-state-error{height:100%;width:100%}.jw-title{position:absolute;top:0}.jw-background-color{background:rgba(0,0,0,0.4)}.jw-text{color:rgba(255,255,255,0.8)}.jw-knob{color:rgba(255,255,255,0.8);background-color:#fff}.jw-button-color{color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):focus,:not(.jw-flag-touch) .jw-button-color:not(.jw-logo-button):hover{color:#fff}.jw-toggle{color:#fff}.jw-toggle.jw-off{color:rgba(255,255,255,0.8)}.jw-toggle.jw-off:focus{color:#fff}.jw-toggle:focus{outline:none}:not(.jw-flag-touch) .jw-toggle.jw-off:hover{color:#fff}.jw-rail{background:rgba(255,255,255,0.3)}.jw-buffer{background:rgba(255,255,255,0.3)}.jw-progress{background:#f2f2f2}.jw-time-tip,.jw-volume-tip{border:0}.jw-slider-volume.jw-volume-tip.jw-background-color.jw-slider-vertical{background:none}.jw-skip{padding:.5em;outline:none}.jw-skip .jw-skiptext,.jw-skip .jw-skip-icon{color:rgba(255,255,255,0.8)}.jw-skip.jw-skippable:hover .jw-skip-icon,.jw-skip.jw-skippable:focus .jw-skip-icon{color:#fff}.jw-icon-cast google-cast-launcher{--connected-color:#fff;--disconnected-color:rgba(255,255,255,0.8)}.jw-icon-cast google-cast-launcher:focus{outline:none}.jw-icon-cast google-cast-launcher.jw-off{--connected-color:rgba(255,255,255,0.8)}.jw-icon-cast:focus google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-icon-cast:hover google-cast-launcher{--connected-color:#fff;--disconnected-color:#fff}.jw-nextup-container{bottom:2.5em;padding:5px .5em}.jw-nextup{border-radius:0}.jw-color-active{color:#fff;stroke:#fff;border-color:#fff}:not(.jw-flag-touch) .jw-color-active-hover:hover,:not(.jw-flag-touch) .jw-color-active-hover:focus{color:#fff;stroke:#fff;border-color:#fff}.jw-color-inactive{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}:not(.jw-flag-touch) .jw-color-inactive-hover:hover{color:rgba(255,255,255,0.8);stroke:rgba(255,255,255,0.8);border-color:rgba(255,255,255,0.8)}.jw-option{color:rgba(255,255,255,0.8)}.jw-option.jw-active-option{color:#fff;background-color:rgba(255,255,255,0.1)}:not(.jw-flag-touch) .jw-option:hover{color:#fff}.jwplayer{width:100%;font-size:16px;position:relative;display:block;min-height:0;overflow:hidden;box-sizing:border-box;font-family:Arial,Helvetica,sans-serif;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;outline:none}.jwplayer *{box-sizing:inherit}.jwplayer.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jwplayer.jw-flag-aspect-mode{height:auto !important}.jwplayer.jw-flag-aspect-mode .jw-aspect{display:block}.jwplayer .jw-aspect{display:none}.jwplayer .jw-swf{outline:none}.jw-media,.jw-preview{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}.jw-media{overflow:hidden;cursor:pointer}.jw-plugin{position:absolute;bottom:66px}.jw-breakpoint-7 .jw-plugin{bottom:132px}.jw-plugin .jw-banner{max-width:100%;opacity:0;cursor:pointer;position:absolute;margin:auto auto 0;left:0;right:0;bottom:0;display:block}.jw-preview,.jw-captions,.jw-title{pointer-events:none}.jw-media,.jw-logo{pointer-events:all}.jw-wrapper{background-color:#000;position:absolute;top:0;left:0;right:0;bottom:0}.jw-hidden-accessibility{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.jw-contract-trigger::before{content:"";overflow:hidden;width:200%;height:200%;display:block;position:absolute;top:0;left:0}.jwplayer .jw-media video{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;margin:auto;background:transparent}.jwplayer .jw-media video::-webkit-media-controls-start-playback-button{display:none}.jwplayer.jw-stretch-uniform .jw-media video{object-fit:contain}.jwplayer.jw-stretch-none .jw-media video{object-fit:none}.jwplayer.jw-stretch-fill .jw-media video{object-fit:cover}.jwplayer.jw-stretch-exactfit .jw-media video{object-fit:fill}.jw-preview{position:absolute;display:none;opacity:1;visibility:visible;width:100%;height:100%;background:#000 no-repeat 50% 50%}.jwplayer .jw-preview,.jw-error .jw-preview{background-size:contain}.jw-stretch-none .jw-preview{background-size:auto auto}.jw-stretch-fill .jw-preview{background-size:cover}.jw-stretch-exactfit .jw-preview{background-size:100% 100%}.jw-title{display:none;padding-top:20px;width:100%;z-index:1}.jw-title-primary,.jw-title-secondary{color:#fff;padding-left:20px;padding-right:20px;padding-bottom:.5em;overflow:hidden;text-overflow:ellipsis;direction:unset;white-space:nowrap;width:100%}.jw-title-primary{font-size:1.625em}.jw-breakpoint-2 .jw-title-primary,.jw-breakpoint-3 .jw-title-primary{font-size:1.5em}.jw-flag-small-player .jw-title-primary{font-size:1.25em}.jw-flag-small-player .jw-title-secondary,.jw-title-secondary:empty{display:none}.jw-captions{position:absolute;width:100%;height:100%;text-align:center;display:none;letter-spacing:normal;word-spacing:normal;text-transform:none;text-indent:0;text-decoration:none;pointer-events:none;overflow:hidden;top:0}.jw-captions.jw-captions-enabled{display:block}.jw-captions-window{display:none;padding:.25em;border-radius:.25em}.jw-captions-window.jw-captions-window-active{display:inline-block}.jw-captions-text{display:inline-block;color:#fff;background-color:#000;word-wrap:normal;word-break:normal;white-space:pre-line;font-style:normal;font-weight:normal;text-align:center;text-decoration:none}.jw-text-track-display{font-size:inherit;line-height:1.5}.jw-text-track-cue{background-color:rgba(0,0,0,0.5);color:#fff;padding:.1em .3em}.jwplayer video::-webkit-media-controls{display:none;justify-content:flex-start}.jwplayer video::-webkit-media-text-track-display{min-width:-webkit-min-content}.jwplayer video::cue{background-color:rgba(0,0,0,0.5)}.jwplayer video::-webkit-media-controls-panel-container{display:none}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing) .jw-captions,.jwplayer.jw-flag-media-audio.jw-state-playing .jw-captions,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden) .jw-captions{max-height:calc(100% - 60px)}.jwplayer:not(.jw-flag-controls-hidden):not(.jw-state-playing):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-flag-media-audio.jw-state-playing:not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container,.jwplayer.jw-state-playing:not(.jw-flag-user-inactive):not(.jw-flag-controls-hidden):not(.jw-flag-ios-fullscreen) video::-webkit-media-text-track-container{max-height:calc(100% - 60px)}.jw-logo{position:absolute;margin:20px;cursor:pointer;pointer-events:all;background-repeat:no-repeat;background-size:contain;top:auto;right:auto;left:auto;bottom:auto;outline:none}.jw-logo.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-flag-audio-player .jw-logo{display:none}.jw-logo-top-right{top:0;right:0}.jw-logo-top-left{top:0;left:0}.jw-logo-bottom-left{left:0}.jw-logo-bottom-right{right:0}.jw-logo-bottom-left,.jw-logo-bottom-right{bottom:44px;transition:bottom 150ms cubic-bezier(0, .25, .25, 1)}.jw-state-idle .jw-logo{z-index:1}.jw-state-setup .jw-wrapper{background-color:inherit}.jw-state-setup .jw-logo,.jw-state-setup .jw-controls,.jw-state-setup .jw-controls-backdrop{visibility:hidden}span.jw-break{display:block}body .jw-error,body .jwplayer.jw-state-error{background-color:#333;color:#fff;font-size:16px;display:table;opacity:1;position:relative}body .jw-error .jw-display,body .jwplayer.jw-state-error .jw-display{display:none}body .jw-error .jw-media,body .jwplayer.jw-state-error .jw-media{cursor:default}body .jw-error .jw-preview,body .jwplayer.jw-state-error .jw-preview{background-color:#333}body .jw-error .jw-error-msg,body .jwplayer.jw-state-error .jw-error-msg{background-color:#000;border-radius:2px;display:flex;flex-direction:row;align-items:stretch;padding:20px}body .jw-error .jw-error-msg .jw-icon,body .jwplayer.jw-state-error .jw-error-msg .jw-icon{height:30px;width:30px;margin-right:20px;flex:0 0 auto;align-self:center}body .jw-error .jw-error-msg .jw-icon:empty,body .jwplayer.jw-state-error .jw-error-msg .jw-icon:empty{display:none}body .jw-error .jw-error-msg .jw-info-container,body .jwplayer.jw-state-error .jw-error-msg .jw-info-container{margin:0;padding:0}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg{flex-direction:column}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-error-text,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-error-text{text-align:center}body .jw-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-flag-small-player .jw-error-msg .jw-icon,body .jw-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon,body .jwplayer.jw-state-error:not(.jw-flag-audio-player).jw-breakpoint-2 .jw-error-msg .jw-icon{flex:.5 0 auto;margin-right:0;margin-bottom:20px}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break{display:inline}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-flag-small-player .jw-error-msg .jw-break:before,.jwplayer.jw-state-error.jw-breakpoint-2 .jw-error-msg .jw-break:before{content:" "}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg{height:100%;width:100%;top:0;position:absolute;left:0;background:#000;-webkit-transform:none;transform:none;padding:4px 16px;z-index:1}.jwplayer.jw-state-error.jw-flag-audio-player .jw-error-msg.jw-info-overlay{max-width:none;max-height:none}body .jwplayer.jw-state-error .jw-title,.jw-state-idle .jw-title,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-title{display:block}body .jwplayer.jw-state-error .jw-preview,.jw-state-idle .jw-preview,.jwplayer.jw-state-complete:not(.jw-flag-casting):not(.jw-flag-audio-player):not(.jw-flag-overlay-open-related) .jw-preview{display:block}.jw-state-idle .jw-captions,.jwplayer.jw-state-complete .jw-captions,body .jwplayer.jw-state-error .jw-captions{display:none}.jw-state-idle video::-webkit-media-text-track-container,.jwplayer.jw-state-complete video::-webkit-media-text-track-container,body .jwplayer.jw-state-error video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-fullscreen{width:100% !important;height:100% !important;top:0;right:0;bottom:0;left:0;z-index:1000;margin:0;position:fixed}body .jwplayer.jw-flag-flash-blocked .jw-title{display:block}.jwplayer.jw-flag-controls-hidden .jw-media{cursor:default}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:45px}.jw-flag-floating{background-size:cover;background-color:#000}.jw-flag-floating .jw-wrapper{position:fixed;z-index:2147483647;-webkit-animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;animation:jw-float-to-bottom 150ms cubic-bezier(0, .25, .25, 1) forwards 1;top:auto;bottom:1rem;left:auto;right:1rem;max-width:400px;max-height:400px;margin:0 auto}@media screen and (max-width:480px){.jw-flag-floating .jw-wrapper{width:100%;left:0;right:0}}.jw-flag-floating .jw-wrapper .jw-media{touch-action:none}@media screen and (max-device-width:480px) and (orientation:portrait){.jw-flag-touch.jw-flag-floating .jw-wrapper{-webkit-animation:none;animation:none;top:62px;bottom:auto;left:0;right:0;max-width:none;max-height:none}}.jw-flag-floating .jw-float-icon{pointer-events:all;cursor:pointer;display:none}.jw-flag-floating .jw-float-icon .jw-svg-icon{-webkit-filter:drop-shadow(0 0 1px #000);filter:drop-shadow(0 0 1px #000)}.jw-flag-floating.jw-floating-dismissible .jw-dismiss-icon{display:none}.jw-flag-floating.jw-floating-dismissible.jw-flag-ads .jw-float-icon{display:flex}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-logo,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-logo{display:none}.jw-flag-floating.jw-floating-dismissible.jw-state-paused .jw-float-icon,.jw-flag-floating.jw-floating-dismissible:not(.jw-flag-user-inactive) .jw-float-icon{display:flex}.jw-float-icon{display:none;position:absolute;top:3px;right:5px;align-items:center;justify-content:center}@-webkit-keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes jw-float-to-bottom{from{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.jw-flag-top{margin-top:2em;overflow:visible}.jw-top{height:2em;line-height:2;pointer-events:none;text-align:center;opacity:.8;position:absolute;top:-2em;width:100%}.jw-top .jw-icon{cursor:pointer;pointer-events:all;height:auto;width:auto}.jw-top .jw-text{color:#555}',""])},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e){t.exports=''},function(t,e,i){var n=i(96);"string"==typeof n&&(n=[["all-players",n,""]]),i(61).style(n,"all-players"),n.locals&&(t.exports=n.locals)},function(t,e,i){(t.exports=i(60)(!1)).push([t.i,'.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-flag-small-player .jw-settings-menu,.jw-settings-submenu{height:100%;width:100%}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;right:0}.jw-overlays,.jw-controls,.jw-controls-backdrop,.jw-settings-item-active::before{top:0;position:absolute;left:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-settings-menu .jw-icon.jw-button-color::after{position:absolute;bottom:0;left:0}.jw-nextup-close{position:absolute;top:0;right:0}.jw-overlays,.jw-controls,.jw-flag-small-player .jw-settings-menu{position:absolute;bottom:0;right:0}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after,.jw-time-tip::after,.jw-settings-menu .jw-icon.jw-button-color::after,.jw-text-live::before,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{content:"";display:block}.jw-svg-icon{height:24px;width:24px;fill:currentColor;pointer-events:none}.jw-icon{height:44px;width:44px;background-color:transparent;outline:none}.jw-icon.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-icon-airplay .jw-svg-icon-airplay-off{display:none}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-off{display:block}.jw-icon-airplay .jw-svg-icon-airplay-on{display:block}.jw-off.jw-icon-airplay .jw-svg-icon-airplay-on{display:none}.jw-icon-cc .jw-svg-icon-cc-off{display:none}.jw-off.jw-icon-cc .jw-svg-icon-cc-off{display:block}.jw-icon-cc .jw-svg-icon-cc-on{display:block}.jw-off.jw-icon-cc .jw-svg-icon-cc-on{display:none}.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:none}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-off{display:block}.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:block}.jw-off.jw-icon-fullscreen .jw-svg-icon-fullscreen-on{display:none}.jw-icon-volume .jw-svg-icon-volume-0{display:none}.jw-off.jw-icon-volume .jw-svg-icon-volume-0{display:block}.jw-icon-volume .jw-svg-icon-volume-100{display:none}.jw-full.jw-icon-volume .jw-svg-icon-volume-100{display:block}.jw-icon-volume .jw-svg-icon-volume-50{display:block}.jw-off.jw-icon-volume .jw-svg-icon-volume-50,.jw-full.jw-icon-volume .jw-svg-icon-volume-50{display:none}.jw-settings-menu .jw-icon::after,.jw-icon-settings::after,.jw-icon-volume::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon[aria-checked="true"]::after,.jw-settings-open .jw-icon-settings::after,.jw-icon-volume.jw-open::after{opacity:1}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-cc,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-settings,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-audio-tracks,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-hd,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-settings-sharing,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-fullscreen,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-airplay,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player).jw-flag-cast-available .jw-icon-cast{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume,.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-text-live{bottom:6px}.jwplayer.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-icon-volume::after{display:none}.jw-overlays,.jw-controls{pointer-events:none}.jw-controls-backdrop{display:block;background:linear-gradient(to bottom, transparent, rgba(0,0,0,0.4) 77%, rgba(0,0,0,0.4) 100%) 100% 100% / 100% 240px no-repeat transparent;transition:opacity 250ms cubic-bezier(0, .25, .25, 1),background-size 250ms cubic-bezier(0, .25, .25, 1);pointer-events:none}.jw-overlays{cursor:auto}.jw-controls{overflow:hidden}.jw-flag-small-player .jw-controls{text-align:center}.jw-text{height:1em;font-family:Arial,Helvetica,sans-serif;font-size:.75em;font-style:normal;font-weight:normal;color:#fff;text-align:center;font-variant:normal;font-stretch:normal}.jw-controlbar,.jw-skip,.jw-display-icon-container .jw-icon,.jw-nextup-container,.jw-autostart-mute,.jw-overlays .jw-plugin{pointer-events:all}.jwplayer .jw-display-icon-container,.jw-error .jw-display-icon-container{width:auto;height:auto;box-sizing:content-box}.jw-display{display:table;height:100%;padding:57px 0;position:relative;width:100%}.jw-flag-dragging .jw-display{display:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-display-container{display:table-cell;height:100%;text-align:center;vertical-align:middle}.jw-display-controls{display:inline-block}.jwplayer .jw-display-icon-container{float:left}.jw-display-icon-container{display:inline-block;padding:5.5px;margin:0 22px}.jw-display-icon-container .jw-icon{height:75px;width:75px;cursor:pointer;display:flex;justify-content:center;align-items:center}.jw-display-icon-container .jw-icon .jw-svg-icon{height:33px;width:33px;padding:0;position:relative}.jw-display-icon-container .jw-icon .jw-svg-icon-rewind{padding:.2em .05em}.jw-breakpoint--1 .jw-nextup-container{display:none}.jw-breakpoint-0 .jw-display-icon-next,.jw-breakpoint--1 .jw-display-icon-next,.jw-breakpoint-0 .jw-display-icon-rewind,.jw-breakpoint--1 .jw-display-icon-rewind{display:none}.jw-breakpoint-0 .jw-display .jw-icon,.jw-breakpoint--1 .jw-display .jw-icon,.jw-breakpoint-0 .jw-display .jw-svg-icon,.jw-breakpoint--1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-0 .jw-display .jw-icon:before,.jw-breakpoint--1 .jw-display .jw-icon:before,.jw-breakpoint-0 .jw-display .jw-svg-icon:before,.jw-breakpoint--1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon,.jw-breakpoint-1 .jw-display .jw-svg-icon{width:44px;height:44px;line-height:44px}.jw-breakpoint-1 .jw-display .jw-icon:before,.jw-breakpoint-1 .jw-display .jw-svg-icon:before{width:22px;height:22px}.jw-breakpoint-1 .jw-display .jw-icon.jw-icon-rewind:before{width:33px;height:33px}.jw-breakpoint-2 .jw-display .jw-icon,.jw-breakpoint-3 .jw-display .jw-icon,.jw-breakpoint-2 .jw-display .jw-svg-icon,.jw-breakpoint-3 .jw-display .jw-svg-icon{width:77px;height:77px;line-height:77px}.jw-breakpoint-2 .jw-display .jw-icon:before,.jw-breakpoint-3 .jw-display .jw-icon:before,.jw-breakpoint-2 .jw-display .jw-svg-icon:before,.jw-breakpoint-3 .jw-display .jw-svg-icon:before{width:38.5px;height:38.5px}.jw-breakpoint-4 .jw-display .jw-icon,.jw-breakpoint-5 .jw-display .jw-icon,.jw-breakpoint-6 .jw-display .jw-icon,.jw-breakpoint-7 .jw-display .jw-icon,.jw-breakpoint-4 .jw-display .jw-svg-icon,.jw-breakpoint-5 .jw-display .jw-svg-icon,.jw-breakpoint-6 .jw-display .jw-svg-icon,.jw-breakpoint-7 .jw-display .jw-svg-icon{width:88px;height:88px;line-height:88px}.jw-breakpoint-4 .jw-display .jw-icon:before,.jw-breakpoint-5 .jw-display .jw-icon:before,.jw-breakpoint-6 .jw-display .jw-icon:before,.jw-breakpoint-7 .jw-display .jw-icon:before,.jw-breakpoint-4 .jw-display .jw-svg-icon:before,.jw-breakpoint-5 .jw-display .jw-svg-icon:before,.jw-breakpoint-6 .jw-display .jw-svg-icon:before,.jw-breakpoint-7 .jw-display .jw-svg-icon:before{width:44px;height:44px}.jw-controlbar{display:flex;flex-flow:row wrap;align-items:center;justify-content:center;position:absolute;left:0;bottom:0;width:100%;border:none;border-radius:0;background-size:auto;box-shadow:none;max-height:72px;transition:250ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s}.jw-breakpoint-7 .jw-controlbar{max-height:140px}.jw-breakpoint-7 .jw-controlbar .jw-button-container{padding:0 48px 20px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-tooltip{margin-bottom:-7px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-overlay{padding-bottom:40%}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text{font-size:1em}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-text.jw-text-elapsed{justify-content:flex-end}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume{height:60px;width:60px}.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-inline .jw-svg-icon,.jw-breakpoint-7 .jw-controlbar .jw-button-container .jw-icon-volume .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time{padding:0 60px;height:34px}.jw-breakpoint-7 .jw-controlbar .jw-slider-time .jw-slider-container{height:10px}.jw-controlbar .jw-button-image{background:no-repeat 50% 50%;background-size:contain;max-height:24px}.jw-controlbar .jw-spacer{flex:1 1 auto;align-self:stretch}.jw-controlbar .jw-icon.jw-button-color:hover{color:#fff}.jw-button-container{display:flex;flex-flow:row nowrap;flex:1 1 auto;align-items:center;justify-content:center;width:100%;padding:0 12px}.jw-slider-horizontal{background-color:transparent}.jw-icon-inline{position:relative}.jw-icon-inline,.jw-icon-tooltip{height:44px;width:44px;align-items:center;display:flex;justify-content:center}.jw-icon-inline:not(.jw-text),.jw-icon-tooltip,.jw-slider-horizontal{cursor:pointer}.jw-text-elapsed,.jw-text-duration{justify-content:flex-start;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.jw-icon-tooltip{position:relative}.jw-knob:hover,.jw-icon-inline:hover,.jw-icon-tooltip:hover,.jw-icon-display:hover,.jw-option:before:hover{color:#fff}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{pointer-events:none}.jw-icon-cast{display:none;margin:0;padding:0}.jw-icon-cast google-cast-launcher{background-color:transparent;border:none;padding:0;width:24px;height:24px;cursor:pointer}.jw-icon-inline.jw-icon-volume{display:none}.jwplayer .jw-text-countdown{display:none}.jw-flag-small-player .jw-display{padding-top:0;padding-bottom:0}.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-rewind,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-next,.jw-flag-small-player:not(.jw-flag-audio-player):not(.jw-flag-ads) .jw-controlbar .jw-button-container>.jw-icon-playback{display:none}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controlbar{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-ads-vpaid:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-playing:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop,.jw-flag-user-inactive.jw-state-buffering:not(.jw-flag-media-audio):not(.jw-flag-audio-player):not(.jw-flag-ads-vpaid-controls):not(.jw-flag-casting) .jw-controls-backdrop{opacity:0}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-countdown{display:flex}.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-elapsed,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint--1 .jw-text-duration,.jwplayer:not(.jw-flag-ads):not(.jw-flag-live).jw-breakpoint-0 .jw-text-duration{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-text-countdown,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-related-btn,.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-slider-volume{display:none}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-controlbar{flex-direction:column-reverse}.jwplayer.jw-breakpoint--1:not(.jw-flag-ads):not(.jw-flag-audio-player) .jw-button-container{height:30px}.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-volume,.jw-breakpoint--1.jw-flag-ads:not(.jw-flag-audio-player) .jw-icon-fullscreen{display:none}.jwplayer:not(.jw-breakpoint-0) .jw-text-duration:before,.jwplayer:not(.jw-breakpoint--1) .jw-text-duration:before{content:"/";padding-right:1ch;padding-left:1ch}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar{will-change:transform}.jwplayer:not(.jw-flag-user-inactive) .jw-controlbar .jw-text{-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.jw-slider-container{display:flex;align-items:center;position:relative;touch-action:none}.jw-rail,.jw-buffer,.jw-progress{position:absolute;cursor:pointer}.jw-progress{background-color:#f2f2f2}.jw-rail{background-color:rgba(255,255,255,0.3)}.jw-buffer{background-color:rgba(255,255,255,0.3)}.jw-knob{height:13px;width:13px;background-color:#fff;border-radius:50%;box-shadow:0 0 10px rgba(0,0,0,0.4);opacity:1;pointer-events:none;position:absolute;-webkit-transform:translate(-50%, -50%) scale(0);transform:translate(-50%, -50%) scale(0);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform}.jw-flag-dragging .jw-slider-time .jw-knob,.jw-icon-volume:active .jw-slider-volume .jw-knob{box-shadow:0 0 26px rgba(0,0,0,0.2),0 0 10px rgba(0,0,0,0.4),0 0 0 6px rgba(255,255,255,0.2)}.jw-slider-horizontal,.jw-slider-vertical{display:flex}.jw-slider-horizontal .jw-slider-container{height:5px;width:100%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue,.jw-slider-horizontal .jw-knob{top:50%}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress,.jw-slider-horizontal .jw-cue{-webkit-transform:translate(0, -50%);transform:translate(0, -50%)}.jw-slider-horizontal .jw-rail,.jw-slider-horizontal .jw-buffer,.jw-slider-horizontal .jw-progress{height:5px}.jw-slider-horizontal .jw-rail{width:100%}.jw-slider-vertical{align-items:center;flex-direction:column}.jw-slider-vertical .jw-slider-container{height:88px;width:5px}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress,.jw-slider-vertical .jw-knob{left:50%}.jw-slider-vertical .jw-rail,.jw-slider-vertical .jw-buffer,.jw-slider-vertical .jw-progress{height:100%;width:5px;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out;bottom:0}.jw-slider-vertical .jw-knob{-webkit-transform:translate(-50%, 50%);transform:translate(-50%, 50%)}.jw-slider-time.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-slider-time,.jw-flag-audio-player .jw-slider-volume{height:17px;width:100%;align-items:center;background:transparent none;padding:0 12px}.jw-slider-time .jw-cue{background-color:rgba(33,33,33,0.8);cursor:pointer;position:absolute;width:6px}.jw-slider-time,.jw-horizontal-volume-container{z-index:1;outline:none}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail,.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer,.jw-slider-time .jw-progress,.jw-horizontal-volume-container .jw-progress,.jw-slider-time .jw-cue,.jw-horizontal-volume-container .jw-cue{-webkit-backface-visibility:hidden;backface-visibility:hidden;height:100%;-webkit-transform:translate(0, -50%) scale(1, .6);transform:translate(0, -50%) scale(1, .6);transition:-webkit-transform 150ms ease-in-out;transition:transform 150ms ease-in-out;transition:transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out}.jw-slider-time:hover .jw-rail,.jw-horizontal-volume-container:hover .jw-rail,.jw-slider-time:focus .jw-rail,.jw-horizontal-volume-container:focus .jw-rail,.jw-flag-dragging .jw-slider-time .jw-rail,.jw-flag-dragging .jw-horizontal-volume-container .jw-rail,.jw-flag-touch .jw-slider-time .jw-rail,.jw-flag-touch .jw-horizontal-volume-container .jw-rail,.jw-slider-time:hover .jw-buffer,.jw-horizontal-volume-container:hover .jw-buffer,.jw-slider-time:focus .jw-buffer,.jw-horizontal-volume-container:focus .jw-buffer,.jw-flag-dragging .jw-slider-time .jw-buffer,.jw-flag-dragging .jw-horizontal-volume-container .jw-buffer,.jw-flag-touch .jw-slider-time .jw-buffer,.jw-flag-touch .jw-horizontal-volume-container .jw-buffer,.jw-slider-time:hover .jw-progress,.jw-horizontal-volume-container:hover .jw-progress,.jw-slider-time:focus .jw-progress,.jw-horizontal-volume-container:focus .jw-progress,.jw-flag-dragging .jw-slider-time .jw-progress,.jw-flag-dragging .jw-horizontal-volume-container .jw-progress,.jw-flag-touch .jw-slider-time .jw-progress,.jw-flag-touch .jw-horizontal-volume-container .jw-progress,.jw-slider-time:hover .jw-cue,.jw-horizontal-volume-container:hover .jw-cue,.jw-slider-time:focus .jw-cue,.jw-horizontal-volume-container:focus .jw-cue,.jw-flag-dragging .jw-slider-time .jw-cue,.jw-flag-dragging .jw-horizontal-volume-container .jw-cue,.jw-flag-touch .jw-slider-time .jw-cue,.jw-flag-touch .jw-horizontal-volume-container .jw-cue{-webkit-transform:translate(0, -50%) scale(1, 1);transform:translate(0, -50%) scale(1, 1)}.jw-slider-time:hover .jw-knob,.jw-horizontal-volume-container:hover .jw-knob,.jw-slider-time:focus .jw-knob,.jw-horizontal-volume-container:focus .jw-knob{-webkit-transform:translate(-50%, -50%) scale(1);transform:translate(-50%, -50%) scale(1)}.jw-slider-time .jw-rail,.jw-horizontal-volume-container .jw-rail{background-color:rgba(255,255,255,0.2)}.jw-slider-time .jw-buffer,.jw-horizontal-volume-container .jw-buffer{background-color:rgba(255,255,255,0.4)}.jw-flag-touch .jw-slider-time::before,.jw-flag-touch .jw-horizontal-volume-container::before{height:44px;width:100%;content:"";position:absolute;display:block;bottom:calc(100% - 17px);left:0}.jw-slider-time.jw-tab-focus:focus .jw-rail,.jw-horizontal-volume-container.jw-tab-focus:focus .jw-rail{outline:solid 2px #4d90fe}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time{height:17px;padding:0}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-slider-container{height:10px}.jw-breakpoint--1:not(.jw-flag-audio-player) .jw-slider-time .jw-knob{border-radius:0;border:1px solid rgba(0,0,0,0.75);height:12px;width:10px}.jw-modal{width:284px}.jw-breakpoint-7 .jw-modal,.jw-breakpoint-6 .jw-modal,.jw-breakpoint-5 .jw-modal{height:232px}.jw-breakpoint-4 .jw-modal,.jw-breakpoint-3 .jw-modal{height:192px}.jw-breakpoint-2 .jw-modal,.jw-flag-small-player .jw-modal{bottom:0;right:0;height:100%;width:100%;max-height:none;max-width:none;z-index:2}.jwplayer .jw-rightclick{display:none;position:absolute;white-space:nowrap}.jwplayer .jw-rightclick.jw-open{display:block}.jwplayer .jw-rightclick .jw-rightclick-list{border-radius:1px;list-style:none;margin:0;padding:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item{background-color:rgba(0,0,0,0.8);border-bottom:1px solid #444;margin:0}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo{color:#fff;display:inline-flex;padding:0 10px 0 0;vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-logo .jw-svg-icon{height:20px;width:20px}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item .jw-rightclick-link{border:none;color:#fff;display:block;font-size:11px;line-height:1em;padding:15px 23px;text-align:start;text-decoration:none;width:100%}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:last-child{border-bottom:none}.jwplayer .jw-rightclick .jw-rightclick-list .jw-rightclick-item:hover{cursor:pointer}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured{vertical-align:middle}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link{color:#fff}.jwplayer .jw-rightclick .jw-rightclick-list .jw-featured .jw-rightclick-link span{color:#fff}.jwplayer .jw-rightclick .jw-info-overlay-item,.jwplayer .jw-rightclick .jw-share-item,.jwplayer .jw-rightclick .jw-shortcuts-item{border:none;background-color:transparent;outline:none;cursor:pointer}.jw-icon-tooltip.jw-open .jw-overlay{opacity:1;pointer-events:auto;transition-delay:0s}.jw-icon-tooltip.jw-open .jw-overlay:focus{outline:none}.jw-icon-tooltip.jw-open .jw-overlay:focus.jw-tab-focus{outline:solid 2px #4d90fe}.jw-slider-time .jw-overlay:before{height:1em;top:auto}.jw-slider-time .jw-icon-tooltip.jw-open .jw-overlay{pointer-events:none}.jw-volume-tip{padding:13px 0 26px}.jw-time-tip,.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{height:auto;width:100%;box-shadow:0 0 10px rgba(0,0,0,0.4);color:#fff;display:block;margin:0 0 14px;pointer-events:none;position:relative;z-index:0}.jw-time-tip::after,.jw-controlbar .jw-tooltip::after,.jw-settings-menu .jw-tooltip::after{top:100%;position:absolute;left:50%;height:14px;width:14px;border-radius:1px;background-color:currentColor;-webkit-transform-origin:75% 50%;transform-origin:75% 50%;-webkit-transform:translate(-50%, -50%) rotate(45deg);transform:translate(-50%, -50%) rotate(45deg);z-index:-1}.jw-time-tip .jw-text,.jw-controlbar .jw-tooltip .jw-text,.jw-settings-menu .jw-tooltip .jw-text{background-color:#fff;border-radius:1px;color:#000;font-size:10px;height:auto;line-height:1;padding:7px 10px;display:inline-block;min-width:100%;vertical-align:middle}.jw-controlbar .jw-overlay{position:absolute;bottom:100%;left:50%;margin:0;min-height:44px;min-width:44px;opacity:0;pointer-events:none;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility;transition-delay:0s, 150ms;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);width:100%;z-index:1}.jw-controlbar .jw-overlay .jw-contents{position:relative}.jw-controlbar .jw-option{position:relative;white-space:nowrap;cursor:pointer;list-style:none;height:1.5em;font-family:inherit;line-height:1.5em;padding:0 .5em;font-size:.8em;margin:0}.jw-controlbar .jw-option::before{padding-right:.125em}.jw-controlbar .jw-tooltip,.jw-settings-menu .jw-tooltip{position:absolute;bottom:100%;left:50%;opacity:0;-webkit-transform:translate(-50%, 0);transform:translate(-50%, 0);transition:100ms 0s cubic-bezier(0, .25, .25, 1);transition-property:opacity, visibility, -webkit-transform;transition-property:opacity, transform, visibility;transition-property:opacity, transform, visibility, -webkit-transform;visibility:hidden;white-space:nowrap;width:auto;z-index:1}.jw-controlbar .jw-tooltip.jw-open,.jw-settings-menu .jw-tooltip.jw-open{opacity:1;-webkit-transform:translate(-50%, -10px);transform:translate(-50%, -10px);transition-duration:150ms;transition-delay:500ms,0s,500ms;visibility:visible}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen{left:auto;right:0;-webkit-transform:translate(0, 0);transform:translate(0, 0)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen.jw-open,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen.jw-open{-webkit-transform:translate(0, -10px);transform:translate(0, -10px)}.jw-controlbar .jw-tooltip.jw-tooltip-fullscreen::after,.jw-settings-menu .jw-tooltip.jw-tooltip-fullscreen::after{left:auto;right:9px}.jw-tooltip-time{height:auto;width:0;bottom:100%;line-height:normal;padding:0;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.jw-tooltip-time .jw-overlay{bottom:0;min-height:0;width:auto}.jw-tooltip{bottom:57px;display:none;position:absolute}.jw-tooltip .jw-text{height:100%;white-space:nowrap;text-overflow:ellipsis;direction:unset;max-width:246px;overflow:hidden}.jw-flag-audio-player .jw-tooltip{display:none}.jw-flag-small-player .jw-time-thumb{display:none}.jwplayer .jw-shortcuts-tooltip{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column;z-index:1}.jwplayer .jw-shortcuts-tooltip.jw-open{display:flex}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-close{flex:0 0 auto;margin:5px 5px 5px auto}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container{display:flex;flex:1 1 auto;flex-flow:column;font-size:12px;margin:0 20px 20px;overflow-y:auto;padding:5px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar{background-color:transparent;width:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-title{font-weight:bold}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list{display:flex;max-width:340px;margin:0 10px}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-tooltip-descriptions{width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row{display:flex;align-items:center;justify-content:space-between;margin:10px 0;width:100%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-description{margin-right:10px;max-width:70%}.jwplayer .jw-shortcuts-tooltip .jw-shortcuts-container .jw-shortcuts-tooltip-list .jw-shortcuts-row .jw-shortcuts-key{background:#fefefe;color:#333;overflow:hidden;padding:7px 10px;text-overflow:ellipsis;white-space:nowrap}.jw-skip{color:rgba(255,255,255,0.8);cursor:default;position:absolute;display:flex;right:.75em;bottom:56px;padding:.5em;border:1px solid #333;background-color:#000;align-items:center;height:2em}.jw-skip.jw-tab-focus:focus{outline:solid 2px #4d90fe}.jw-skip.jw-skippable{cursor:pointer;padding:.25em .75em}.jw-skip.jw-skippable:hover{cursor:pointer;color:#fff}.jw-skip.jw-skippable .jw-skip-icon{display:inline;height:24px;width:24px;margin:0}.jw-breakpoint-7 .jw-skip{padding:1.35em 1em;bottom:130px}.jw-breakpoint-7 .jw-skip .jw-text{font-size:1em;font-weight:normal}.jw-breakpoint-7 .jw-skip .jw-icon-inline{height:30px;width:30px}.jw-breakpoint-7 .jw-skip .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-skip .jw-skip-icon{display:none;margin-left:-0.75em;padding:0 .5em;pointer-events:none}.jw-skip .jw-skip-icon .jw-svg-icon-next{display:block;padding:0}.jw-skip .jw-text,.jw-skip .jw-skip-icon{vertical-align:middle;font-size:.7em}.jw-skip .jw-text{font-weight:bold}.jw-cast{background-size:cover;display:none;height:100%;position:relative;width:100%}.jw-cast-container{background:linear-gradient(180deg, rgba(25,25,25,0.75), rgba(25,25,25,0.25), rgba(25,25,25,0));left:0;padding:20px 20px 80px;position:absolute;top:0;width:100%}.jw-cast-text{color:#fff;font-size:1.6em}.jw-breakpoint--1 .jw-cast-text,.jw-breakpoint-0 .jw-cast-text{font-size:1.15em}.jw-breakpoint-1 .jw-cast-text,.jw-breakpoint-2 .jw-cast-text,.jw-breakpoint-3 .jw-cast-text{font-size:1.3em}.jw-nextup-container{position:absolute;bottom:66px;left:0;background-color:transparent;cursor:pointer;margin:0 auto;padding:12px;pointer-events:none;right:0;text-align:right;visibility:hidden;width:100%}.jw-settings-open .jw-nextup-container,.jw-info-open .jw-nextup-container{display:none}.jw-breakpoint-7 .jw-nextup-container{padding:60px}.jw-flag-small-player .jw-nextup-container{padding:0 12px 0 0}.jw-flag-small-player .jw-nextup-container .jw-nextup-title,.jw-flag-small-player .jw-nextup-container .jw-nextup-duration,.jw-flag-small-player .jw-nextup-container .jw-nextup-close{display:none}.jw-flag-small-player .jw-nextup-container .jw-nextup-tooltip{height:30px}.jw-flag-small-player .jw-nextup-container .jw-nextup-header{font-size:12px}.jw-flag-small-player .jw-nextup-container .jw-nextup-body{justify-content:center;align-items:center;padding:.75em .3em}.jw-flag-small-player .jw-nextup-container .jw-nextup-thumbnail{width:50%}.jw-flag-small-player .jw-nextup-container .jw-nextup{max-width:65px}.jw-flag-small-player .jw-nextup-container .jw-nextup.jw-nextup-thumbnail-visible{max-width:120px}.jw-nextup{background:#333;border-radius:0;box-shadow:0 0 10px rgba(0,0,0,0.5);color:rgba(255,255,255,0.8);display:inline-block;max-width:280px;overflow:hidden;opacity:0;position:relative;width:64%;pointer-events:all;-webkit-transform:translate(0, -5px);transform:translate(0, -5px);transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:opacity, -webkit-transform;transition-property:opacity, transform;transition-property:opacity, transform, -webkit-transform;transition-delay:0s}.jw-nextup:hover .jw-nextup-tooltip{color:#fff}.jw-nextup.jw-nextup-thumbnail-visible{max-width:400px}.jw-nextup.jw-nextup-thumbnail-visible .jw-nextup-thumbnail{display:block}.jw-nextup-container-visible{visibility:visible}.jw-nextup-container-visible .jw-nextup{opacity:1;-webkit-transform:translate(0, 0);transform:translate(0, 0);transition-delay:0s, 0s, 150ms}.jw-nextup-tooltip{display:flex;height:80px}.jw-nextup-thumbnail{width:120px;background-position:center;background-size:cover;flex:0 0 auto;display:none}.jw-nextup-body{flex:1 1 auto;overflow:hidden;padding:.75em .875em;display:flex;flex-flow:column wrap;justify-content:space-between}.jw-nextup-header,.jw-nextup-title{font-size:14px;line-height:1.35}.jw-nextup-header{font-weight:bold}.jw-nextup-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.jw-nextup-duration{align-self:flex-end;text-align:right;font-size:12px}.jw-nextup-close{height:24px;width:24px;border:none;color:rgba(255,255,255,0.8);cursor:pointer;margin:6px;visibility:hidden}.jw-nextup-close:hover{color:#fff}.jw-nextup-sticky .jw-nextup-close{visibility:visible}.jw-autostart-mute{position:absolute;bottom:0;right:12px;height:44px;width:44px;background-color:rgba(33,33,33,0.4);padding:5px 4px 5px 6px;display:none}.jwplayer.jw-flag-autostart:not(.jw-flag-media-audio) .jw-nextup{display:none}.jw-settings-menu{position:absolute;bottom:57px;right:12px;align-items:flex-start;background-color:#333;display:none;flex-flow:column nowrap;max-width:284px;pointer-events:auto}.jw-settings-open .jw-settings-menu{display:flex}.jw-breakpoint-7 .jw-settings-menu{bottom:130px;right:60px;max-height:none;max-width:none;height:35%;width:25%}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline{height:60px;width:60px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-svg-icon{height:30px;width:30px}.jw-breakpoint-7 .jw-settings-menu .jw-settings-topbar:not(.jw-nested-menu-open) .jw-icon-inline .jw-tooltip .jw-text{font-size:1em}.jw-breakpoint-7 .jw-settings-menu .jw-settings-back{min-width:60px}.jw-breakpoint-6 .jw-settings-menu,.jw-breakpoint-5 .jw-settings-menu{height:232px;width:284px;max-height:232px}.jw-breakpoint-4 .jw-settings-menu,.jw-breakpoint-3 .jw-settings-menu{height:192px;width:284px;max-height:192px}.jw-breakpoint-2 .jw-settings-menu{height:179px;width:284px;max-height:179px}.jw-flag-small-player .jw-settings-menu{max-width:none}.jw-settings-menu .jw-icon.jw-button-color::after{height:100%;width:24px;box-shadow:inset 0 -3px 0 -1px currentColor;margin:auto;opacity:0;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-settings-menu .jw-icon.jw-button-color[aria-checked="true"]::after{opacity:1}.jw-settings-menu .jw-settings-reset{text-decoration:underline}.jw-settings-topbar{align-items:center;background-color:rgba(0,0,0,0.4);display:flex;flex:0 0 auto;padding:3px 5px 0;width:100%}.jw-settings-topbar.jw-nested-menu-open{padding:0}.jw-settings-topbar.jw-nested-menu-open .jw-icon:not(.jw-settings-close):not(.jw-settings-back){display:none}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-close{width:20px}.jw-settings-topbar.jw-nested-menu-open .jw-svg-icon-arrow-left{height:12px}.jw-settings-topbar.jw-nested-menu-open .jw-settings-topbar-text{display:block;outline:none}.jw-settings-topbar .jw-settings-back{min-width:44px}.jw-settings-topbar .jw-settings-topbar-buttons{display:inherit;width:100%;height:100%}.jw-settings-topbar .jw-settings-topbar-text{display:none;color:#fff;font-size:13px;width:100%}.jw-settings-topbar .jw-settings-close{margin-left:auto}.jw-settings-submenu{display:none;flex:1 1 auto;overflow-y:auto;padding:8px 20px 0 5px}.jw-settings-submenu::-webkit-scrollbar{background-color:transparent;width:6px}.jw-settings-submenu::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-settings-submenu.jw-settings-submenu-active{display:block}.jw-settings-submenu .jw-submenu-topbar{box-shadow:0 2px 9px 0 #1d1d1d;background-color:#2f2d2d;margin:-8px -20px 0 -5px}.jw-settings-submenu .jw-submenu-topbar .jw-settings-content-item{cursor:pointer;text-align:right;padding-right:15px;text-decoration:underline}.jw-settings-submenu .jw-settings-value-wrapper{float:right;display:flex;align-items:center}.jw-settings-submenu .jw-settings-value-wrapper .jw-settings-content-item-arrow{display:flex}.jw-settings-submenu .jw-settings-value-wrapper .jw-svg-icon-arrow-right{width:8px;margin-left:5px;height:12px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item{font-size:1em;padding:11px 15px 11px 30px}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-settings-item-active::before{justify-content:flex-end}.jw-breakpoint-7 .jw-settings-submenu .jw-settings-content-item .jw-auto-label{font-size:.85em;padding-left:10px}.jw-flag-touch .jw-settings-submenu{overflow-y:scroll;-webkit-overflow-scrolling:touch}.jw-auto-label{font-size:10px;font-weight:initial;opacity:.75;padding-left:5px}.jw-settings-content-item{position:relative;color:rgba(255,255,255,0.8);cursor:pointer;font-size:12px;line-height:1;padding:7px 0 7px 15px;width:100%;text-align:left;outline:none}.jw-settings-content-item:hover{color:#fff}.jw-settings-content-item:focus{font-weight:bold}.jw-flag-small-player .jw-settings-content-item{line-height:1.75}.jw-settings-content-item.jw-tab-focus:focus{border:solid 2px #4d90fe}.jw-settings-item-active{font-weight:bold;position:relative}.jw-settings-item-active::before{height:100%;width:1em;align-items:center;content:"\\2022";display:inline-flex;justify-content:center}.jw-breakpoint-2 .jw-settings-open .jw-display-container,.jw-flag-small-player .jw-settings-open .jw-display-container,.jw-flag-touch .jw-settings-open .jw-display-container{display:none}.jw-breakpoint-2 .jw-settings-open.jw-controls,.jw-flag-small-player .jw-settings-open.jw-controls,.jw-flag-touch .jw-settings-open.jw-controls{z-index:1}.jw-flag-small-player .jw-settings-open .jw-controlbar{display:none}.jw-settings-open .jw-icon-settings::after{opacity:1}.jw-settings-open .jw-tooltip-settings{display:none}.jw-sharing-link{cursor:pointer}.jw-shortcuts-container .jw-switch{position:relative;display:inline-block;transition:ease-out .15s;transition-property:opacity, background;border-radius:18px;width:80px;height:20px;padding:10px;background:rgba(80,80,80,0.8);cursor:pointer;font-size:inherit;vertical-align:middle}.jw-shortcuts-container .jw-switch.jw-tab-focus{outline:solid 2px #4d90fe}.jw-shortcuts-container .jw-switch .jw-switch-knob{position:absolute;top:2px;left:1px;transition:ease-out .15s;box-shadow:0 0 10px rgba(0,0,0,0.4);border-radius:13px;width:15px;height:15px;background:#fefefe}.jw-shortcuts-container .jw-switch:before,.jw-shortcuts-container .jw-switch:after{position:absolute;top:3px;transition:inherit;color:#fefefe}.jw-shortcuts-container .jw-switch:before{content:attr(data-jw-switch-disabled);right:8px}.jw-shortcuts-container .jw-switch:after{content:attr(data-jw-switch-enabled);left:8px;opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]{background:#475470}.jw-shortcuts-container .jw-switch[aria-checked="true"]:before{opacity:0}.jw-shortcuts-container .jw-switch[aria-checked="true"]:after{opacity:1}.jw-shortcuts-container .jw-switch[aria-checked="true"] .jw-switch-knob{left:60px}.jw-idle-icon-text{display:none;line-height:1;position:absolute;text-align:center;text-indent:.35em;top:100%;white-space:nowrap;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.jw-idle-label{border-radius:50%;color:#fff;-webkit-filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));filter:drop-shadow(1px 1px 5px rgba(12,26,71,0.25));font:normal 16px/1 Arial,Helvetica,sans-serif;position:relative;transition:background-color 150ms cubic-bezier(0, .25, .25, 1);transition-property:background-color,-webkit-filter;transition-property:background-color,filter;transition-property:background-color,filter,-webkit-filter;-webkit-font-smoothing:antialiased}.jw-state-idle .jw-icon-display.jw-idle-label .jw-idle-icon-text{display:block}.jw-state-idle .jw-icon-display.jw-idle-label .jw-svg-icon-play{-webkit-transform:scale(.7, .7);transform:scale(.7, .7)}.jw-breakpoint-0.jw-state-idle .jw-icon-display.jw-idle-label,.jw-breakpoint--1.jw-state-idle .jw-icon-display.jw-idle-label{font-size:12px}.jw-info-overlay{top:50%;position:absolute;left:50%;background:#333;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);display:none;color:#fff;pointer-events:all;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;overflow:hidden;flex-direction:column}.jw-info-overlay .jw-info-close{flex:0 0 auto;margin:5px 5px 5px auto}.jw-info-open .jw-info-overlay{display:flex}.jw-info-container{display:flex;flex:1 1 auto;flex-flow:column;margin:0 20px 20px;overflow-y:auto;padding:5px}.jw-info-container [class*="jw-info"]:not(:first-of-type){color:rgba(255,255,255,0.8);padding-top:10px;font-size:12px}.jw-info-container .jw-info-description{margin-bottom:30px;text-align:start}.jw-info-container .jw-info-description:empty{display:none}.jw-info-container .jw-info-duration{text-align:start}.jw-info-container .jw-info-title{text-align:start;font-size:12px;font-weight:bold}.jw-info-container::-webkit-scrollbar{background-color:transparent;width:6px}.jw-info-container::-webkit-scrollbar-thumb{background-color:#fff;border:1px solid #333;border-radius:6px}.jw-info-clientid{align-self:flex-end;font-size:12px;color:rgba(255,255,255,0.8);margin:0 20px 20px 44px;text-align:right}.jw-flag-touch .jw-info-open .jw-display-container{display:none}@supports ((-webkit-filter: drop-shadow(0 0 3px #000)) or (filter: drop-shadow(0 0 3px #000))){.jwplayer.jw-ab-drop-shadow .jw-controls .jw-svg-icon,.jwplayer.jw-ab-drop-shadow .jw-controls .jw-icon.jw-text,.jwplayer.jw-ab-drop-shadow .jw-slider-container .jw-rail,.jwplayer.jw-ab-drop-shadow .jw-title{text-shadow:none;box-shadow:none;-webkit-filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3));filter:drop-shadow(0 2px 3px rgba(0,0,0,0.3))}.jwplayer.jw-ab-drop-shadow .jw-button-color{opacity:.8;transition-property:color, opacity}.jwplayer.jw-ab-drop-shadow .jw-button-color:not(:hover){color:#fff;opacity:.8}.jwplayer.jw-ab-drop-shadow .jw-button-color:hover{opacity:1}.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0), hsla(0, 0%, 0%, 0.00787) 10.79%, hsla(0, 0%, 0%, 0.02963) 21.99%, hsla(0, 0%, 0%, 0.0625) 33.34%, hsla(0, 0%, 0%, 0.1037) 44.59%, hsla(0, 0%, 0%, 0.15046) 55.48%, hsla(0, 0%, 0%, 0.2) 65.75%, hsla(0, 0%, 0%, 0.24954) 75.14%, hsla(0, 0%, 0%, 0.2963) 83.41%, hsla(0, 0%, 0%, 0.3375) 90.28%, hsla(0, 0%, 0%, 0.37037) 95.51%, hsla(0, 0%, 0%, 0.39213) 98.83%, hsla(0, 0%, 0%, 0.4));mix-blend-mode:multiply;transition-property:opacity}.jw-state-idle.jwplayer.jw-ab-drop-shadow .jw-controls-backdrop{background-image:linear-gradient(to bottom, hsla(0, 0%, 0%, 0.2), hsla(0, 0%, 0%, 0.19606) 1.17%, hsla(0, 0%, 0%, 0.18519) 4.49%, hsla(0, 0%, 0%, 0.16875) 9.72%, hsla(0, 0%, 0%, 0.14815) 16.59%, hsla(0, 0%, 0%, 0.12477) 24.86%, hsla(0, 0%, 0%, 0.1) 34.25%, hsla(0, 0%, 0%, 0.07523) 44.52%, hsla(0, 0%, 0%, 0.05185) 55.41%, hsla(0, 0%, 0%, 0.03125) 66.66%, hsla(0, 0%, 0%, 0.01481) 78.01%, hsla(0, 0%, 0%, 0.00394) 89.21%, hsla(0, 0%, 0%, 0));background-size:100% 7rem;background-position:50% 0}.jwplayer.jw-ab-drop-shadow.jw-state-idle .jw-controls{background-color:transparent}}.jw-video-thumbnail-container{position:relative;overflow:hidden}.jw-video-thumbnail-container:not(.jw-related-shelf-item-image){height:100%;width:100%}.jw-video-thumbnail-container.jw-video-thumbnail-generated{position:absolute;top:0;left:0}.jw-video-thumbnail-container:hover,.jw-related-item-content:hover .jw-video-thumbnail-container,.jw-related-shelf-item:hover .jw-video-thumbnail-container{cursor:pointer}.jw-video-thumbnail-container:hover .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-item-content:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed),.jw-related-shelf-item:hover .jw-video-thumbnail-container .jw-video-thumbnail:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail{position:absolute;top:50%;left:50%;bottom:unset;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);width:100%;height:auto;min-width:100%;min-height:100%;opacity:0;transition:opacity .3s ease;object-fit:cover;background:#000}.jw-related-item-next-up .jw-video-thumbnail-container .jw-video-thumbnail{height:100%;width:auto}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-visible:not(.jw-video-thumbnail-completed){opacity:1}.jw-video-thumbnail-container .jw-video-thumbnail.jw-video-thumbnail-completed{opacity:0}.jw-video-thumbnail-container .jw-video-thumbnail~.jw-svg-icon-play{display:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-shelf-item-aspect{pointer-events:none}.jw-video-thumbnail-container .jw-video-thumbnail+.jw-related-item-poster-content{pointer-events:none}.jw-state-idle:not(.jw-flag-cast-available) .jw-display{padding:0}.jw-state-idle .jw-controls{background:rgba(0,0,0,0.4)}.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-slider-time,.jw-state-idle.jw-flag-cast-available:not(.jw-flag-audio-player) .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay),.jw-state-idle.jw-flag-cardboard-available .jw-controlbar .jw-icon:not(.jw-icon-cardboard):not(.jw-icon-cast):not(.jw-icon-airplay){display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon:focus{border:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-icon .jw-svg-icon-buffer{-webkit-animation:jw-spin 2s linear infinite;animation:jw-spin 2s linear infinite;display:block}@-webkit-keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes jw-spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.jwplayer.jw-state-buffering .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-pause{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-play{display:none}.jwplayer.jw-state-playing .jw-display .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-playing .jw-icon-playback .jw-svg-icon-pause{display:block}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-controls-backdrop{opacity:0}.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio) .jw-logo-bottom-left,.jwplayer.jw-state-playing.jw-flag-user-inactive:not(.jw-flag-audio-player):not(.jw-flag-casting):not(.jw-flag-media-audio):not(.jw-flag-autostart) .jw-logo-bottom-right{bottom:0}.jwplayer .jw-icon-playback .jw-svg-icon-stop{display:none}.jwplayer.jw-state-paused .jw-svg-icon-pause,.jwplayer.jw-state-idle .jw-svg-icon-pause,.jwplayer.jw-state-error .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-svg-icon-pause{display:none}.jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-complete .jw-icon-display .jw-svg-icon-play,.jwplayer.jw-state-buffering .jw-icon-display .jw-svg-icon-play{display:none}.jwplayer:not(.jw-state-buffering) .jw-svg-icon-buffer{display:none}.jwplayer:not(.jw-state-complete) .jw-svg-icon-replay{display:none}.jwplayer:not(.jw-state-error) .jw-svg-icon-error{display:none}.jwplayer.jw-state-complete .jw-display .jw-icon-display .jw-svg-icon-replay{display:block}.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-state-complete .jw-controls{background:rgba(0,0,0,0.4);height:100%}.jw-state-idle .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-state-paused .jw-icon-display .jw-svg-icon-pause,.jwplayer.jw-state-complete .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-state-idle .jw-display-icon-rewind,.jwplayer.jw-state-buffering .jw-display-icon-rewind,.jwplayer.jw-state-complete .jw-display-icon-rewind,body .jw-error .jw-display-icon-rewind,body .jwplayer.jw-state-error .jw-display-icon-rewind,.jw-state-idle .jw-display-icon-next,.jwplayer.jw-state-buffering .jw-display-icon-next,.jwplayer.jw-state-complete .jw-display-icon-next,body .jw-error .jw-display-icon-next,body .jwplayer.jw-state-error .jw-display-icon-next{display:none}body .jw-error .jw-icon-display,body .jwplayer.jw-state-error .jw-icon-display{cursor:default}body .jw-error .jw-icon-display .jw-svg-icon-error,body .jwplayer.jw-state-error .jw-icon-display .jw-svg-icon-error{display:block}body .jw-error .jw-icon-container{position:absolute;width:100%;height:100%;top:0;left:0;bottom:0;right:0}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-preview{display:none}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title{padding-top:4px}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-primary{width:auto;display:inline-block;padding-right:.5ch}body .jwplayer.jw-state-error.jw-flag-audio-player .jw-title-secondary{width:auto;display:inline-block;padding-left:0}body .jwplayer.jw-state-error .jw-controlbar,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-controlbar{display:none}body .jwplayer.jw-state-error .jw-settings-menu,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-settings-menu{height:100%;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}body .jwplayer.jw-state-error .jw-display,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-display{padding:0}body .jwplayer.jw-state-error .jw-logo-bottom-left,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-left,body .jwplayer.jw-state-error .jw-logo-bottom-right,.jwplayer.jw-state-idle:not(.jw-flag-audio-player):not(.jw-flag-cast-available):not(.jw-flag-cardboard-available) .jw-logo-bottom-right{bottom:0}.jwplayer.jw-state-playing.jw-flag-user-inactive .jw-display{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-state-playing:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display,.jwplayer.jw-state-paused:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting):not(.jw-flag-play-rejected) .jw-display{display:none}.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-rewind,.jwplayer.jw-state-paused.jw-flag-play-rejected:not(.jw-flag-touch):not(.jw-flag-small-player):not(.jw-flag-casting) .jw-display-icon-next{display:none}.jwplayer.jw-state-buffering .jw-display-icon-display .jw-text,.jwplayer.jw-state-complete .jw-display .jw-text{display:none}.jwplayer.jw-flag-casting:not(.jw-flag-audio-player) .jw-cast{display:block}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-display-icon-container{display:none}.jwplayer.jw-flag-casting .jw-icon-hd,.jwplayer.jw-flag-casting .jw-captions,.jwplayer.jw-flag-casting .jw-icon-fullscreen,.jwplayer.jw-flag-casting .jw-icon-audio-tracks{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-volume{display:none}.jwplayer.jw-flag-casting.jw-flag-airplay-casting .jw-icon-airplay{color:#fff}.jw-state-playing.jw-flag-casting:not(.jw-flag-audio-player) .jw-display,.jw-state-paused.jw-flag-casting:not(.jw-flag-audio-player) .jw-display{display:table}.jwplayer.jw-flag-cast-available .jw-icon-cast,.jwplayer.jw-flag-cast-available .jw-icon-airplay{display:flex}.jwplayer.jw-flag-cardboard-available .jw-icon-cardboard{display:flex}.jwplayer.jw-flag-live .jw-display-icon-rewind{visibility:hidden}.jwplayer.jw-flag-live .jw-controlbar .jw-text-elapsed,.jwplayer.jw-flag-live .jw-controlbar .jw-text-duration,.jwplayer.jw-flag-live .jw-controlbar .jw-text-countdown,.jwplayer.jw-flag-live .jw-controlbar .jw-slider-time{display:none}.jwplayer.jw-flag-live .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-live .jw-controlbar .jw-overlay:after{display:none}.jwplayer.jw-flag-live .jw-nextup-container{bottom:44px}.jwplayer.jw-flag-live .jw-text-elapsed,.jwplayer.jw-flag-live .jw-text-duration{display:none}.jwplayer.jw-flag-live .jw-text-live{cursor:default}.jwplayer.jw-flag-live .jw-text-live:hover{color:rgba(255,255,255,0.8)}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-stop,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-stop{display:block}.jwplayer.jw-flag-live.jw-state-playing .jw-icon-playback .jw-svg-icon-pause,.jwplayer.jw-flag-live.jw-state-buffering .jw-icon-playback .jw-svg-icon-pause{display:none}.jw-text-live{height:24px;width:auto;align-items:center;border-radius:1px;color:rgba(255,255,255,0.8);display:flex;font-size:12px;font-weight:bold;margin-right:10px;padding:0 1ch;text-rendering:geometricPrecision;text-transform:uppercase;transition:150ms cubic-bezier(0, .25, .25, 1);transition-property:box-shadow,color}.jw-text-live::before{height:8px;width:8px;background-color:currentColor;border-radius:50%;margin-right:6px;opacity:1;transition:opacity 150ms cubic-bezier(0, .25, .25, 1)}.jw-text-live.jw-dvr-live{box-shadow:inset 0 0 0 2px currentColor}.jw-text-live.jw-dvr-live::before{opacity:.5}.jw-text-live.jw-dvr-live:hover{color:#fff}.jwplayer.jw-flag-controls-hidden .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-controls-hidden:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-controls-hidden .jw-plugin{bottom:.5em}.jwplayer.jw-flag-controls-hidden .jw-nextup-container{bottom:0}.jw-flag-controls-hidden .jw-controlbar,.jw-flag-controls-hidden .jw-display{visibility:hidden;pointer-events:none;opacity:0;transition-delay:0s, 250ms}.jw-flag-controls-hidden .jw-controls-backdrop{opacity:0}.jw-flag-controls-hidden .jw-logo{visibility:visible}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-logo.jw-hide{visibility:hidden;pointer-events:none;opacity:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-casting) .jw-logo-top-right{top:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-plugin{bottom:.5em}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing .jw-nextup-container{bottom:0}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-controls-hidden) .jw-media{cursor:none;-webkit-cursor-visibility:auto-hide}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing.jw-flag-casting .jw-display{display:table}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-state-playing:not(.jw-flag-ads) .jw-autostart-mute{display:flex}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting .jw-nextup-container{bottom:66px}.jwplayer.jw-flag-user-inactive:not(.jw-flag-media-audio).jw-flag-casting.jw-state-idle .jw-nextup-container{display:none}.jw-flag-media-audio .jw-preview{display:block}.jwplayer.jw-flag-ads .jw-preview,.jwplayer.jw-flag-ads .jw-logo,.jwplayer.jw-flag-ads .jw-captions.jw-captions-enabled,.jwplayer.jw-flag-ads .jw-nextup-container,.jwplayer.jw-flag-ads .jw-text-duration,.jwplayer.jw-flag-ads .jw-text-elapsed{display:none}.jwplayer.jw-flag-ads video::-webkit-media-text-track-container{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-rewind,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-next,.jwplayer.jw-flag-ads.jw-flag-small-player .jw-display-icon-display{display:none}.jwplayer.jw-flag-ads.jw-flag-small-player.jw-state-buffering .jw-display-icon-display{display:inline-block}.jwplayer.jw-flag-ads .jw-controlbar{flex-wrap:wrap-reverse}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time{height:auto;padding:0;pointer-events:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-slider-container{height:5px}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-rail,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-knob,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-buffer,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-cue,.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-icon-settings{display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-slider-time .jw-progress{-webkit-transform:none;transform:none;top:auto}.jwplayer.jw-flag-ads .jw-controlbar .jw-tooltip,.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-tooltip:not(.jw-icon-volume),.jwplayer.jw-flag-ads .jw-controlbar .jw-icon-inline:not(.jw-icon-playback):not(.jw-icon-fullscreen):not(.jw-icon-volume){display:none}.jwplayer.jw-flag-ads .jw-controlbar .jw-volume-tip{padding:13px 0}.jwplayer.jw-flag-ads .jw-controlbar .jw-text-alt{display:flex}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid) .jw-controls .jw-controlbar,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart .jw-controls .jw-controlbar{display:flex;pointer-events:all;visibility:visible;opacity:1}.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-user-inactive .jw-controls-backdrop,.jwplayer.jw-flag-ads.jw-flag-ads.jw-state-playing.jw-flag-touch:not(.jw-flag-ads-vpaid).jw-flag-autostart.jw-flag-user-inactive .jw-controls-backdrop{opacity:1;background-size:100% 60px}.jwplayer.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-display-container,.jwplayer.jw-flag-ads-vpaid .jw-skip,.jwplayer.jw-flag-touch.jw-flag-ads-vpaid .jw-skip{display:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls{background:none}.jwplayer.jw-flag-ads-vpaid.jw-flag-small-player .jw-controls::after{content:none}.jwplayer.jw-flag-ads-hide-controls .jw-controls-backdrop,.jwplayer.jw-flag-ads-hide-controls .jw-controls{display:none !important}.jw-flag-overlay-open-related .jw-controls,.jw-flag-overlay-open-related .jw-title,.jw-flag-overlay-open-related .jw-logo{display:none}.jwplayer.jw-flag-rightclick-open{overflow:visible}.jwplayer.jw-flag-rightclick-open .jw-rightclick{z-index:16777215}body .jwplayer.jw-flag-flash-blocked .jw-controls,body .jwplayer.jw-flag-flash-blocked .jw-overlays,body .jwplayer.jw-flag-flash-blocked .jw-controls-backdrop,body .jwplayer.jw-flag-flash-blocked .jw-preview{display:none}body .jwplayer.jw-flag-flash-blocked .jw-error-msg{top:25%}.jw-flag-touch.jw-breakpoint-7 .jw-captions,.jw-flag-touch.jw-breakpoint-6 .jw-captions,.jw-flag-touch.jw-breakpoint-5 .jw-captions,.jw-flag-touch.jw-breakpoint-4 .jw-captions,.jw-flag-touch.jw-breakpoint-7 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-6 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-5 .jw-nextup-container,.jw-flag-touch.jw-breakpoint-4 .jw-nextup-container{bottom:4.25em}.jw-flag-touch .jw-controlbar .jw-icon-volume{display:flex}.jw-flag-touch .jw-display,.jw-flag-touch .jw-display-container,.jw-flag-touch .jw-display-controls{pointer-events:none}.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-next,.jw-flag-touch.jw-state-paused:not(.jw-breakpoint-1) .jw-display-icon-rewind,.jw-flag-touch.jw-state-playing:not(.jw-breakpoint-1) .jw-display-icon-rewind{display:none}.jw-flag-touch.jw-state-paused.jw-flag-dragging .jw-display{display:none}.jw-flag-audio-player{background-color:#000}.jw-flag-audio-player:not(.jw-flag-flash-blocked) .jw-media{visibility:hidden}.jw-flag-audio-player .jw-title{background:none}.jw-flag-audio-player object{min-height:44px}.jw-flag-audio-player:not(.jw-flag-live) .jw-spacer{display:none}.jw-flag-audio-player .jw-preview,.jw-flag-audio-player .jw-display,.jw-flag-audio-player .jw-title,.jw-flag-audio-player .jw-nextup-container{display:none}.jw-flag-audio-player .jw-controlbar{position:relative}.jw-flag-audio-player .jw-controlbar .jw-button-container{padding-right:3px;padding-left:0}.jw-flag-audio-player .jw-controlbar .jw-icon-tooltip,.jw-flag-audio-player .jw-controlbar .jw-icon-inline{display:none}.jw-flag-audio-player .jw-controlbar .jw-icon-volume,.jw-flag-audio-player .jw-controlbar .jw-icon-playback,.jw-flag-audio-player .jw-controlbar .jw-icon-next,.jw-flag-audio-player .jw-controlbar .jw-icon-rewind,.jw-flag-audio-player .jw-controlbar .jw-icon-cast,.jw-flag-audio-player .jw-controlbar .jw-text-live,.jw-flag-audio-player .jw-controlbar .jw-icon-airplay,.jw-flag-audio-player .jw-controlbar .jw-logo-button,.jw-flag-audio-player .jw-controlbar .jw-text-elapsed,.jw-flag-audio-player .jw-controlbar .jw-text-duration{display:flex;flex:0 0 auto}.jw-flag-audio-player .jw-controlbar .jw-text-duration,.jw-flag-audio-player .jw-controlbar .jw-text-countdown{padding-right:10px}.jw-flag-audio-player .jw-controlbar .jw-slider-time{flex:0 1 auto;align-items:center;display:flex;order:1}.jw-flag-audio-player .jw-controlbar .jw-icon-volume{margin-right:0;transition:margin-right 150ms cubic-bezier(0, .25, .25, 1)}.jw-flag-audio-player .jw-controlbar .jw-icon-volume .jw-overlay{display:none}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container{transition:width 300ms cubic-bezier(0, .25, .25, 1);width:0}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open{width:140px}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open .jw-slider-volume{padding-right:24px;transition:opacity 300ms;opacity:1}.jw-flag-audio-player .jw-controlbar .jw-horizontal-volume-container.jw-open~.jw-slider-time{flex:1 1 auto;width:auto;transition:opacity 300ms, width 300ms}.jw-flag-audio-player .jw-controlbar .jw-slider-volume{opacity:0}.jw-flag-audio-player .jw-controlbar .jw-slider-volume .jw-knob{-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.jw-flag-audio-player .jw-controlbar .jw-slider-volume~.jw-icon-volume{margin-right:140px}.jw-flag-audio-player.jw-breakpoint-1 .jw-horizontal-volume-container.jw-open~.jw-slider-time,.jw-flag-audio-player.jw-breakpoint-2 .jw-horizontal-volume-container.jw-open~.jw-slider-time{opacity:0}.jw-flag-audio-player.jw-flag-small-player .jw-text-elapsed,.jw-flag-audio-player.jw-flag-small-player .jw-text-duration{display:none}.jw-flag-audio-player.jw-flag-ads .jw-slider-time{display:none}.jw-hidden{display:none}',""])}]]); \ No newline at end of file diff --git a/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.js b/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.js index 0ad8f2471..cc4772f2e 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.js +++ b/ui/v2.5/public/jwplayer/jwplayer.core.controls.polyfills.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * diff --git a/ui/v2.5/public/jwplayer/jwplayer.core.js b/ui/v2.5/public/jwplayer/jwplayer.core.js index ed16e4c4b..e2b9abb1f 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.core.js +++ b/ui/v2.5/public/jwplayer/jwplayer.core.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * diff --git a/ui/v2.5/public/jwplayer/jwplayer.js b/ui/v2.5/public/jwplayer/jwplayer.js index 6b9881bb6..539f533f2 100644 --- a/ui/v2.5/public/jwplayer/jwplayer.js +++ b/ui/v2.5/public/jwplayer/jwplayer.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * @@ -92,4 +92,4 @@ COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQ The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders. */ -window.jwplayer=function(t){function e(e){for(var n,i,o=e[0],u=e[1],a=0,s=[];a2;if(null==t&&(t=[]),p&&t.reduce===p)return r&&(e=G(e,r)),i?t.reduce(e,n):t.reduce(e);if(k(t,(function(t,o,u){i?n=e.call(r,n,t,o,u):(n=t,i=!0)})),!i)throw new TypeError(S);return n},T=E,A=E,_=function(t,e,n){var r;return R(t,(function(t,i,o){if(e.call(n,t,i,o))return r=t,!0})),r},F=_,L=function(t,e,n){var r=[];return null==t?r:v&&t.filter===v?t.filter(e,n):(k(t,(function(t,i,o){e.call(n,t,i,o)&&r.push(t)})),r)},I=L,M=function(t,e,n){e||(e=kt);var r=!0;return null==t?r:g&&t.every===g?t.every(e,n):(k(t,(function(t,o,u){if(!(r=r&&e.call(n,t,o,u)))return i})),!!r)},N=M,R=function(t,e,n){e||(e=kt);var r=!1;return null==t?r:b&&t.some===b?t.some(e,n):(k(t,(function(t,o,u){if(r||(r=e.call(n,t,o,u)))return i})),!!r)},D=R,B=function(t,e){var n;return function(){return--t>0&&(n=e.apply(this,arguments)),t<=1&&(e=null),n}},q=function(t){return null==t?kt:vt(t)?t:xt(t)},z=function(t){return function(e,n,r){var i={};return n=q(n),k(e,(function(o,u){var a=n.call(r,o,u,e);t(i,a,o)})),i}},V=z((function(t,e,n){Ot(t,e)?t[e].push(n):t[e]=[n]})),Q=z((function(t,e,n){t[e]=n})),W=function(t,e,n,r){for(var i=(n=q(n)).call(r,e),o=0,u=t.length;o>>1;n.call(r,t[a])=0)},H=X,Y=function(t,e){return _(t,Ct(e))},U=function(t){var e=s.apply(o,c.call(arguments,1));return L(t,(function(t){return!X(e,t)}))},J=function(t,e,n){if(null==t)return-1;var r=0,i=t.length;if(n){if("number"!=typeof n)return t[r=W(t,e)]===e?r:-1;r=n<0?Math.max(0,i+n):n}if(m&&t.indexOf===m)return t.indexOf(e,n);for(;r1&&void 0!==arguments[1]?arguments[1]:100;return function(){for(var r=this,i=arguments.length,o=new Array(i),u=0;ui&&(r=t,i=a)})),r},memoize:tt,now:Pt,omit:function(t){var e={},n=s.apply(o,c.call(arguments,1));for(var r in t)X(n,r)||(e[r]=t[r]);return e},once:Z,partial:K,pick:st,pluck:function(t,e){return C(t,xt(e))},property:xt,propertyOf:function(t){return null==t?function(){}:function(e){return t[e]}},reduce:E,reject:function(t,e,n){return L(t,(function(t,r,i){return!e.call(n,t,r,i)}),n)},result:function(t,e){if(null!=t){var n=t[e];return vt(n)?n.call(t):n}},select:I,size:function(t){return null==t?0:t.length===+t.length?t.length:it(t).length},some:D,sortedIndex:W,throttle:rt,where:function(t,e){return L(t,Ct(e))},without:function(t){return U(t,c.call(arguments,1))}}},function(t,e,n){"use strict";n.d(e,"t",(function(){return o})),n.d(e,"s",(function(){return u})),n.d(e,"r",(function(){return a})),n.d(e,"o",(function(){return c})),n.d(e,"p",(function(){return s})),n.d(e,"a",(function(){return l})),n.d(e,"c",(function(){return f})),n.d(e,"q",(function(){return d})),n.d(e,"d",(function(){return p})),n.d(e,"h",(function(){return h})),n.d(e,"e",(function(){return v})),n.d(e,"b",(function(){return O})),n.d(e,"f",(function(){return k})),n.d(e,"g",(function(){return x})),n.d(e,"k",(function(){return C})),n.d(e,"i",(function(){return P})),n.d(e,"j",(function(){return S})),n.d(e,"l",(function(){return E})),n.d(e,"m",(function(){return T})),n.d(e,"n",(function(){return A})),n.d(e,"v",(function(){return _})),n.d(e,"u",(function(){return F})),n.d(e,"w",(function(){return L}));var r=n(0);function i(t,e){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:null;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.code=Object(r.u)(n)?n:0,this.sourceError=i,e&&(this.key=e)}var e,n,o;return e=t,o=[{key:"logMessage",value:function(t){var e=t%1e3,n=Math.floor((t-e)/1e3),r=t;return e>=400&&e<600&&(r="".concat(n,"400-").concat(n,"599")),"JW Player ".concat(t>299999&&t<4e5?"Warning":"Error"," ").concat(t,". For more information see https://developer.jwplayer.com/jw-player/docs/developer-guide/api/errors-reference#").concat(r)}}],(n=null)&&i(e.prototype,n),o&&i(e,o),t}();function _(t,e,n){return n instanceof A&&n.code?n:new A(t,e,n)}function F(t,e){var n=_(T,e,t);return n.code=(t&&t.code||0)+e,n}function L(t){var e=t.name,n=t.message;switch(e){case"AbortError":return/pause/.test(n)?y:/load/.test(n)?m:b;case"NotAllowedError":return w;case"NotSupportedError":return j;default:return g}}},function(t,e,n){"use strict";n.d(e,"i",(function(){return o})),n.d(e,"e",(function(){return u})),n.d(e,"j",(function(){return a})),n.d(e,"a",(function(){return c})),n.d(e,"b",(function(){return s})),n.d(e,"g",(function(){return l})),n.d(e,"d",(function(){return f})),n.d(e,"f",(function(){return d})),n.d(e,"h",(function(){return p})),n.d(e,"c",(function(){return h}));var r=n(0),i=window.parseFloat;function o(t){return t.replace(/^\s+|\s+$/g,"")}function u(t,e,n){for(t=""+t,n=n||"0";t.length-1?t.substr(t.lastIndexOf(".")+1,t.length).toLowerCase():void 0}function s(t){var e=(t/60|0)%60,n=t%60;return u(t/3600|0,2)+":"+u(e,2)+":"+u(n.toFixed(3),6)}function l(t,e){if(!t)return 0;if(Object(r.u)(t))return t;var n=t.replace(",","."),o=n.slice(-1),u=n.split(":"),a=u.length,c=0;if("s"===o)c=i(n);else if("m"===o)c=60*i(n);else if("h"===o)c=3600*i(n);else if(a>1){var s=a-1;4===a&&(e&&(c=i(u[s])/e),s-=1),c+=i(u[s]),c+=60*i(u[s-1]),a>=3&&(c+=3600*i(u[s-2]))}else c=i(n);return Object(r.u)(c)?c:0}function f(t,e,n){if(Object(r.s)(t)&&"%"===t.slice(-1)){var o=i(t);return e&&Object(r.u)(e)&&Object(r.u)(o)?e*o/100:null}return l(t,n)}function d(t,e){return t.map((function(t){return e+t}))}function p(t,e){return t.map((function(t){return t+e}))}function h(t){return"string"==typeof t&&"%"===t.slice(-1)}},function(t,e,n){"use strict";n.d(e,"jb",(function(){return r})),n.d(e,"mb",(function(){return i})),n.d(e,"kb",(function(){return o})),n.d(e,"ob",(function(){return u})),n.d(e,"pb",(function(){return a})),n.d(e,"lb",(function(){return c})),n.d(e,"nb",(function(){return s})),n.d(e,"qb",(function(){return l})),n.d(e,"s",(function(){return f})),n.d(e,"u",(function(){return d})),n.d(e,"t",(function(){return p})),n.d(e,"n",(function(){return h})),n.d(e,"q",(function(){return v})),n.d(e,"rb",(function(){return g})),n.d(e,"r",(function(){return b})),n.d(e,"Z",(function(){return m})),n.d(e,"W",(function(){return y})),n.d(e,"v",(function(){return w})),n.d(e,"Y",(function(){return j})),n.d(e,"w",(function(){return O})),n.d(e,"tb",(function(){return k})),n.d(e,"a",(function(){return x})),n.d(e,"b",(function(){return C})),n.d(e,"c",(function(){return P})),n.d(e,"d",(function(){return S})),n.d(e,"e",(function(){return E})),n.d(e,"h",(function(){return T})),n.d(e,"F",(function(){return A})),n.d(e,"gb",(function(){return _})),n.d(e,"Q",(function(){return F})),n.d(e,"C",(function(){return L})),n.d(e,"B",(function(){return I})),n.d(e,"E",(function(){return M})),n.d(e,"p",(function(){return N})),n.d(e,"cb",(function(){return R})),n.d(e,"m",(function(){return D})),n.d(e,"G",(function(){return B})),n.d(e,"H",(function(){return q})),n.d(e,"N",(function(){return z})),n.d(e,"O",(function(){return V})),n.d(e,"R",(function(){return Q})),n.d(e,"ib",(function(){return W})),n.d(e,"bb",(function(){return X})),n.d(e,"D",(function(){return H})),n.d(e,"S",(function(){return Y})),n.d(e,"P",(function(){return U})),n.d(e,"T",(function(){return J})),n.d(e,"V",(function(){return $})),n.d(e,"M",(function(){return G})),n.d(e,"L",(function(){return K})),n.d(e,"K",(function(){return Z})),n.d(e,"I",(function(){return tt})),n.d(e,"J",(function(){return et})),n.d(e,"U",(function(){return nt})),n.d(e,"o",(function(){return rt})),n.d(e,"y",(function(){return it})),n.d(e,"hb",(function(){return ot})),n.d(e,"db",(function(){return ut})),n.d(e,"eb",(function(){return at})),n.d(e,"f",(function(){return ct})),n.d(e,"g",(function(){return st})),n.d(e,"ab",(function(){return lt})),n.d(e,"A",(function(){return ft})),n.d(e,"l",(function(){return dt})),n.d(e,"k",(function(){return pt})),n.d(e,"fb",(function(){return ht})),n.d(e,"sb",(function(){return vt})),n.d(e,"z",(function(){return gt})),n.d(e,"j",(function(){return bt})),n.d(e,"X",(function(){return mt})),n.d(e,"i",(function(){return yt})),n.d(e,"x",(function(){return wt}));var r="buffering",i="idle",o="complete",u="paused",a="playing",c="error",s="loading",l="stalled",f="drag",d="dragStart",p="dragEnd",h="click",v="doubleClick",g="tap",b="doubleTap",m="over",y="move",w="enter",j="out",O=c,k="warning",x="adClick",C="adPause",P="adPlay",S="adSkipped",E="adTime",T="autostartNotAllowed",A=o,_="ready",F="seek",L="beforePlay",I="beforeComplete",M="bufferFull",N="displayClick",R="playlistComplete",D="cast",B="mediaError",q="firstFrame",z="playAttempt",V="playAttemptFailed",Q="seeked",W="setupError",X="state",H="bufferChange",Y="time",U="ratechange",J="mediaType",$="volume",G="mute",K="metadataCueParsed",Z="meta",tt="levels",et="levelsChanged",nt="visualQuality",rt="controls",it="fullscreen",ot="resize",ut="playlistItem",at="playlist",ct="audioTracks",st="audioTrackChanged",lt="playbackRateChanged",ft="logoClick",dt="captionsList",pt="captionsChanged",ht="providerFirstFrame",vt="userAction",gt="instreamClick",bt="breakpoint",mt="fullscreenchange",yt="bandwidthEstimate",wt="float"},function(t,e,n){"use strict";n.d(e,"b",(function(){return i})),n.d(e,"d",(function(){return o})),n.d(e,"a",(function(){return u})),n.d(e,"c",(function(){return a}));var r=n(2);function i(t){var e="";return t&&(t.localName?e=t.localName:t.baseName&&(e=t.baseName)),e}function o(t){var e="";return t&&(t.textContent?e=Object(r.i)(t.textContent):t.text&&(e=Object(r.i)(t.text))),e}function u(t,e){return t.childNodes[e]}function a(t){return t.childNodes?t.childNodes.length:0}},function(t,e,n){"use strict";n.d(e,"i",(function(){return a})),n.d(e,"e",(function(){return c})),n.d(e,"q",(function(){return s})),n.d(e,"j",(function(){return l})),n.d(e,"s",(function(){return f})),n.d(e,"r",(function(){return d})),n.d(e,"u",(function(){return p})),n.d(e,"d",(function(){return g})),n.d(e,"a",(function(){return b})),n.d(e,"o",(function(){return m})),n.d(e,"p",(function(){return y})),n.d(e,"v",(function(){return w})),n.d(e,"t",(function(){return j})),n.d(e,"h",(function(){return O})),n.d(e,"b",(function(){return k})),n.d(e,"g",(function(){return x})),n.d(e,"c",(function(){return C})),n.d(e,"m",(function(){return P})),n.d(e,"k",(function(){return S})),n.d(e,"n",(function(){return E})),n.d(e,"l",(function(){return T})),n.d(e,"f",(function(){return A}));var r,i=n(0),o=n(2),u=n(8);function a(t,e){return t.classList.contains(e)}function c(t){return l(t).firstChild}function s(t,e){O(t),function(t,e){if(!e)return;for(var n=document.createDocumentFragment(),r=l(e).childNodes,i=0;i0?"":"px")}function h(t){return Object(i.s)(t.className)?t.className.split(" "):[]}function v(t,e){e=Object(o.i)(e),t.className!==e&&(t.className=e)}function g(t){return t.classList?t.classList:h(t)}function b(t,e){var n=h(t);(Array.isArray(e)?e:e.split(" ")).forEach((function(t){Object(i.b)(n,t)||n.push(t)})),v(t,n.join(" "))}function m(t,e){var n=h(t),r=Array.isArray(e)?e:e.split(" ");v(t,Object(i.e)(n,r).join(" "))}function y(t,e,n){var r=t.className||"";e.test(r)?r=r.replace(e,n):n&&(r+=" "+n),v(t,r)}function w(t,e,n){var r=a(t,e);(n=Object(i.n)(n)?n:!r)!==r&&(n?b(t,e):m(t,e))}function j(t,e,n){t.setAttribute(e,n)}function O(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function k(t){var e=document.createElement("link");e.rel="stylesheet",e.href=t,document.getElementsByTagName("head")[0].appendChild(e)}function x(t){t&&O(t)}function C(t){var e={left:0,right:0,width:0,height:0,top:0,bottom:0};if(!t||!document.body.contains(t))return e;var n=t.getBoundingClientRect(),r=window.pageYOffset,i=window.pageXOffset;return n.width||n.height||n.left||n.top?(e.left=n.left+i,e.right=n.right+i,e.top=n.top+r,e.bottom=n.bottom+r,e.width=n.right-n.left,e.height=n.bottom-n.top,e):e}function P(t,e){t.insertBefore(e,t.firstChild)}function S(t){return t.nextElementSibling}function E(t){return t.previousElementSibling}function T(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=document.createElement("a");r.href=t,r.target=e,r=Object(i.g)(r,n),u.Browser.firefox?r.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window})):r.click()}function A(){var t=window.screen.orientation;return!!t&&("landscape-primary"===t.type||"landscape-secondary"===t.type)||90===window.orientation||-90===window.orientation}},function(t,e,n){"use strict";n.d(e,"h",(function(){return u})),n.d(e,"f",(function(){return a})),n.d(e,"l",(function(){return s})),n.d(e,"k",(function(){return f})),n.d(e,"p",(function(){return d})),n.d(e,"g",(function(){return p})),n.d(e,"e",(function(){return h})),n.d(e,"n",(function(){return v})),n.d(e,"d",(function(){return g})),n.d(e,"i",(function(){return b})),n.d(e,"q",(function(){return m})),n.d(e,"j",(function(){return y})),n.d(e,"c",(function(){return w})),n.d(e,"b",(function(){return j})),n.d(e,"o",(function(){return O})),n.d(e,"m",(function(){return k})),n.d(e,"a",(function(){return x}));var r=navigator.userAgent;function i(t){return null!==r.match(t)}function o(t){return function(){return i(t)}}function u(){var t=x();return!!(t&&t>=18)}var a=o(/gecko\//i),c=o(/trident\/.+rv:\s*11/i),s=o(/iP(hone|od)/i),l="MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,f=function(){return i(/iPad/i)||l},d=function(){return i(/Macintosh/i)&&!l},p=o(/FBAV/i);function h(){return i(/\sEdge\/\d+/i)}function v(){return i(/msie/i)}function g(){return i(/\s(?:(?:Headless)?Chrome|CriOS)\//i)&&!h()&&!i(/UCBrowser/i)}function b(){return h()||c()||v()}function m(){return i(/safari/i)&&!i(/(?:Chrome|CriOS|chromium|android|phantom)/i)}function y(){return i(/iP(hone|ad|od)/i)||l}function w(){return!(i(/chrome\/[123456789]/i)&&!i(/chrome\/18/i)&&!a())&&j()}function j(){return i(/Android/i)&&!i(/Windows Phone/i)}function O(){return y()||j()||i(/Windows Phone/i)}function k(){try{return window.self!==window.top}catch(t){return!0}}function x(){if(j())return 0;var t,e=navigator.plugins;if(e&&(t=e["Shockwave Flash"])&&t.description)return parseFloat(t.description.replace(/\D+(\d+\.?\d*).*/,"$1"));if(void 0!==window.ActiveXObject){try{if(t=new window.ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseFloat(t.GetVariable("$version").split(" ")[1].replace(/\s*,\s*/,"."))}catch(t){return 0}return t}return 0}},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t,e){for(var n=0;ne)return t[e]}var o=n(0);n.d(e,"Browser",(function(){return a})),n.d(e,"OS",(function(){return c})),n.d(e,"Features",(function(){return s}));var u=navigator.userAgent;var a={},c={},s={};Object.defineProperties(a,{androidNative:{get:Object(o.x)(r.c),enumerable:!0},chrome:{get:Object(o.x)(r.d),enumerable:!0},edge:{get:Object(o.x)(r.e),enumerable:!0},facebook:{get:Object(o.x)(r.g),enumerable:!0},firefox:{get:Object(o.x)(r.f),enumerable:!0},ie:{get:Object(o.x)(r.i),enumerable:!0},msie:{get:Object(o.x)(r.n),enumerable:!0},safari:{get:Object(o.x)(r.q),enumerable:!0},version:{get:Object(o.x)(function(t,e){var n,r,i,o;return t.chrome?n=-1!==e.indexOf("Chrome")?e.substring(e.indexOf("Chrome")+7):e.substring(e.indexOf("CriOS")+6):t.safari?n=e.substring(e.indexOf("Version")+8):t.firefox?n=e.substring(e.indexOf("Firefox")+8):t.edge?n=e.substring(e.indexOf("Edge")+5):t.ie&&(-1!==e.indexOf("rv:")?n=e.substring(e.indexOf("rv:")+3):-1!==e.indexOf("MSIE")&&(n=e.substring(e.indexOf("MSIE")+5))),n&&(-1!==(o=n.indexOf(";"))&&(n=n.substring(0,o)),-1!==(o=n.indexOf(" "))&&(n=n.substring(0,o)),-1!==(o=n.indexOf(")"))&&(n=n.substring(0,o)),r=parseInt(n,10),i=parseInt(n.split(".")[1],10)),{version:n,major:r,minor:i}}.bind(void 0,a,u)),enumerable:!0}}),Object.defineProperties(c,{android:{get:Object(o.x)(r.b),enumerable:!0},iOS:{get:Object(o.x)(r.j),enumerable:!0},mobile:{get:Object(o.x)(r.o),enumerable:!0},mac:{get:Object(o.x)(r.p),enumerable:!0},iPad:{get:Object(o.x)(r.k),enumerable:!0},iPhone:{get:Object(o.x)(r.l),enumerable:!0},windows:{get:Object(o.x)((function(){return u.indexOf("Windows")>-1})),enumerable:!0},version:{get:Object(o.x)(function(t,e){var n,r,o;if(t.windows)switch(n=i(/Windows(?: NT|)? ([._\d]+)/.exec(e),1)){case"6.1":n="7.0";break;case"6.2":n="8.0";break;case"6.3":n="8.1"}else t.android?n=i(/Android ([._\d]+)/.exec(e),1):t.iOS?n=i(/OS ([._\d]+)/.exec(e),1):t.mac&&(n=i(/Mac OS X (10[._\d]+)/.exec(e),1));if(n){r=parseInt(n,10);var u=n.split(/[._]/);u&&(o=parseInt(u[1],10))}return{version:n,major:r,minor:o}}.bind(void 0,c,u)),enumerable:!0}}),Object.defineProperties(s,{flash:{get:Object(o.x)(r.h),enumerable:!0},flashVersion:{get:Object(o.x)(r.a),enumerable:!0},iframe:{get:Object(o.x)(r.m),enumerable:!0},passiveEvents:{get:Object(o.x)((function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){return t=!0}});window.addEventListener("testPassive",null,e),window.removeEventListener("testPassive",null,e)}catch(t){}return t})),enumerable:!0},backgroundLoading:{get:Object(o.x)((function(){return!(c.iOS||a.safari)})),enumerable:!0}})},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}n.r(e),n.d(e,"exists",(function(){return o})),n.d(e,"isHTTPS",(function(){return u})),n.d(e,"isFileProtocol",(function(){return a})),n.d(e,"isRtmp",(function(){return c})),n.d(e,"isYouTube",(function(){return s})),n.d(e,"typeOf",(function(){return l})),n.d(e,"isDeepKeyCompliant",(function(){return f}));var i=window.location.protocol;function o(t){switch(r(t)){case"string":return t.length>0;case"object":return null!==t;case"undefined":return!1;default:return!0}}function u(){return"https:"===i}function a(){return"file:"===i}function c(t,e){return 0===t.indexOf("rtmp:")||"rtmp"===e}function s(t,e){return"youtube"===e||/^(http|\/\/).*(youtube\.com|youtu\.be)\/.+/.test(t)}function l(t){if(null===t)return"null";var e=r(t);return"object"===e&&Array.isArray(t)?"array":e}function f(t,e,n){var i=Object.keys(t);return Object.keys(e).length>=i.length&&i.every((function(i){var o=t[i],u=e[i];return o&&"object"===r(o)?!(!u||"object"!==r(u))&&f(o,u,n):n(i,t)}))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return c})),n.d(e,"b",(function(){return s})),n.d(e,"d",(function(){return l})),n.d(e,"e",(function(){return p})),n.d(e,"c",(function(){return h}));var r=n(2),i=n(39),o=n.n(i);function u(t){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var a,c=o.a.clear;function s(t,e,n,r){n=n||"all-players";var i="";if("object"===u(e)){var a=document.createElement("div");l(a,e);var c=a.style.cssText;Object.prototype.hasOwnProperty.call(e,"content")&&c&&(c="".concat(c,' content: "').concat(e.content,'";')),r&&c&&(c=c.replace(/;/g," !important;")),i="{"+c+"}"}else"string"==typeof e&&(i=e);""!==i&&"{}"!==i?o.a.style([[t,t+i]],n):o.a.clear(n,t)}function l(t,e){if(null!=t){var n;void 0===t.length&&(t=[t]);var r={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=d(n,e[n]));for(var i=0;i-1?t:parseInt(t.replace("px",""),10):t}function l(t,e){if(t<=0&&!e||Object(i.q)(parseInt(t)))return"00:00";var n=t<0?"-":"";t=Math.abs(t);var r=Math.floor(t/3600),o=Math.floor((t-3600*r)/60),u=Math.floor(t%60);return n+(r?r+":":"")+(o<10?"0":"")+o+":"+(u<10?"0":"")+u}},function(t,e,n){"use strict";e.a=[]},function(t,e,n){"use strict";n.d(e,"h",(function(){return d})),n.d(e,"c",(function(){return h})),n.d(e,"e",(function(){return g})),n.d(e,"f",(function(){return b})),n.d(e,"b",(function(){return m})),n.d(e,"d",(function(){return w})),n.d(e,"g",(function(){return j})),n.d(e,"a",(function(){return O}));var r=n(0),i=n(6),o=n(22),u=n(9),a=n(33),c={},s={zh:"Chinese",nl:"Dutch",en:"English",fr:"French",de:"German",it:"Italian",ja:"Japanese",pt:"Portuguese",ru:"Russian",es:"Spanish",el:"Greek",fi:"Finnish",id:"Indonesian",ko:"Korean",th:"Thai",vi:"Vietnamese"};Object(r.m)(s);function l(t){var e=f(t),n=e.indexOf("_");return-1===n?e:e.substring(0,n)}function f(t){return t.toLowerCase().replace("-","_")}function d(t){return t?Object.keys(t).reduce((function(e,n){return e[f(n)]=t[n],e}),{}):{}}function p(t){var e=t.querySelector("html");return e?e.getAttribute("lang"):null}function h(){var t=p(document);if(!t&&Object(i.m)())try{t=p(window.top.document)}catch(t){}return t||navigator.language||"en"}var v=["ar","da","de","el","es","fi","fr","he","id","it","ja","ko","nl","no","oc","pt","ro","ru","sl","sv","th","tr","vi","zh"];function g(t){return 8207===t.charCodeAt(0)||/^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.test(t)}function b(t){return v.indexOf(l(t))>=0}function m(t,e,n){return Object(r.g)({},function(t){var e=t.advertising,n=t.related,i=t.sharing,o=t.abouttext,u=Object(r.g)({},t.localization);e&&(u.advertising=u.advertising||{},y(u.advertising,e,"admessage"),y(u.advertising,e,"cuetext"),y(u.advertising,e,"loadingAd"),y(u.advertising,e,"podmessage"),y(u.advertising,e,"skipmessage"),y(u.advertising,e,"skiptext"));"string"==typeof u.related?u.related={heading:u.related}:u.related=u.related||{};n&&y(u.related,n,"autoplaymessage");i&&(u.sharing=u.sharing||{},y(u.sharing,i,"heading"),y(u.sharing,i,"copied"));o&&y(u,t,"abouttext");var a=u.close||u.nextUpClose;a&&(u.close=a);return u}(t),e[l(n)],e[f(n)])}function y(t,e,n){var r=t[n]||e[n];r&&(t[n]=r)}function w(t){return Object(u.isDeepKeyCompliant)(a.a,t,(function(t,e){return"string"==typeof e[t]}))}function j(t,e){var n=c[e];if(!n){var r="".concat(t,"translations/").concat(l(e),".json");c[e]=n=new Promise((function(t,n){Object(o.a)({url:r,oncomplete:t,onerror:function(t,r,i,o){c[e]=null,n(o)},responseType:"json"})}))}return n}function O(t,e){var n=Object(r.g)({},t,e);return k(n,"errors",t,e),k(n,"related",t,e),k(n,"sharing",t,e),k(n,"advertising",t,e),k(n,"shortcuts",t,e),n}function k(t,e,n,i){t[e]=Object(r.g)({},n[e],i[e])}},function(t,e,n){"use strict";var r=n(52),i=n(9),o=document.createElement("video"),u={aac:"audio/mp4",mp4:"video/mp4",f4v:"video/mp4",m4v:"video/mp4",mov:"video/mp4",mp3:"audio/mpeg",mpeg:"audio/mpeg",ogv:"video/ogg",ogg:"video/ogg",oga:"video/ogg",vorbis:"video/ogg",webm:"video/webm",f4a:"video/aac",m3u8:"application/vnd.apple.mpegurl",m3u:"application/vnd.apple.mpegurl",hls:"application/vnd.apple.mpegurl"},a=[{name:"html5",supports:function(t){if(!1===Object(r.a)(t))return!1;if(!o.canPlayType)return!1;var e=t.file,n=t.type;if(Object(i.isRtmp)(e,n))return!1;var a=t.mimeType||u[n];if(!a)return!1;var c=t.mediaTypes;c&&c.length&&(a=[a].concat(c.slice()).join("; "));return!!o.canPlayType(a)}}];e.a=a},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r=Date.now||function(){return(new Date).getTime()}},function(t,e,n){"use strict";n.d(e,"a",(function(){return l})),n.d(e,"d",(function(){return f})),n.d(e,"b",(function(){return d})),n.d(e,"c",(function(){return p}));var r=n(25),i=n(26),o=n(14),u=n(21),a=n(32),c=n(1),s=null,l={};function f(t){return s||(s=function(t){var e=t.get("controls"),s=h(),f=function(t,e){var n=t.get("playlist");if(Array.isArray(n)&&n.length)for(var u=Object(i.c)(Object(r.a)(n[0]),t),a=0;a=0)return r.substr(0,i+1)}}return""}),o=function(){return i("jwplayer.js")},u=function(t){var e=("0"+t).split(/\W/),n=r.a.split(/\W/),i=parseFloat(e[0]),o=parseFloat(n[0]);return!(i>o)&&!(i===o&&parseFloat("0"+e[1])>parseFloat(n[1]))},a=function(){return i("jwplayer.js")}},function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(28),i=n(14),o=n(53),u=n(0);function a(t){var e=t.getName().name;if(!r.a[e]){if(!Object(u.i)(i.a,Object(u.w)({name:e}))){if(!Object(u.p)(t.supports))throw new Error("Tried to register a provider with an invalid object");i.a.unshift({name:e,supports:t.supports})}Object(u.d)(t.prototype,o.a),r.a[e]=t}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return m}));var r=n(0),i=n(11),o=n(9),u=n(1),a=1,c=2,s=3,l=4,f=5,d=6,p=7,h=601,v=602,g=611,b=function(){};function m(t,e,n,h){var O;t===Object(t)&&(t=(h=t).url);var k=Object(r.g)({xhr:null,url:t,withCredentials:!1,retryWithoutCredentials:!1,timeout:6e4,timeoutId:-1,oncomplete:e||b,onerror:n||b,mimeType:h&&!h.responseType?"text/xml":"",requireValidXML:!1,responseType:h&&h.plainText?"text":"",useDomParser:!1,requestFilter:null},h),x=function(t,e){return function(t,n){var i=t.currentTarget||e.xhr;if(clearTimeout(e.timeoutId),e.retryWithoutCredentials&&e.xhr.withCredentials)return y(i),void m(Object(r.g)({},e,{xhr:null,withCredentials:!1,retryWithoutCredentials:!1}));!n&&i.status>=400&&i.status<600&&(n=i.status),w(e,n?u.k:u.m,n||d,t)}}(0,k);if("XMLHttpRequest"in window){if(O=k.xhr=k.xhr||new window.XMLHttpRequest,"function"==typeof k.requestFilter){var C;try{C=k.requestFilter({url:t,xhr:O})}catch(t){return x(t,f),O}C&&"open"in C&&"send"in C&&(O=k.xhr=C)}O.onreadystatechange=function(t){return function(e){var n=e.currentTarget||t.xhr;if(4===n.readyState){clearTimeout(t.timeoutId);var a=n.status;if(a>=400)return void w(t,u.k,a<600?a:d);if(200===a)return function(t){return function(e){var n=e.currentTarget||t.xhr;if(clearTimeout(t.timeoutId),t.responseType){if("json"===t.responseType)return function(t,e){if(!t.response||"string"==typeof t.response&&'"'!==t.responseText.substr(1))try{t=Object(r.g)({},t,{response:JSON.parse(t.responseText)})}catch(t){return void w(e,u.k,g,t)}return e.oncomplete(t)}(n,t)}else{var o,a=n.responseXML;if(a)try{o=a.firstChild}catch(t){}if(a&&o)return j(n,a,t);if(t.useDomParser&&n.responseText&&!a&&(a=Object(i.parseXML)(n.responseText))&&a.firstChild)return j(n,a,t);if(t.requireValidXML)return void w(t,u.k,v)}t.oncomplete(n)}}(t)(e);0===a&&Object(o.isFileProtocol)()&&!/^[a-z][a-z0-9+.-]*:/.test(t.url)&&w(t,u.k,p)}}}(k),O.onerror=x,"overrideMimeType"in O?k.mimeType&&O.overrideMimeType(k.mimeType):k.useDomParser=!0;try{t=t.replace(/#.*$/,""),O.open("GET",t,!0)}catch(t){return x(t,s),O}if(k.responseType)try{O.responseType=k.responseType}catch(t){}k.timeout&&(k.timeoutId=setTimeout((function(){y(O),w(k,u.m,a)}),k.timeout),O.onabort=function(){clearTimeout(k.timeoutId)});try{k.withCredentials&&"withCredentials"in O&&(O.withCredentials=!0),O.send()}catch(t){x(t,l)}return O}w(k,u.m,c)}function y(t){t.onload=null,t.onprogress=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()}function w(t,e,n,r){t.onerror(e,t.url,t.xhr,new u.n(e,n,r))}function j(t,e,n){var i=e.documentElement;if(!n.requireValidXML||"parsererror"!==i.nodeName&&!i.getElementsByTagName("parsererror").length)return t.responseXML||(t=Object(r.g)({},t,{responseXML:e})),n.oncomplete(t);w(n,u.k,h)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r="8.11.5+local.2019-12-16-11-42-43-502"},function(t,e,n){"use strict";var r=n(0),i=n(15),o=window.performance||{timing:{}},u=o.timing.navigationStart||Object(i.a)();function a(){return u+o.now()}"now"in o||(o.now=function(){return Object(i.a)()-u});e.a=function(){var t={},e={},n={},i={};return{start:function(e){t[e]=a(),n[e]=n[e]+1||1},end:function(n){if(t[n]){var r=a()-t[n];delete t[n],e[n]=e[n]+r||r}},dump:function(){var o=Object(r.g)({},e);for(var u in t)if(Object.prototype.hasOwnProperty.call(t,u)){var c=a()-t[u];o[u]=o[u]+c||c}return{counts:Object(r.g)({},n),sums:o,events:Object(r.g)({},i)}},tick:function(t){i[t]=a()},clear:function(t){delete i[t]},between:function(t,e){return i[e]&&i[t]?i[e]-i[t]:null}}}},function(t,e,n){"use strict";var r=n(0),i=n(29),o=function(t){if(t&&t.file)return Object(r.g)({},{kind:"captions",default:!1},t)},u=Array.isArray;e.a=function(t){u((t=t||{}).tracks)||delete t.tracks;var e=Object(r.g)({},{sources:[],tracks:[],minDvrWindow:120,dvrSeekLimit:25},t);e.sources!==Object(e.sources)||u(e.sources)||(e.sources=[Object(i.a)(e.sources)]),u(e.sources)&&0!==e.sources.length||(t.levels?e.sources=t.levels:e.sources=[Object(i.a)(t)]);for(var n=0;n0)return d;var n=t.indexOf("/"),r=Object(f.a)(t);return!(e<0&&n<0)||r&&isNaN(r)?p:2}};var v=function(t){this.url=t,this.promise_=null};Object.defineProperties(v.prototype,{promise:{get:function(){return this.promise_||this.load()},set:function(){}}}),Object(i.g)(v.prototype,{load:function(){var t=this,e=this.promise_;if(!e){if(2===h(this.url))e=Promise.resolve(this);else{var n=new s.a(function(t){switch(h(t)){case d:return t;case p:return Object(l.getAbsolutePath)(t,window.location.href)}}(this.url));this.loader=n,e=n.load().then((function(){return t}))}this.promise_=e}return e},registerPlugin:function(t,e,n){this.name=t,this.target=e,this.js=n},getNewInstance:function(t,e,n){var i=this.js;if("function"!=typeof i)throw new r.n(null,u(this.url)+100);var o=new i(t,e,n);return o.addToPlayer=function(){var e=t.getContainer().querySelector(".jw-overlays");e&&(n.left=e.style.left,n.top=e.style.top,e.appendChild(n),o.displayArea=e)},o.resizeHandler=function(){var t=o.displayArea;t&&o.resize(t.clientWidth,t.clientHeight)},o}});var g=v,b=n(38),m={},y=function(){},w=y.prototype;w.setupPlugin=function(t){var e=this.getPlugin(t);return e?(e.url!==t&&Object(b.a)('JW Plugin "'.concat(o(t),'" already loaded from "').concat(e.url,'". Ignoring "').concat(t,'."')),e.promise):this.addPlugin(t).load()},w.addPlugin=function(t){var e=o(t),n=m[e];return n||(n=new g(t),m[e]=n),n},w.getPlugin=function(t){return m[o(t)]},w.removePlugin=function(t){delete m[o(t)]},w.getPlugins=function(){return m};var j=y;n.d(e,"b",(function(){return k})),n.d(e,"a",(function(){return x}));var O=new j,k=function(t,e,n){var r=O.addPlugin(t);r.js||r.registerPlugin(t,e,n)};function x(t,e){var n=t.get("plugins");return window.jwplayerPluginJsonp=k,(t.pluginLoader=t.pluginLoader||new c).load(e,O,n,t).then((function(e){if(!t.attributes._destroyed)return delete window.jwplayerPluginJsonp,e}))}},function(t,e,n){"use strict";e.a={}},function(t,e,n){"use strict";var r=n(0),i=n(9),o=n(2);e.a=function(t){if(t&&t.file){var e=Object(r.g)({},{default:!1},t);e.file=Object(o.i)(""+e.file);var n=/^[^/]+\/(?:x-)?([^/]+)$/;if(n.test(e.type)&&(e.mimeType=e.type,e.type=e.type.replace(n,"$1")),Object(i.isYouTube)(e.file)?e.type="youtube":Object(i.isRtmp)(e.file)?e.type="rtmp":e.type||(e.type=Object(o.a)(e.file)),e.type){switch(e.type){case"m3u8":case"vnd.apple.mpegurl":e.type="hls";break;case"dash+xml":e.type="dash";break;case"m4a":e.type="aac";break;case"smil":e.type="rtmp"}return Object.keys(e).forEach((function(t){""===e[t]&&delete e[t]})),e}}}},,,function(t,e,n){"use strict";n.d(e,"a",(function(){return o})),n.d(e,"b",(function(){return u}));var r=n(16),i=null,o={};function u(){return i||(i=n.e(1).then(function(t){var e=n(17).default;return o.controls=e,e}.bind(null,n)).catch((function(){i=null,Object(r.c)(301130)()}))),i}},function(t,e,n){"use strict";e.a={advertising:{admessage:"This ad will end in xx",cuetext:"Advertisement",displayHeading:"Advertisement",loadingAd:"Loading ad",podmessage:"Ad __AD_POD_CURRENT__ of __AD_POD_LENGTH__.",skipmessage:"Skip ad in xx",skiptext:"Skip"},airplay:"AirPlay",audioTracks:"Audio Tracks",auto:"Auto",buffer:"Loading",cast:"Chromecast",cc:"Closed Captions",close:"Close",errors:{badConnection:"This video cannot be played because of a problem with your internet connection.",cantLoadPlayer:"Sorry, the video player failed to load.",cantPlayInBrowser:"The video cannot be played in this browser.",cantPlayVideo:"This video file cannot be played.",errorCode:"Error Code",liveStreamDown:"The live stream is either down or has ended.",protectedContent:"There was a problem providing access to protected content.",technicalError:"This video cannot be played because of a technical error."},exitFullscreen:"Exit Fullscreen",fullscreen:"Fullscreen",hd:"Quality",liveBroadcast:"Live",logo:"Logo",mute:"Mute",next:"Next",nextUp:"Next Up",notLive:"Not Live",off:"Off",pause:"Pause",play:"Play",playback:"Play",playbackRates:"Playback Rates",player:"Video Player",poweredBy:"Powered by",prev:"Previous",related:{autoplaymessage:"Next up in xx",heading:"More Videos"},replay:"Replay",rewind:"Rewind 10 Seconds",settings:"Settings",sharing:{copied:"Copied",email:"Email",embed:"Embed",heading:"Share",link:"Link"},slider:"Seek",stop:"Stop",unmute:"Unmute",videoInfo:"About This Video",volume:"Volume",volumeSlider:"Volume",shortcuts:{playPause:"Play/Pause",volumeToggle:"Mute/Unmute",fullscreenToggle:"Fullscreen/Exit Fullscreen",seekPercent:"Seek %",keyboardShortcuts:"Keyboard Shortcuts",increaseVolume:"Increase Volume",decreaseVolume:"Decrease Volume",seekForward:"Seek Forward",seekBackward:"Seek Backward",spacebar:"SPACE",captionsToggle:"Captions On/Off"}}},function(t,e,n){"use strict";var r=n(0),i=n(14),o=n(21),u=n(28),a=n(16);function c(t){this.config=t||{}}var s={html5:function(){return n.e(9).then(function(t){var e=n(31).default;return Object(o.a)(e),e}.bind(null,n)).catch(Object(a.b)(152))}};Object(r.g)(c.prototype,{load:function(t){var e=s[t],n=function(){return Promise.reject(new Error("Failed to load media"))};return e?e().then((function(){var e=u.a[t];return e||n()})):n()},providerSupports:function(t,e){return t.supports(e)},choose:function(t){if(t===Object(t))for(var e=i.a.length,n=0;n')+'
    '+'
    '.concat(e||"",'').concat(i,"
    ")+"
    "},i=n(5),o=n(10);function u(t,e){var n=e.message,u=e.code,a=r(t.get("id"),n,t.get("localization").errors.errorCode,u),c=t.get("width"),s=t.get("height"),l=Object(i.e)(a);return Object(o.d)(l,{width:c.toString().indexOf("%")>0?c:"".concat(c,"px"),height:s.toString().indexOf("%")>0?s:"".concat(s,"px")}),l}n.d(e,"a",(function(){return u}))},function(t,e,n){"use strict";function r(t){return t.slice&&"px"===t.slice(-2)&&(t=t.slice(0,-2)),t}function i(t,e){if(-1===e.toString().indexOf("%"))return 0;if("string"!=typeof t||!t)return 0;if(/^\d*\.?\d+%$/.test(t))return t;var n=t.indexOf(":");if(-1===n)return 0;var r=parseFloat(t.substr(0,n)),i=parseFloat(t.substr(n+1));return r<=0||i<=0?0:i/r*100+"%"}n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}))},function(t,e,n){"use strict";var r=n(0),i=n(7),o=n(3),u={},a=45e3,c=2,s=3;function l(t){var e=document.createElement("link");return e.type="text/css",e.rel="stylesheet",e.href=t,e}function f(t,e){var n=document.createElement("script");return n.type="text/javascript",n.charset="utf-8",n.async=!0,n.timeout=e||a,n.src=t,n}var d=function(t,e,n){var r=this,i=0;function d(t){i=c,r.trigger(o.w,t).off()}function p(t){i=s,r.trigger(o.kb,t).off()}this.getStatus=function(){return i},this.load=function(){var r=u[t];return 0!==i?r:(r&&r.then(p).catch(d),i=1,r=new Promise((function(r,i){var o=(e?l:f)(t,n),u=function(){o.onerror=o.onload=null,clearTimeout(s)},c=function(t){u(),d(t),i(t)},s=setTimeout((function(){c(new Error("Network timeout ".concat(t)))}),a);o.onerror=function(){c(new Error("Failed to load ".concat(t)))},o.onload=function(t){u(),p(t),r(t)};var h=document.getElementsByTagName("head")[0]||document.documentElement;h.insertBefore(o,h.firstChild)})),u[t]=r,r)}};Object(r.g)(d.prototype,i.a),e.a=d},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r="function"==typeof console.log?console.log.bind(console):function(){}},function(t,e){var n,r,i={},o={},u=(n=function(){return document.head||document.getElementsByTagName("head")[0]},function(){return void 0===r&&(r=n.apply(this,arguments)),r});function a(t){var e=document.createElement("style");return e.type="text/css",e.setAttribute("data-jwplayer-id",t),function(t){u().appendChild(t)}(e),e}function c(t,e){var n,r,i,u=o[t];u||(u=o[t]={element:a(t),counter:0});var c=u.counter++;return n=u.element,i=function(){f(n,c,"")},(r=function(t){f(n,c,t)})(e.css),function(t){if(t){if(t.css===e.css&&t.media===e.media)return;r((e=t).css)}else i()}}t.exports={style:function(t,e){!function(t,e){for(var n=0;nk*k&&(N(t,i.u,e),t.dragged=!0,N(t,i.s,e))}n||"touchmove"!==e.type||D(e)},s=function(n){if(clearTimeout(h),t.el)if(I(t),L(t,y),t.dragged)t.dragged=!1,N(t,i.t,n);else if(-1===n.type.indexOf("cancel")&&e.contains(n.target)){if(Object(u.a)()-t.lastStart>C)return;var r="pointerup"===n.type||"pointercancel"===n.type,o="mouseup"===n.type||r&&"mouse"===n.pointerType;!function(t,e,n){if(t.enableDoubleTap)if(Object(u.a)()-t.lastClick4&&void 0!==arguments[4]?arguments[4]:O,o=t.handlers[e],u=t.options[e];if(o||(o=t.handlers[e]={},u=t.options[e]={}),o[n])throw new Error("".concat(e," ").concat(n," already registered"));o[n]=r,u[n]=i;var a=t.el;(e===y?_(a):a).addEventListener(n,r,i)}function L(t,e){var n=t.el,r=t.handlers,i=t.options,o=e===y?_(n):n,u=r[e],a=i[e];u&&(Object.keys(u).forEach((function(t){var e=a[t];"boolean"==typeof e?o.removeEventListener(t,u[t],e):o.removeEventListener(t,u[t])})),r[e]=null,i[e]=null)}function I(t){var e=t.el;null!==t.pointerId&&(e.releasePointerCapture(t.pointerId),t.pointerId=null)}function M(t,e,n){var r=t.el,i=n.target;t.trigger(e,{type:e,sourceEvent:n,currentTarget:r,target:i})}function N(t,e,n){var r=function(t,e,n){var r,i=e.target,o=e.touches,u=e.changedTouches,a=e.pointerType;o||u?(r=o&&o.length?o[0]:u[0],a=a||"touch"):(r=e,a=a||"mouse");var c=r,s=c.pageX,l=c.pageY;return{type:t,pointerType:a,pageX:s,pageY:l,sourceEvent:e,currentTarget:n,target:i}}(e,n,t.el);t.trigger(e,r)}function R(t){return 0===t.type.indexOf("touch")?(t.originalEvent||t).changedTouches[0]:t}function D(t){t.preventDefault&&t.preventDefault()}},function(t,e,n){"use strict";n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}));var r={audioMode:!1,flashBlocked:!1,item:0,itemMeta:{},playbackRate:1,playRejected:!1,state:n(3).mb,itemReady:!1,controlsEnabled:!1},i={position:0,duration:0,buffer:0,currentTime:0}},function(t,e,n){"use strict";n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}));var r=window.requestAnimationFrame||function(t){return setTimeout(t,17)},i=window.cancelAnimationFrame||clearTimeout},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r=function(t,e,n){return Math.max(Math.min(t,n),e)}},function(t,e,n){"use strict";function r(t,e,n){var r=[],i={};function o(){for(;r.length>0;){var e=r.shift(),n=e.command,o=e.args;(i[n]||t[n]).apply(t,o)}}e.forEach((function(e){var u=t[e];i[e]=u,t[e]=function(){var t=Array.prototype.slice.call(arguments,0);n()?r.push({command:e,args:t}):(o(),u&&u.apply(this,t))}})),Object.defineProperty(this,"queue",{enumerable:!0,get:function(){return r}}),this.flush=o,this.empty=function(){r.length=0},this.off=function(){e.forEach((function(e){var n=i[e];n&&(t[e]=n,delete i[e])}))},this.destroy=function(){this.off(),this.empty()}}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t,e){for(var n=0;n=.25&&t<=4})).map((function(t){return Math.round(100*t)/100}))).indexOf(1)<0&&b.push(1),b.sort(),h.playbackRateControls=!0,h.playbackRates=b}(!h.playbackRateControls||h.playbackRates.indexOf(h.defaultPlaybackRate)<0)&&(h.defaultPlaybackRate=1),h.playbackRate=h.defaultPlaybackRate,h.aspectratio||delete h.aspectratio;var m=h.playlist;if(m)Array.isArray(m.playlist)&&(h.feedData=m,h.playlist=m.playlist);else{var y=Object(r.y)(h,["title","description","type","mediaid","image","images","file","sources","tracks","preload","duration"]);h.playlist=[y]}h.qualityLabels=h.qualityLabels||h.hlslabels,delete h.duration;var w=h.liveTimeout;null!==w&&(Object(r.u)(w)?0!==w&&(w=Math.max(30,w)):w=null,h.liveTimeout=w);var j,O,k,x=parseFloat(h.bandwidthEstimate),C=parseFloat(h.bitrateSelection);return h.bandwidthEstimate=Object(r.u)(x)?x:(j=h.defaultBandwidthEstimate,O=parseFloat(j),Object(r.u)(O)?Math.max(O,1):f.bandwidthEstimate),h.bitrateSelection=Object(r.u)(C)?C:f.bitrateSelection,h.liveSyncDuration=(k=h.liveSyncDuration)?k<5?5:k>30?30:k:25,h.backgroundLoading=Object(r.n)(h.backgroundLoading)?h.backgroundLoading:c.Features.backgroundLoading,h},p=n(16),h=n(27),v=n(3),g=n(51),b=n(26),m=n(37),y=n(1);function w(t,e,n){var r=t.attributes;r.playlist=Object(b.a)(e),r.feedData=n}function j(t){return function(t){var e=t.get("playlist");return new Promise((function(n,r){if("string"!=typeof e){var i=t.get("feedData")||{};return w(t,e,i),n()}var o=new g.a;o.on(v.eb,(function(e){var r=e.playlist;delete e.playlist,w(t,r,e),n()})),o.on(v.w,(function(e){w(t,[],{}),r(Object(y.u)(e,y.p))})),o.load(e)}))}(t).then((function(){if(!x(t)){var e=Object(b.b)(t.get("playlist"),t);t.attributes.playlist=e;try{Object(b.e)(e)}catch(t){throw t.code+=y.p,t}var n=t.getProviders(),r=n.choose(e[0].sources[0]),i=r.provider,o=r.name;return"function"==typeof i?i:p.a.html5&&"html5"===o?p.a.html5:n.load(o).catch((function(t){throw Object(y.u)(t,y.q)}))}}))}function O(t){var e=t.get("skin")?t.get("skin").url:void 0;if("string"==typeof e&&!function(t){for(var e=document.styleSheets,n=0,r=e.length;n0&&(n=t(l,n));break;case"title":n.title=Object(o.d)(l);break;case"description":n.description=Object(o.d)(l);break;case"guid":n.mediaid=Object(o.d)(l);break;case"thumbnail":n.image||(n.image=Object(u.j)(l,"url"));break;case"group":t(l,n);break;case"subtitle":var p={};p.file=Object(u.j)(l,"url"),p.kind="captions",Object(u.j)(l,"lang").length>0&&(p.label=(r=Object(u.j)(l,"lang"),i=void 0,(i={zh:"Chinese",nl:"Dutch",en:"English",fr:"French",de:"German",it:"Italian",ja:"Japanese",pt:"Portuguese",ru:"Russian",es:"Spanish"})[r]?i[r]:r)),c.push(p)}}}n.hasOwnProperty("tracks")||(n.tracks=[]);for(var h=0;h0&&(r[f][n]="true"===r[f][n],r[f].label.length||delete r[f].label,e.sources.push(r[f]))}if(i.length){e.tracks=[];for(var d=0;d0&&(i[d][n]="true"===i[d][n],i[d].kind=i[d].kind.length?i[d].kind:"captions",i[d].label.length||delete i[d].label,e.tracks.push(i[d]))}return e},f=n(25);function d(t){for(var e={},n=0;n=4.4):null}},function(t,e,n){"use strict";var r=n(3),i=function(){},o=function(){return!1},u={name:"default"},a={supports:o,play:i,pause:i,preload:i,load:i,stop:i,volume:i,mute:i,seek:i,resize:i,remove:i,destroy:i,setVisibility:i,setFullscreen:i,getFullscreen:o,supportsFullscreen:o,getContainer:i,setContainer:i,getName:function(){return u},getQualityLevels:i,getCurrentQuality:i,setCurrentQuality:i,getAudioTracks:i,getCurrentAudioTrack:i,setCurrentAudioTrack:i,getSeekRange:function(){return{start:0,end:this.getDuration()}},setPlaybackRate:i,getPlaybackRate:function(){return 1},getBandwidthEstimate:function(){return null},getLiveLatency:function(){return null},setControls:i,attachMedia:i,detachMedia:i,init:i,setState:function(t){this.state=t,this.trigger(r.bb,{newstate:t})},sendMediaType:function(t){var e=t[0],n=e.type,i=e.mimeType,o="aac"===n||"mp3"===n||"mpeg"===n||i&&0===i.indexOf("audio/");this.trigger(r.T,{mediaType:o?"audio":"video"})}};e.a=a},function(t,e,n){"use strict";var r,i=n(49),o=n(8),u=n(5),a=[],c=[],s=[],l={},f="screen"in window&&"orientation"in window.screen,d=o.OS.android&&o.Browser.chrome,p=!1;function h(t,e){for(var n=e.length;n--;){var r=e[n];if(t.target===r.getContainer()){r.setIntersection(t);break}}}function v(){a.forEach((function(t){var e=t.model;if(!(e.get("audioMode")||!e.get("controls")||e.get("visibility")<.75)){var n=e.get("state"),r=Object(u.f)();!r&&"paused"===n&&t.api.getFullscreen()?t.api.setFullscreen(!1):"playing"===n&&t.api.setFullscreen(r)}}))}function g(){a.forEach((function(t){t.model.set("activeTab",Object(i.a)())}))}function b(t,e){var n=e.indexOf(t);-1!==n&&e.splice(n,1)}function m(t){s.forEach((function(e){e(t)}))}document.addEventListener("visibilitychange",g),document.addEventListener("webkitvisibilitychange",g),d&&f&&window.screen.orientation.addEventListener("change",v),window.addEventListener("beforeunload",(function(){document.removeEventListener("visibilitychange",g),document.removeEventListener("webkitvisibilitychange",g),window.removeEventListener("scroll",m),d&&f&&window.screen.orientation.removeEventListener("change",v)})),e.a={add:function(t){a.push(t)},remove:function(t){b(t,a)},addScrollHandler:function(t){p||(p=!0,window.addEventListener("scroll",m)),s.push(t)},removeScrollHandler:function(t){var e=s.indexOf(t);-1!==e&&s.splice(e,1)},addWidget:function(t){c.push(t)},removeWidget:function(t){b(t,c)},size:function(){return a.length},observe:function(t){var e;e=window.IntersectionObserver,r||(r=new e((function(t){if(t&&t.length)for(var e=t.length;e--;){var n=t[e];h(n,a),h(n,c)}}),{threshold:[0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1]})),l[t.id]||(l[t.id]=!0,r.observe(t))},unobserve:function(t){r&&l[t.id]&&(delete l[t.id],r.unobserve(t))}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return f}));var r=n(0),i=n(42),o=n(5),u=n(10);function a(t,e){for(var n=0;n
    '),d=f.firstChild,p=d.firstChild,h=d.nextSibling;Object(u.d)([d,h],Object(r.g)({overflow:"auto"},a,s)),Object(u.d)(f,Object(r.g)({},a,s)),this.expandElement=d,this.expandChild=p,this.contractElement=h,this.hiddenElement=f,this.element=e,this.view=n,this.model=i,this.width=0,this.resized=!1,e.firstChild?e.insertBefore(f,e.firstChild):e.appendChild(f),e.addEventListener("scroll",l,!0),c.push(this),l()}var e,n,i;return e=t,(n=[{key:"destroy",value:function(){if(this.view){var t=c.indexOf(this);-1!==t&&c.splice(t,1),this.element.removeEventListener("scroll",l,!0),this.element.removeChild(this.hiddenElement),this.view=this.model=null}}}])&&a(e.prototype,n),i&&a(e,i),t}()},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}n.r(e);var i=setTimeout;function o(){}function u(t){if(!(this instanceof u))throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],d(t,this)}function a(t,e){for(;3===t._state;)t=t._value;0!==t._state?(t._handled=!0,u._immediateFn((function(){var n=1===t._state?e.onFulfilled:e.onRejected;if(null!==n){var r;try{r=n(t._value)}catch(t){return void s(e.promise,t)}c(e.promise,r)}else(1===t._state?c:s)(e.promise,t._value)}))):t._deferreds.push(e)}function c(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"===r(e)||"function"==typeof e)){var n=e.then;if(e instanceof u)return t._state=3,t._value=e,void l(t);if("function"==typeof n)return void d((i=n,o=e,function(){i.apply(o,arguments)}),t)}t._state=1,t._value=e,l(t)}catch(e){s(t,e)}var i,o}function s(t,e){t._state=2,t._value=e,l(t)}function l(t){2===t._state&&0===t._deferreds.length&&u._immediateFn((function(){t._handled||u._unhandledRejectionFn(t._value)}));for(var e=0,n=t._deferreds.length;e2&&void 0!==arguments[2]?arguments[2]:[];if(O.a.debug)return t.apply(e||this,n);try{return t.apply(e||this,n)}catch(e){return new _(t.name,e)}},Error:_,Timer:P.a,log:R.a,genId:D.b,between:N.a,foreach:function(t,e){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e(n,t[n])},flashVersion:F.a,isIframe:F.m,indexOf:j.l,trim:A.i,pad:A.e,extension:A.a,hms:A.b,seconds:A.g,prefix:A.f,suffix:A.h,noop:function(){}}),q=0;function z(t,e){var n=new x.a(e);return n.on(C.gb,(function(e){t._qoe.tick("ready"),e.setupTime=t._qoe.between("setup","ready")})),n.on("all",(function(e,n){t.trigger(e,n)})),n}function V(t,e){var n=t.plugins;Object.keys(n).forEach((function(t){delete n[t]})),e.get("setupConfig")&&t.trigger("remove"),t.off(),e.playerDestroy(),e.getContainer().removeAttribute("data-jwplayer-id")}function Q(t){var e=++q,n=t.id||"player-".concat(e),r=new P.a,i={},o=z(this,t);r.tick("init"),t.setAttribute("data-jwplayer-id",n),Object.defineProperties(this,{id:{enumerable:!0,get:function(){return n}},uniqueId:{enumerable:!0,get:function(){return e}},plugins:{enumerable:!0,get:function(){return i}},_qoe:{enumerable:!0,get:function(){return r}},version:{enumerable:!0,get:function(){return w.a}},Events:{enumerable:!0,get:function(){return S.a}},utils:{enumerable:!0,get:function(){return B}},_:{enumerable:!0,get:function(){return j.c}}}),Object(j.g)(this,{_events:{},setup:function(e){return r.clear("ready"),r.tick("setup"),V(this,o),(o=z(this,t)).init(e,this),this.on(e.events,null,this)},remove:function(){return function(t){for(var e=v.a.length;e--;)if(v.a[e].uniqueId===t.uniqueId){v.a.splice(e,1);break}}(this),V(this,o),this},qoe:function(){var t=o.getItemQoe();return{setupTime:this._qoe.between("setup","ready"),firstFrame:t.getFirstFrame?t.getFirstFrame():null,player:this._qoe.dump(),item:t.dump()}},addCues:function(t){return Array.isArray(t)&&o.addCues(t),this},getAudioTracks:function(){return o.getAudioTracks()},getBuffer:function(){return o.get("buffer")},getCaptions:function(){return o.get("captions")},getCaptionsList:function(){return o.getCaptionsList()},getConfig:function(){return o.getConfig()},getContainer:function(){return o.getContainer()},getControls:function(){return o.get("controls")},getCues:function(){return o.get("cues")},getCurrentAudioTrack:function(){return o.getCurrentAudioTrack()},getCurrentCaptions:function(){return o.getCurrentCaptions()},getCurrentQuality:function(){return o.getCurrentQuality()},getCurrentTime:function(){return o.get("currentTime")},getDuration:function(){return o.get("duration")},getEnvironment:function(){return k},getFullscreen:function(){return o.get("fullscreen")},getHeight:function(){return o.getHeight()},getItemMeta:function(){return o.get("itemMeta")||{}},getMute:function(){return o.getMute()},getPlaybackRate:function(){return o.get("playbackRate")},getPlaylist:function(){return o.get("playlist")},getPlaylistIndex:function(){return o.get("item")},getPlaylistItem:function(t){if(!B.exists(t))return o.get("playlistItem");var e=this.getPlaylist();return e?e[t]:null},getPosition:function(){return o.get("position")},getProvider:function(){return o.getProvider()},getQualityLevels:function(){return o.getQualityLevels()},getSafeRegion:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return o.getSafeRegion(t)},getState:function(){return o.getState()},getStretching:function(){return o.get("stretching")},getViewable:function(){return o.get("viewable")},getVisualQuality:function(){return o.getVisualQuality()},getVolume:function(){return o.get("volume")},getWidth:function(){return o.getWidth()},setCaptions:function(t){return o.setCaptions(t),this},setConfig:function(t){return o.setConfig(t),this},setControls:function(t){return o.setControls(t),this},setCurrentAudioTrack:function(t){o.setCurrentAudioTrack(t)},setCurrentCaptions:function(t){o.setCurrentCaptions(t)},setCurrentQuality:function(t){o.setCurrentQuality(t)},setFullscreen:function(t){return o.setFullscreen(t),this},setMute:function(t){return o.setMute(t),this},setPlaybackRate:function(t){return o.setPlaybackRate(t),this},setPlaylistItem:function(t,e){return o.setPlaylistItem(t,e),this},setCues:function(t){return Array.isArray(t)&&o.setCues(t),this},setVolume:function(t){return o.setVolume(t),this},load:function(t,e){return o.load(t,e),this},play:function(t){return o.play(t),this},pause:function(t){return o.pause(t),this},playToggle:function(t){switch(this.getState()){case C.pb:case C.jb:return this.pause(t);default:return this.play(t)}},seek:function(t,e){return o.seek(t,e),this},playlistItem:function(t,e){return o.playlistItem(t,e),this},playlistNext:function(t){return o.playlistNext(t),this},playlistPrev:function(t){return o.playlistPrev(t),this},next:function(t){return o.next(t),this},castToggle:function(){return o.castToggle(),this},createInstream:function(){return o.createInstream()},stop:function(){return o.stop(),this},resize:function(t,e){return o.resize(t,e),this},addButton:function(t,e,n,r,i){return o.addButton(t,e,n,r,i),this},removeButton:function(t){return o.removeButton(t),this},attachMedia:function(){return o.attachMedia(),this},detachMedia:function(){return o.detachMedia(),this},isBeforeComplete:function(){return o.isBeforeComplete()},isBeforePlay:function(){return o.isBeforePlay()}})}Object(j.g)(Q.prototype,{on:function(t,e,n){return S.c.call(this,t,e,n)},once:function(t,e,n){return S.d.call(this,t,e,n)},off:function(t,e,n){return S.b.call(this,t,e,n)},trigger:function(t,e){return(e=j.c.isObject(e)?Object(j.g)({},e):{}).type=t,O.a.debug?S.e.call(this,t,e):S.f.call(this,t,e)},getPlugin:function(t){return this.plugins[t]},addPlugin:function(t,e){this.plugins[t]=e,this.on("ready",e.addToPlayer),e.resize&&this.on("resize",e.resizeHandler)},registerPlugin:function(t,e,n){Object(y.b)(t,e,n)},getAdBlock:function(){return!1},playAd:function(t){},pauseAd:function(t){},skipAd:function(){}}),n.d(e,"assignLibraryProperties",(function(){return H})),n.p=Object(h.loadFrom)();var W=function(t){var e,n;if(t?"string"==typeof t?(e=X(t))||(n=document.getElementById(t)):"number"==typeof t?e=v.a[t]:t.nodeType&&(e=X((n=t).id||n.getAttribute("data-jwplayer-id"))):e=v.a[0],e)return e;if(n){var r=new Q(n);return v.a.push(r),r}return{registerPlugin:y.b}};function X(t){for(var e=0;e2;if(null==t&&(t=[]),p&&t.reduce===p)return r&&(e=G(e,r)),i?t.reduce(e,n):t.reduce(e);if(k(t,(function(t,o,u){i?n=e.call(r,n,t,o,u):(n=t,i=!0)})),!i)throw new TypeError(S);return n},T=E,A=E,_=function(t,e,n){var r;return R(t,(function(t,i,o){if(e.call(n,t,i,o))return r=t,!0})),r},F=_,L=function(t,e,n){var r=[];return null==t?r:v&&t.filter===v?t.filter(e,n):(k(t,(function(t,i,o){e.call(n,t,i,o)&&r.push(t)})),r)},I=L,M=function(t,e,n){e||(e=kt);var r=!0;return null==t?r:g&&t.every===g?t.every(e,n):(k(t,(function(t,o,u){if(!(r=r&&e.call(n,t,o,u)))return i})),!!r)},N=M,R=function(t,e,n){e||(e=kt);var r=!1;return null==t?r:b&&t.some===b?t.some(e,n):(k(t,(function(t,o,u){if(r||(r=e.call(n,t,o,u)))return i})),!!r)},D=R,B=function(t,e){var n;return function(){return--t>0&&(n=e.apply(this,arguments)),t<=1&&(e=null),n}},q=function(t){return null==t?kt:vt(t)?t:xt(t)},z=function(t){return function(e,n,r){var i={};return n=q(n),k(e,(function(o,u){var a=n.call(r,o,u,e);t(i,a,o)})),i}},V=z((function(t,e,n){Ot(t,e)?t[e].push(n):t[e]=[n]})),Q=z((function(t,e,n){t[e]=n})),W=function(t,e,n,r){for(var i=(n=q(n)).call(r,e),o=0,u=t.length;o>>1;n.call(r,t[a])=0)},H=X,Y=function(t,e){return _(t,Ct(e))},U=function(t){var e=s.apply(o,c.call(arguments,1));return L(t,(function(t){return!X(e,t)}))},J=function(t,e,n){if(null==t)return-1;var r=0,i=t.length;if(n){if("number"!=typeof n)return t[r=W(t,e)]===e?r:-1;r=n<0?Math.max(0,i+n):n}if(m&&t.indexOf===m)return t.indexOf(e,n);for(;r1&&void 0!==arguments[1]?arguments[1]:100;return function(){for(var r=this,i=arguments.length,o=new Array(i),u=0;ui&&(r=t,i=a)})),r},memoize:tt,now:Pt,omit:function(t){var e={},n=s.apply(o,c.call(arguments,1));for(var r in t)X(n,r)||(e[r]=t[r]);return e},once:Z,partial:K,pick:st,pluck:function(t,e){return C(t,xt(e))},property:xt,propertyOf:function(t){return null==t?function(){}:function(e){return t[e]}},reduce:E,reject:function(t,e,n){return L(t,(function(t,r,i){return!e.call(n,t,r,i)}),n)},result:function(t,e){if(null!=t){var n=t[e];return vt(n)?n.call(t):n}},select:I,size:function(t){return null==t?0:t.length===+t.length?t.length:it(t).length},some:D,sortedIndex:W,throttle:rt,where:function(t,e){return L(t,Ct(e))},without:function(t){return U(t,c.call(arguments,1))}}},function(t,e,n){"use strict";n.d(e,"t",(function(){return o})),n.d(e,"s",(function(){return u})),n.d(e,"r",(function(){return a})),n.d(e,"o",(function(){return c})),n.d(e,"p",(function(){return s})),n.d(e,"a",(function(){return l})),n.d(e,"c",(function(){return f})),n.d(e,"q",(function(){return d})),n.d(e,"d",(function(){return p})),n.d(e,"h",(function(){return h})),n.d(e,"e",(function(){return v})),n.d(e,"b",(function(){return O})),n.d(e,"f",(function(){return k})),n.d(e,"g",(function(){return x})),n.d(e,"k",(function(){return C})),n.d(e,"i",(function(){return P})),n.d(e,"j",(function(){return S})),n.d(e,"l",(function(){return E})),n.d(e,"m",(function(){return T})),n.d(e,"n",(function(){return A})),n.d(e,"v",(function(){return _})),n.d(e,"u",(function(){return F})),n.d(e,"w",(function(){return L}));var r=n(0);function i(t,e){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:null;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.code=Object(r.u)(n)?n:0,this.sourceError=i,e&&(this.key=e)}var e,n,o;return e=t,o=[{key:"logMessage",value:function(t){var e=t%1e3,n=Math.floor((t-e)/1e3),r=t;return e>=400&&e<600&&(r="".concat(n,"400-").concat(n,"599")),"JW Player ".concat(t>299999&&t<4e5?"Warning":"Error"," ").concat(t,". For more information see https://developer.jwplayer.com/jw-player/docs/developer-guide/api/errors-reference#").concat(r)}}],(n=null)&&i(e.prototype,n),o&&i(e,o),t}();function _(t,e,n){return n instanceof A&&n.code?n:new A(t,e,n)}function F(t,e){var n=_(T,e,t);return n.code=(t&&t.code||0)+e,n}function L(t){var e=t.name,n=t.message;switch(e){case"AbortError":return/pause/.test(n)?y:/load/.test(n)?m:b;case"NotAllowedError":return w;case"NotSupportedError":return j;default:return g}}},function(t,e,n){"use strict";n.d(e,"i",(function(){return o})),n.d(e,"e",(function(){return u})),n.d(e,"j",(function(){return a})),n.d(e,"a",(function(){return c})),n.d(e,"b",(function(){return s})),n.d(e,"g",(function(){return l})),n.d(e,"d",(function(){return f})),n.d(e,"f",(function(){return d})),n.d(e,"h",(function(){return p})),n.d(e,"c",(function(){return h}));var r=n(0),i=window.parseFloat;function o(t){return t.replace(/^\s+|\s+$/g,"")}function u(t,e,n){for(t=""+t,n=n||"0";t.length-1?t.substr(t.lastIndexOf(".")+1,t.length).toLowerCase():void 0}function s(t){var e=(t/60|0)%60,n=t%60;return u(t/3600|0,2)+":"+u(e,2)+":"+u(n.toFixed(3),6)}function l(t,e){if(!t)return 0;if(Object(r.u)(t))return t;var n=t.replace(",","."),o=n.slice(-1),u=n.split(":"),a=u.length,c=0;if("s"===o)c=i(n);else if("m"===o)c=60*i(n);else if("h"===o)c=3600*i(n);else if(a>1){var s=a-1;4===a&&(e&&(c=i(u[s])/e),s-=1),c+=i(u[s]),c+=60*i(u[s-1]),a>=3&&(c+=3600*i(u[s-2]))}else c=i(n);return Object(r.u)(c)?c:0}function f(t,e,n){if(Object(r.s)(t)&&"%"===t.slice(-1)){var o=i(t);return e&&Object(r.u)(e)&&Object(r.u)(o)?e*o/100:null}return l(t,n)}function d(t,e){return t.map((function(t){return e+t}))}function p(t,e){return t.map((function(t){return t+e}))}function h(t){return"string"==typeof t&&"%"===t.slice(-1)}},function(t,e,n){"use strict";n.d(e,"jb",(function(){return r})),n.d(e,"mb",(function(){return i})),n.d(e,"kb",(function(){return o})),n.d(e,"ob",(function(){return u})),n.d(e,"pb",(function(){return a})),n.d(e,"lb",(function(){return c})),n.d(e,"nb",(function(){return s})),n.d(e,"qb",(function(){return l})),n.d(e,"s",(function(){return f})),n.d(e,"u",(function(){return d})),n.d(e,"t",(function(){return p})),n.d(e,"n",(function(){return h})),n.d(e,"q",(function(){return v})),n.d(e,"rb",(function(){return g})),n.d(e,"r",(function(){return b})),n.d(e,"Z",(function(){return m})),n.d(e,"W",(function(){return y})),n.d(e,"v",(function(){return w})),n.d(e,"Y",(function(){return j})),n.d(e,"w",(function(){return O})),n.d(e,"tb",(function(){return k})),n.d(e,"a",(function(){return x})),n.d(e,"b",(function(){return C})),n.d(e,"c",(function(){return P})),n.d(e,"d",(function(){return S})),n.d(e,"e",(function(){return E})),n.d(e,"h",(function(){return T})),n.d(e,"F",(function(){return A})),n.d(e,"gb",(function(){return _})),n.d(e,"Q",(function(){return F})),n.d(e,"C",(function(){return L})),n.d(e,"B",(function(){return I})),n.d(e,"E",(function(){return M})),n.d(e,"p",(function(){return N})),n.d(e,"cb",(function(){return R})),n.d(e,"m",(function(){return D})),n.d(e,"G",(function(){return B})),n.d(e,"H",(function(){return q})),n.d(e,"N",(function(){return z})),n.d(e,"O",(function(){return V})),n.d(e,"R",(function(){return Q})),n.d(e,"ib",(function(){return W})),n.d(e,"bb",(function(){return X})),n.d(e,"D",(function(){return H})),n.d(e,"S",(function(){return Y})),n.d(e,"P",(function(){return U})),n.d(e,"T",(function(){return J})),n.d(e,"V",(function(){return $})),n.d(e,"M",(function(){return G})),n.d(e,"L",(function(){return K})),n.d(e,"K",(function(){return Z})),n.d(e,"I",(function(){return tt})),n.d(e,"J",(function(){return et})),n.d(e,"U",(function(){return nt})),n.d(e,"o",(function(){return rt})),n.d(e,"y",(function(){return it})),n.d(e,"hb",(function(){return ot})),n.d(e,"db",(function(){return ut})),n.d(e,"eb",(function(){return at})),n.d(e,"f",(function(){return ct})),n.d(e,"g",(function(){return st})),n.d(e,"ab",(function(){return lt})),n.d(e,"A",(function(){return ft})),n.d(e,"l",(function(){return dt})),n.d(e,"k",(function(){return pt})),n.d(e,"fb",(function(){return ht})),n.d(e,"sb",(function(){return vt})),n.d(e,"z",(function(){return gt})),n.d(e,"j",(function(){return bt})),n.d(e,"X",(function(){return mt})),n.d(e,"i",(function(){return yt})),n.d(e,"x",(function(){return wt}));var r="buffering",i="idle",o="complete",u="paused",a="playing",c="error",s="loading",l="stalled",f="drag",d="dragStart",p="dragEnd",h="click",v="doubleClick",g="tap",b="doubleTap",m="over",y="move",w="enter",j="out",O=c,k="warning",x="adClick",C="adPause",P="adPlay",S="adSkipped",E="adTime",T="autostartNotAllowed",A=o,_="ready",F="seek",L="beforePlay",I="beforeComplete",M="bufferFull",N="displayClick",R="playlistComplete",D="cast",B="mediaError",q="firstFrame",z="playAttempt",V="playAttemptFailed",Q="seeked",W="setupError",X="state",H="bufferChange",Y="time",U="ratechange",J="mediaType",$="volume",G="mute",K="metadataCueParsed",Z="meta",tt="levels",et="levelsChanged",nt="visualQuality",rt="controls",it="fullscreen",ot="resize",ut="playlistItem",at="playlist",ct="audioTracks",st="audioTrackChanged",lt="playbackRateChanged",ft="logoClick",dt="captionsList",pt="captionsChanged",ht="providerFirstFrame",vt="userAction",gt="instreamClick",bt="breakpoint",mt="fullscreenchange",yt="bandwidthEstimate",wt="float"},function(t,e,n){"use strict";n.d(e,"b",(function(){return i})),n.d(e,"d",(function(){return o})),n.d(e,"a",(function(){return u})),n.d(e,"c",(function(){return a}));var r=n(2);function i(t){var e="";return t&&(t.localName?e=t.localName:t.baseName&&(e=t.baseName)),e}function o(t){var e="";return t&&(t.textContent?e=Object(r.i)(t.textContent):t.text&&(e=Object(r.i)(t.text))),e}function u(t,e){return t.childNodes[e]}function a(t){return t.childNodes?t.childNodes.length:0}},function(t,e,n){"use strict";n.d(e,"i",(function(){return a})),n.d(e,"e",(function(){return c})),n.d(e,"q",(function(){return s})),n.d(e,"j",(function(){return l})),n.d(e,"s",(function(){return f})),n.d(e,"r",(function(){return d})),n.d(e,"u",(function(){return p})),n.d(e,"d",(function(){return g})),n.d(e,"a",(function(){return b})),n.d(e,"o",(function(){return m})),n.d(e,"p",(function(){return y})),n.d(e,"v",(function(){return w})),n.d(e,"t",(function(){return j})),n.d(e,"h",(function(){return O})),n.d(e,"b",(function(){return k})),n.d(e,"g",(function(){return x})),n.d(e,"c",(function(){return C})),n.d(e,"m",(function(){return P})),n.d(e,"k",(function(){return S})),n.d(e,"n",(function(){return E})),n.d(e,"l",(function(){return T})),n.d(e,"f",(function(){return A}));var r,i=n(0),o=n(2),u=n(8);function a(t,e){return t.classList.contains(e)}function c(t){return l(t).firstChild}function s(t,e){O(t),function(t,e){if(!e)return;for(var n=document.createDocumentFragment(),r=l(e).childNodes,i=0;i0?"":"px")}function h(t){return Object(i.s)(t.className)?t.className.split(" "):[]}function v(t,e){e=Object(o.i)(e),t.className!==e&&(t.className=e)}function g(t){return t.classList?t.classList:h(t)}function b(t,e){var n=h(t);(Array.isArray(e)?e:e.split(" ")).forEach((function(t){Object(i.b)(n,t)||n.push(t)})),v(t,n.join(" "))}function m(t,e){var n=h(t),r=Array.isArray(e)?e:e.split(" ");v(t,Object(i.e)(n,r).join(" "))}function y(t,e,n){var r=t.className||"";e.test(r)?r=r.replace(e,n):n&&(r+=" "+n),v(t,r)}function w(t,e,n){var r=a(t,e);(n=Object(i.n)(n)?n:!r)!==r&&(n?b(t,e):m(t,e))}function j(t,e,n){t.setAttribute(e,n)}function O(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function k(t){var e=document.createElement("link");e.rel="stylesheet",e.href=t,document.getElementsByTagName("head")[0].appendChild(e)}function x(t){t&&O(t)}function C(t){var e={left:0,right:0,width:0,height:0,top:0,bottom:0};if(!t||!document.body.contains(t))return e;var n=t.getBoundingClientRect(),r=window.pageYOffset,i=window.pageXOffset;return n.width||n.height||n.left||n.top?(e.left=n.left+i,e.right=n.right+i,e.top=n.top+r,e.bottom=n.bottom+r,e.width=n.right-n.left,e.height=n.bottom-n.top,e):e}function P(t,e){t.insertBefore(e,t.firstChild)}function S(t){return t.nextElementSibling}function E(t){return t.previousElementSibling}function T(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=document.createElement("a");r.href=t,r.target=e,r=Object(i.g)(r,n),u.Browser.firefox?r.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,view:window})):r.click()}function A(){var t=window.screen.orientation;return!!t&&("landscape-primary"===t.type||"landscape-secondary"===t.type)||90===window.orientation||-90===window.orientation}},function(t,e,n){"use strict";n.d(e,"h",(function(){return u})),n.d(e,"f",(function(){return a})),n.d(e,"l",(function(){return s})),n.d(e,"k",(function(){return f})),n.d(e,"p",(function(){return d})),n.d(e,"g",(function(){return p})),n.d(e,"e",(function(){return h})),n.d(e,"n",(function(){return v})),n.d(e,"d",(function(){return g})),n.d(e,"i",(function(){return b})),n.d(e,"q",(function(){return m})),n.d(e,"j",(function(){return y})),n.d(e,"c",(function(){return w})),n.d(e,"b",(function(){return j})),n.d(e,"o",(function(){return O})),n.d(e,"m",(function(){return k})),n.d(e,"a",(function(){return x}));var r=navigator.userAgent;function i(t){return null!==r.match(t)}function o(t){return function(){return i(t)}}function u(){var t=x();return!!(t&&t>=18)}var a=o(/gecko\//i),c=o(/trident\/.+rv:\s*11/i),s=o(/iP(hone|od)/i),l="MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,f=function(){return i(/iPad/i)||l},d=function(){return i(/Macintosh/i)&&!l},p=o(/FBAV/i);function h(){return i(/\sEdge\/\d+/i)}function v(){return i(/msie/i)}function g(){return i(/\s(?:(?:Headless)?Chrome|CriOS)\//i)&&!h()&&!i(/UCBrowser/i)}function b(){return h()||c()||v()}function m(){return i(/safari/i)&&!i(/(?:Chrome|CriOS|chromium|android|phantom)/i)}function y(){return i(/iP(hone|ad|od)/i)||l}function w(){return!(i(/chrome\/[123456789]/i)&&!i(/chrome\/18/i)&&!a())&&j()}function j(){return i(/Android/i)&&!i(/Windows Phone/i)}function O(){return y()||j()||i(/Windows Phone/i)}function k(){try{return window.self!==window.top}catch(t){return!0}}function x(){if(j())return 0;var t,e=navigator.plugins;if(e&&(t=e["Shockwave Flash"])&&t.description)return parseFloat(t.description.replace(/\D+(\d+\.?\d*).*/,"$1"));if(void 0!==window.ActiveXObject){try{if(t=new window.ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseFloat(t.GetVariable("$version").split(" ")[1].replace(/\s*,\s*/,"."))}catch(t){return 0}return t}return 0}},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t,e){for(var n=0;ne)return t[e]}var o=n(0);n.d(e,"Browser",(function(){return a})),n.d(e,"OS",(function(){return c})),n.d(e,"Features",(function(){return s}));var u=navigator.userAgent;var a={},c={},s={};Object.defineProperties(a,{androidNative:{get:Object(o.x)(r.c),enumerable:!0},chrome:{get:Object(o.x)(r.d),enumerable:!0},edge:{get:Object(o.x)(r.e),enumerable:!0},facebook:{get:Object(o.x)(r.g),enumerable:!0},firefox:{get:Object(o.x)(r.f),enumerable:!0},ie:{get:Object(o.x)(r.i),enumerable:!0},msie:{get:Object(o.x)(r.n),enumerable:!0},safari:{get:Object(o.x)(r.q),enumerable:!0},version:{get:Object(o.x)(function(t,e){var n,r,i,o;return t.chrome?n=-1!==e.indexOf("Chrome")?e.substring(e.indexOf("Chrome")+7):e.substring(e.indexOf("CriOS")+6):t.safari?n=e.substring(e.indexOf("Version")+8):t.firefox?n=e.substring(e.indexOf("Firefox")+8):t.edge?n=e.substring(e.indexOf("Edge")+5):t.ie&&(-1!==e.indexOf("rv:")?n=e.substring(e.indexOf("rv:")+3):-1!==e.indexOf("MSIE")&&(n=e.substring(e.indexOf("MSIE")+5))),n&&(-1!==(o=n.indexOf(";"))&&(n=n.substring(0,o)),-1!==(o=n.indexOf(" "))&&(n=n.substring(0,o)),-1!==(o=n.indexOf(")"))&&(n=n.substring(0,o)),r=parseInt(n,10),i=parseInt(n.split(".")[1],10)),{version:n,major:r,minor:i}}.bind(void 0,a,u)),enumerable:!0}}),Object.defineProperties(c,{android:{get:Object(o.x)(r.b),enumerable:!0},iOS:{get:Object(o.x)(r.j),enumerable:!0},mobile:{get:Object(o.x)(r.o),enumerable:!0},mac:{get:Object(o.x)(r.p),enumerable:!0},iPad:{get:Object(o.x)(r.k),enumerable:!0},iPhone:{get:Object(o.x)(r.l),enumerable:!0},windows:{get:Object(o.x)((function(){return u.indexOf("Windows")>-1})),enumerable:!0},version:{get:Object(o.x)(function(t,e){var n,r,o;if(t.windows)switch(n=i(/Windows(?: NT|)? ([._\d]+)/.exec(e),1)){case"6.1":n="7.0";break;case"6.2":n="8.0";break;case"6.3":n="8.1"}else t.android?n=i(/Android ([._\d]+)/.exec(e),1):t.iOS?n=i(/OS ([._\d]+)/.exec(e),1):t.mac&&(n=i(/Mac OS X (10[._\d]+)/.exec(e),1));if(n){r=parseInt(n,10);var u=n.split(/[._]/);u&&(o=parseInt(u[1],10))}return{version:n,major:r,minor:o}}.bind(void 0,c,u)),enumerable:!0}}),Object.defineProperties(s,{flash:{get:Object(o.x)(r.h),enumerable:!0},flashVersion:{get:Object(o.x)(r.a),enumerable:!0},iframe:{get:Object(o.x)(r.m),enumerable:!0},passiveEvents:{get:Object(o.x)((function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){return t=!0}});window.addEventListener("testPassive",null,e),window.removeEventListener("testPassive",null,e)}catch(t){}return t})),enumerable:!0},backgroundLoading:{get:Object(o.x)((function(){return!(c.iOS||a.safari)})),enumerable:!0}})},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}n.r(e),n.d(e,"exists",(function(){return o})),n.d(e,"isHTTPS",(function(){return u})),n.d(e,"isFileProtocol",(function(){return a})),n.d(e,"isRtmp",(function(){return c})),n.d(e,"isYouTube",(function(){return s})),n.d(e,"typeOf",(function(){return l})),n.d(e,"isDeepKeyCompliant",(function(){return f}));var i=window.location.protocol;function o(t){switch(r(t)){case"string":return t.length>0;case"object":return null!==t;case"undefined":return!1;default:return!0}}function u(){return"https:"===i}function a(){return"file:"===i}function c(t,e){return 0===t.indexOf("rtmp:")||"rtmp"===e}function s(t,e){return"youtube"===e||/^(http|\/\/).*(youtube\.com|youtu\.be)\/.+/.test(t)}function l(t){if(null===t)return"null";var e=r(t);return"object"===e&&Array.isArray(t)?"array":e}function f(t,e,n){var i=Object.keys(t);return Object.keys(e).length>=i.length&&i.every((function(i){var o=t[i],u=e[i];return o&&"object"===r(o)?!(!u||"object"!==r(u))&&f(o,u,n):n(i,t)}))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return c})),n.d(e,"b",(function(){return s})),n.d(e,"d",(function(){return l})),n.d(e,"e",(function(){return p})),n.d(e,"c",(function(){return h}));var r=n(2),i=n(39),o=n.n(i);function u(t){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var a,c=o.a.clear;function s(t,e,n,r){n=n||"all-players";var i="";if("object"===u(e)){var a=document.createElement("div");l(a,e);var c=a.style.cssText;Object.prototype.hasOwnProperty.call(e,"content")&&c&&(c="".concat(c,' content: "').concat(e.content,'";')),r&&c&&(c=c.replace(/;/g," !important;")),i="{"+c+"}"}else"string"==typeof e&&(i=e);""!==i&&"{}"!==i?o.a.style([[t,t+i]],n):o.a.clear(n,t)}function l(t,e){if(null!=t){var n;void 0===t.length&&(t=[t]);var r={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=d(n,e[n]));for(var i=0;i-1?t:parseInt(t.replace("px",""),10):t}function l(t,e){if(t<=0&&!e||Object(i.q)(parseInt(t)))return"00:00";var n=t<0?"-":"";t=Math.abs(t);var r=Math.floor(t/3600),o=Math.floor((t-3600*r)/60),u=Math.floor(t%60);return n+(r?r+":":"")+(o<10?"0":"")+o+":"+(u<10?"0":"")+u}},function(t,e,n){"use strict";e.a=[]},function(t,e,n){"use strict";n.d(e,"h",(function(){return d})),n.d(e,"c",(function(){return h})),n.d(e,"e",(function(){return g})),n.d(e,"f",(function(){return b})),n.d(e,"b",(function(){return m})),n.d(e,"d",(function(){return w})),n.d(e,"g",(function(){return j})),n.d(e,"a",(function(){return O}));var r=n(0),i=n(6),o=n(22),u=n(9),a=n(33),c={},s={zh:"Chinese",nl:"Dutch",en:"English",fr:"French",de:"German",it:"Italian",ja:"Japanese",pt:"Portuguese",ru:"Russian",es:"Spanish",el:"Greek",fi:"Finnish",id:"Indonesian",ko:"Korean",th:"Thai",vi:"Vietnamese"};Object(r.m)(s);function l(t){var e=f(t),n=e.indexOf("_");return-1===n?e:e.substring(0,n)}function f(t){return t.toLowerCase().replace("-","_")}function d(t){return t?Object.keys(t).reduce((function(e,n){return e[f(n)]=t[n],e}),{}):{}}function p(t){var e=t.querySelector("html");return e?e.getAttribute("lang"):null}function h(){var t=p(document);if(!t&&Object(i.m)())try{t=p(window.top.document)}catch(t){}return t||navigator.language||"en"}var v=["ar","da","de","el","es","fi","fr","he","id","it","ja","ko","nl","no","oc","pt","ro","ru","sl","sv","th","tr","vi","zh"];function g(t){return 8207===t.charCodeAt(0)||/^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.test(t)}function b(t){return v.indexOf(l(t))>=0}function m(t,e,n){return Object(r.g)({},function(t){var e=t.advertising,n=t.related,i=t.sharing,o=t.abouttext,u=Object(r.g)({},t.localization);e&&(u.advertising=u.advertising||{},y(u.advertising,e,"admessage"),y(u.advertising,e,"cuetext"),y(u.advertising,e,"loadingAd"),y(u.advertising,e,"podmessage"),y(u.advertising,e,"skipmessage"),y(u.advertising,e,"skiptext"));"string"==typeof u.related?u.related={heading:u.related}:u.related=u.related||{};n&&y(u.related,n,"autoplaymessage");i&&(u.sharing=u.sharing||{},y(u.sharing,i,"heading"),y(u.sharing,i,"copied"));o&&y(u,t,"abouttext");var a=u.close||u.nextUpClose;a&&(u.close=a);return u}(t),e[l(n)],e[f(n)])}function y(t,e,n){var r=t[n]||e[n];r&&(t[n]=r)}function w(t){return Object(u.isDeepKeyCompliant)(a.a,t,(function(t,e){return"string"==typeof e[t]}))}function j(t,e){var n=c[e];if(!n){var r="".concat(t,"translations/").concat(l(e),".json");c[e]=n=new Promise((function(t,n){Object(o.a)({url:r,oncomplete:t,onerror:function(t,r,i,o){c[e]=null,n(o)},responseType:"json"})}))}return n}function O(t,e){var n=Object(r.g)({},t,e);return k(n,"errors",t,e),k(n,"related",t,e),k(n,"sharing",t,e),k(n,"advertising",t,e),k(n,"shortcuts",t,e),n}function k(t,e,n,i){t[e]=Object(r.g)({},n[e],i[e])}},function(t,e,n){"use strict";var r=n(52),i=n(9),o=document.createElement("video"),u={aac:"audio/mp4",mp4:"video/mp4",f4v:"video/mp4",m4v:"video/mp4",mov:"video/mp4",mp3:"audio/mpeg",mpeg:"audio/mpeg",ogv:"video/ogg",ogg:"video/ogg",oga:"video/ogg",vorbis:"video/ogg",webm:"video/webm",f4a:"video/aac",m3u8:"application/vnd.apple.mpegurl",m3u:"application/vnd.apple.mpegurl",hls:"application/vnd.apple.mpegurl"},a=[{name:"html5",supports:function(t){if(!1===Object(r.a)(t))return!1;if(!o.canPlayType)return!1;var e=t.file,n=t.type;if(Object(i.isRtmp)(e,n))return!1;var a=t.mimeType||u[n];if(!a)return!1;var c=t.mediaTypes;c&&c.length&&(a=[a].concat(c.slice()).join("; "));return!!o.canPlayType(a)}}];e.a=a},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r=Date.now||function(){return(new Date).getTime()}},function(t,e,n){"use strict";n.d(e,"a",(function(){return l})),n.d(e,"d",(function(){return f})),n.d(e,"b",(function(){return d})),n.d(e,"c",(function(){return p}));var r=n(25),i=n(26),o=n(14),u=n(21),a=n(32),c=n(1),s=null,l={};function f(t){return s||(s=function(t){var e=t.get("controls"),s=h(),f=function(t,e){var n=t.get("playlist");if(Array.isArray(n)&&n.length)for(var u=Object(i.c)(Object(r.a)(n[0]),t),a=0;a=0)return r.substr(0,i+1)}}return""}),o=function(){return i("jwplayer.js")},u=function(t){var e=("0"+t).split(/\W/),n=r.a.split(/\W/),i=parseFloat(e[0]),o=parseFloat(n[0]);return!(i>o)&&!(i===o&&parseFloat("0"+e[1])>parseFloat(n[1]))},a=function(){return i("jwplayer.js")}},function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(28),i=n(14),o=n(53),u=n(0);function a(t){var e=t.getName().name;if(!r.a[e]){if(!Object(u.i)(i.a,Object(u.w)({name:e}))){if(!Object(u.p)(t.supports))throw new Error("Tried to register a provider with an invalid object");i.a.unshift({name:e,supports:t.supports})}Object(u.d)(t.prototype,o.a),r.a[e]=t}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return m}));var r=n(0),i=n(11),o=n(9),u=n(1),a=1,c=2,s=3,l=4,f=5,d=6,p=7,h=601,v=602,g=611,b=function(){};function m(t,e,n,h){var O;t===Object(t)&&(t=(h=t).url);var k=Object(r.g)({xhr:null,url:t,withCredentials:!1,retryWithoutCredentials:!1,timeout:6e4,timeoutId:-1,oncomplete:e||b,onerror:n||b,mimeType:h&&!h.responseType?"text/xml":"",requireValidXML:!1,responseType:h&&h.plainText?"text":"",useDomParser:!1,requestFilter:null},h),x=function(t,e){return function(t,n){var i=t.currentTarget||e.xhr;if(clearTimeout(e.timeoutId),e.retryWithoutCredentials&&e.xhr.withCredentials)return y(i),void m(Object(r.g)({},e,{xhr:null,withCredentials:!1,retryWithoutCredentials:!1}));!n&&i.status>=400&&i.status<600&&(n=i.status),w(e,n?u.k:u.m,n||d,t)}}(0,k);if("XMLHttpRequest"in window){if(O=k.xhr=k.xhr||new window.XMLHttpRequest,"function"==typeof k.requestFilter){var C;try{C=k.requestFilter({url:t,xhr:O})}catch(t){return x(t,f),O}C&&"open"in C&&"send"in C&&(O=k.xhr=C)}O.onreadystatechange=function(t){return function(e){var n=e.currentTarget||t.xhr;if(4===n.readyState){clearTimeout(t.timeoutId);var a=n.status;if(a>=400)return void w(t,u.k,a<600?a:d);if(200===a)return function(t){return function(e){var n=e.currentTarget||t.xhr;if(clearTimeout(t.timeoutId),t.responseType){if("json"===t.responseType)return function(t,e){if(!t.response||"string"==typeof t.response&&'"'!==t.responseText.substr(1))try{t=Object(r.g)({},t,{response:JSON.parse(t.responseText)})}catch(t){return void w(e,u.k,g,t)}return e.oncomplete(t)}(n,t)}else{var o,a=n.responseXML;if(a)try{o=a.firstChild}catch(t){}if(a&&o)return j(n,a,t);if(t.useDomParser&&n.responseText&&!a&&(a=Object(i.parseXML)(n.responseText))&&a.firstChild)return j(n,a,t);if(t.requireValidXML)return void w(t,u.k,v)}t.oncomplete(n)}}(t)(e);0===a&&Object(o.isFileProtocol)()&&!/^[a-z][a-z0-9+.-]*:/.test(t.url)&&w(t,u.k,p)}}}(k),O.onerror=x,"overrideMimeType"in O?k.mimeType&&O.overrideMimeType(k.mimeType):k.useDomParser=!0;try{t=t.replace(/#.*$/,""),O.open("GET",t,!0)}catch(t){return x(t,s),O}if(k.responseType)try{O.responseType=k.responseType}catch(t){}k.timeout&&(k.timeoutId=setTimeout((function(){y(O),w(k,u.m,a)}),k.timeout),O.onabort=function(){clearTimeout(k.timeoutId)});try{k.withCredentials&&"withCredentials"in O&&(O.withCredentials=!0),O.send()}catch(t){x(t,l)}return O}w(k,u.m,c)}function y(t){t.onload=null,t.onprogress=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()}function w(t,e,n,r){t.onerror(e,t.url,t.xhr,new u.n(e,n,r))}function j(t,e,n){var i=e.documentElement;if(!n.requireValidXML||"parsererror"!==i.nodeName&&!i.getElementsByTagName("parsererror").length)return t.responseXML||(t=Object(r.g)({},t,{responseXML:e})),n.oncomplete(t);w(n,u.k,h)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r="8.11.5+local.2020-07-09-13-21-01-936"},function(t,e,n){"use strict";var r=n(0),i=n(15),o=window.performance||{timing:{}},u=o.timing.navigationStart||Object(i.a)();function a(){return u+o.now()}"now"in o||(o.now=function(){return Object(i.a)()-u});e.a=function(){var t={},e={},n={},i={};return{start:function(e){t[e]=a(),n[e]=n[e]+1||1},end:function(n){if(t[n]){var r=a()-t[n];delete t[n],e[n]=e[n]+r||r}},dump:function(){var o=Object(r.g)({},e);for(var u in t)if(Object.prototype.hasOwnProperty.call(t,u)){var c=a()-t[u];o[u]=o[u]+c||c}return{counts:Object(r.g)({},n),sums:o,events:Object(r.g)({},i)}},tick:function(t){i[t]=a()},clear:function(t){delete i[t]},between:function(t,e){return i[e]&&i[t]?i[e]-i[t]:null}}}},function(t,e,n){"use strict";var r=n(0),i=n(29),o=function(t){if(t&&t.file)return Object(r.g)({},{kind:"captions",default:!1},t)},u=Array.isArray;e.a=function(t){u((t=t||{}).tracks)||delete t.tracks;var e=Object(r.g)({},{sources:[],tracks:[],minDvrWindow:120,dvrSeekLimit:25},t);e.sources!==Object(e.sources)||u(e.sources)||(e.sources=[Object(i.a)(e.sources)]),u(e.sources)&&0!==e.sources.length||(t.levels?e.sources=t.levels:e.sources=[Object(i.a)(t)]);for(var n=0;n0)return d;var n=t.indexOf("/"),r=Object(f.a)(t);return!(e<0&&n<0)||r&&isNaN(r)?p:2}};var v=function(t){this.url=t,this.promise_=null};Object.defineProperties(v.prototype,{promise:{get:function(){return this.promise_||this.load()},set:function(){}}}),Object(i.g)(v.prototype,{load:function(){var t=this,e=this.promise_;if(!e){if(2===h(this.url))e=Promise.resolve(this);else{var n=new s.a(function(t){switch(h(t)){case d:return t;case p:return Object(l.getAbsolutePath)(t,window.location.href)}}(this.url));this.loader=n,e=n.load().then((function(){return t}))}this.promise_=e}return e},registerPlugin:function(t,e,n){this.name=t,this.target=e,this.js=n},getNewInstance:function(t,e,n){var i=this.js;if("function"!=typeof i)throw new r.n(null,u(this.url)+100);var o=new i(t,e,n);return o.addToPlayer=function(){var e=t.getContainer().querySelector(".jw-overlays");e&&(n.left=e.style.left,n.top=e.style.top,e.appendChild(n),o.displayArea=e)},o.resizeHandler=function(){var t=o.displayArea;t&&o.resize(t.clientWidth,t.clientHeight)},o}});var g=v,b=n(38),m={},y=function(){},w=y.prototype;w.setupPlugin=function(t){var e=this.getPlugin(t);return e?(e.url!==t&&Object(b.a)('JW Plugin "'.concat(o(t),'" already loaded from "').concat(e.url,'". Ignoring "').concat(t,'."')),e.promise):this.addPlugin(t).load()},w.addPlugin=function(t){var e=o(t),n=m[e];return n||(n=new g(t),m[e]=n),n},w.getPlugin=function(t){return m[o(t)]},w.removePlugin=function(t){delete m[o(t)]},w.getPlugins=function(){return m};var j=y;n.d(e,"b",(function(){return k})),n.d(e,"a",(function(){return x}));var O=new j,k=function(t,e,n){var r=O.addPlugin(t);r.js||r.registerPlugin(t,e,n)};function x(t,e){var n=t.get("plugins");return window.jwplayerPluginJsonp=k,(t.pluginLoader=t.pluginLoader||new c).load(e,O,n,t).then((function(e){if(!t.attributes._destroyed)return delete window.jwplayerPluginJsonp,e}))}},function(t,e,n){"use strict";e.a={}},function(t,e,n){"use strict";var r=n(0),i=n(9),o=n(2);e.a=function(t){if(t&&t.file){var e=Object(r.g)({},{default:!1},t);e.file=Object(o.i)(""+e.file);var n=/^[^/]+\/(?:x-)?([^/]+)$/;if(n.test(e.type)&&(e.mimeType=e.type,e.type=e.type.replace(n,"$1")),Object(i.isYouTube)(e.file)?e.type="youtube":Object(i.isRtmp)(e.file)?e.type="rtmp":e.type||(e.type=Object(o.a)(e.file)),e.type){switch(e.type){case"m3u8":case"vnd.apple.mpegurl":e.type="hls";break;case"dash+xml":e.type="dash";break;case"m4a":e.type="aac";break;case"smil":e.type="rtmp"}return Object.keys(e).forEach((function(t){""===e[t]&&delete e[t]})),e}}}},,,function(t,e,n){"use strict";n.d(e,"a",(function(){return o})),n.d(e,"b",(function(){return u}));var r=n(16),i=null,o={};function u(){return i||(i=n.e(1).then(function(t){var e=n(17).default;return o.controls=e,e}.bind(null,n)).catch((function(){i=null,Object(r.c)(301130)()}))),i}},function(t,e,n){"use strict";e.a={advertising:{admessage:"This ad will end in xx",cuetext:"Advertisement",displayHeading:"Advertisement",loadingAd:"Loading ad",podmessage:"Ad __AD_POD_CURRENT__ of __AD_POD_LENGTH__.",skipmessage:"Skip ad in xx",skiptext:"Skip"},airplay:"AirPlay",audioTracks:"Audio Tracks",auto:"Auto",buffer:"Loading",cast:"Chromecast",cc:"Closed Captions",close:"Close",errors:{badConnection:"This video cannot be played because of a problem with your internet connection.",cantLoadPlayer:"Sorry, the video player failed to load.",cantPlayInBrowser:"The video cannot be played in this browser.",cantPlayVideo:"This video file cannot be played.",errorCode:"Error Code",liveStreamDown:"The live stream is either down or has ended.",protectedContent:"There was a problem providing access to protected content.",technicalError:"This video cannot be played because of a technical error."},exitFullscreen:"Exit Fullscreen",fullscreen:"Fullscreen",hd:"Quality",liveBroadcast:"Live",logo:"Logo",mute:"Mute",next:"Next",nextUp:"Next Up",notLive:"Not Live",off:"Off",pause:"Pause",play:"Play",playback:"Play",playbackRates:"Playback Rates",player:"Video Player",poweredBy:"Powered by",prev:"Previous",related:{autoplaymessage:"Next up in xx",heading:"More Videos"},replay:"Replay",rewind:"Rewind 10 Seconds",settings:"Settings",sharing:{copied:"Copied",email:"Email",embed:"Embed",heading:"Share",link:"Link"},slider:"Seek",stop:"Stop",unmute:"Unmute",videoInfo:"About This Video",volume:"Volume",volumeSlider:"Volume",shortcuts:{playPause:"Play/Pause",volumeToggle:"Mute/Unmute",fullscreenToggle:"Fullscreen/Exit Fullscreen",seekPercent:"Seek %",keyboardShortcuts:"Keyboard Shortcuts",increaseVolume:"Increase Volume",decreaseVolume:"Decrease Volume",seekForward:"Seek Forward",seekBackward:"Seek Backward",spacebar:"SPACE",captionsToggle:"Captions On/Off"}}},function(t,e,n){"use strict";var r=n(0),i=n(14),o=n(21),u=n(28),a=n(16);function c(t){this.config=t||{}}var s={html5:function(){return n.e(9).then(function(t){var e=n(31).default;return Object(o.a)(e),e}.bind(null,n)).catch(Object(a.b)(152))}};Object(r.g)(c.prototype,{load:function(t){var e=s[t],n=function(){return Promise.reject(new Error("Failed to load media"))};return e?e().then((function(){var e=u.a[t];return e||n()})):n()},providerSupports:function(t,e){return t.supports(e)},choose:function(t){if(t===Object(t))for(var e=i.a.length,n=0;n')+'
    '+'
    '.concat(e||"",'').concat(i,"
    ")+"
    "},i=n(5),o=n(10);function u(t,e){var n=e.message,u=e.code,a=r(t.get("id"),n,t.get("localization").errors.errorCode,u),c=t.get("width"),s=t.get("height"),l=Object(i.e)(a);return Object(o.d)(l,{width:c.toString().indexOf("%")>0?c:"".concat(c,"px"),height:s.toString().indexOf("%")>0?s:"".concat(s,"px")}),l}n.d(e,"a",(function(){return u}))},function(t,e,n){"use strict";function r(t){return t.slice&&"px"===t.slice(-2)&&(t=t.slice(0,-2)),t}function i(t,e){if(-1===e.toString().indexOf("%"))return 0;if("string"!=typeof t||!t)return 0;if(/^\d*\.?\d+%$/.test(t))return t;var n=t.indexOf(":");if(-1===n)return 0;var r=parseFloat(t.substr(0,n)),i=parseFloat(t.substr(n+1));return r<=0||i<=0?0:i/r*100+"%"}n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}))},function(t,e,n){"use strict";var r=n(0),i=n(7),o=n(3),u={},a=45e3,c=2,s=3;function l(t){var e=document.createElement("link");return e.type="text/css",e.rel="stylesheet",e.href=t,e}function f(t,e){var n=document.createElement("script");return n.type="text/javascript",n.charset="utf-8",n.async=!0,n.timeout=e||a,n.src=t,n}var d=function(t,e,n){var r=this,i=0;function d(t){i=c,r.trigger(o.w,t).off()}function p(t){i=s,r.trigger(o.kb,t).off()}this.getStatus=function(){return i},this.load=function(){var r=u[t];return 0!==i?r:(r&&r.then(p).catch(d),i=1,r=new Promise((function(r,i){var o=(e?l:f)(t,n),u=function(){o.onerror=o.onload=null,clearTimeout(s)},c=function(t){u(),d(t),i(t)},s=setTimeout((function(){c(new Error("Network timeout ".concat(t)))}),a);o.onerror=function(){c(new Error("Failed to load ".concat(t)))},o.onload=function(t){u(),p(t),r(t)};var h=document.getElementsByTagName("head")[0]||document.documentElement;h.insertBefore(o,h.firstChild)})),u[t]=r,r)}};Object(r.g)(d.prototype,i.a),e.a=d},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r="function"==typeof console.log?console.log.bind(console):function(){}},function(t,e){var n,r,i={},o={},u=(n=function(){return document.head||document.getElementsByTagName("head")[0]},function(){return void 0===r&&(r=n.apply(this,arguments)),r});function a(t){var e=document.createElement("style");return e.type="text/css",e.setAttribute("data-jwplayer-id",t),function(t){u().appendChild(t)}(e),e}function c(t,e){var n,r,i,u=o[t];u||(u=o[t]={element:a(t),counter:0});var c=u.counter++;return n=u.element,i=function(){f(n,c,"")},(r=function(t){f(n,c,t)})(e.css),function(t){if(t){if(t.css===e.css&&t.media===e.media)return;r((e=t).css)}else i()}}t.exports={style:function(t,e){!function(t,e){for(var n=0;nk*k&&(N(t,i.u,e),t.dragged=!0,N(t,i.s,e))}n||"touchmove"!==e.type||D(e)},s=function(n){if(clearTimeout(h),t.el)if(I(t),L(t,y),t.dragged)t.dragged=!1,N(t,i.t,n);else if(-1===n.type.indexOf("cancel")&&e.contains(n.target)){if(Object(u.a)()-t.lastStart>C)return;var r="pointerup"===n.type||"pointercancel"===n.type,o="mouseup"===n.type||r&&"mouse"===n.pointerType;!function(t,e,n){if(t.enableDoubleTap)if(Object(u.a)()-t.lastClick4&&void 0!==arguments[4]?arguments[4]:O,o=t.handlers[e],u=t.options[e];if(o||(o=t.handlers[e]={},u=t.options[e]={}),o[n])throw new Error("".concat(e," ").concat(n," already registered"));o[n]=r,u[n]=i;var a=t.el;(e===y?_(a):a).addEventListener(n,r,i)}function L(t,e){var n=t.el,r=t.handlers,i=t.options,o=e===y?_(n):n,u=r[e],a=i[e];u&&(Object.keys(u).forEach((function(t){var e=a[t];"boolean"==typeof e?o.removeEventListener(t,u[t],e):o.removeEventListener(t,u[t])})),r[e]=null,i[e]=null)}function I(t){var e=t.el;null!==t.pointerId&&(e.releasePointerCapture(t.pointerId),t.pointerId=null)}function M(t,e,n){var r=t.el,i=n.target;t.trigger(e,{type:e,sourceEvent:n,currentTarget:r,target:i})}function N(t,e,n){var r=function(t,e,n){var r,i=e.target,o=e.touches,u=e.changedTouches,a=e.pointerType;o||u?(r=o&&o.length?o[0]:u[0],a=a||"touch"):(r=e,a=a||"mouse");var c=r,s=c.pageX,l=c.pageY;return{type:t,pointerType:a,pageX:s,pageY:l,sourceEvent:e,currentTarget:n,target:i}}(e,n,t.el);t.trigger(e,r)}function R(t){return 0===t.type.indexOf("touch")?(t.originalEvent||t).changedTouches[0]:t}function D(t){t.preventDefault&&t.preventDefault()}},function(t,e,n){"use strict";n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}));var r={audioMode:!1,flashBlocked:!1,item:0,itemMeta:{},playbackRate:1,playRejected:!1,state:n(3).mb,itemReady:!1,controlsEnabled:!1},i={position:0,duration:0,buffer:0,currentTime:0}},function(t,e,n){"use strict";n.d(e,"b",(function(){return r})),n.d(e,"a",(function(){return i}));var r=window.requestAnimationFrame||function(t){return setTimeout(t,17)},i=window.cancelAnimationFrame||clearTimeout},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));var r=function(t,e,n){return Math.max(Math.min(t,n),e)}},function(t,e,n){"use strict";function r(t,e,n){var r=[],i={};function o(){for(;r.length>0;){var e=r.shift(),n=e.command,o=e.args;(i[n]||t[n]).apply(t,o)}}e.forEach((function(e){var u=t[e];i[e]=u,t[e]=function(){var t=Array.prototype.slice.call(arguments,0);n()?r.push({command:e,args:t}):(o(),u&&u.apply(this,t))}})),Object.defineProperty(this,"queue",{enumerable:!0,get:function(){return r}}),this.flush=o,this.empty=function(){r.length=0},this.off=function(){e.forEach((function(e){var n=i[e];n&&(t[e]=n,delete i[e])}))},this.destroy=function(){this.off(),this.empty()}}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t,e){for(var n=0;n=.25&&t<=4})).map((function(t){return Math.round(100*t)/100}))).indexOf(1)<0&&b.push(1),b.sort(),h.playbackRateControls=!0,h.playbackRates=b}(!h.playbackRateControls||h.playbackRates.indexOf(h.defaultPlaybackRate)<0)&&(h.defaultPlaybackRate=1),h.playbackRate=h.defaultPlaybackRate,h.aspectratio||delete h.aspectratio;var m=h.playlist;if(m)Array.isArray(m.playlist)&&(h.feedData=m,h.playlist=m.playlist);else{var y=Object(r.y)(h,["title","description","type","mediaid","image","images","file","sources","tracks","preload","duration"]);h.playlist=[y]}h.qualityLabels=h.qualityLabels||h.hlslabels,delete h.duration;var w=h.liveTimeout;null!==w&&(Object(r.u)(w)?0!==w&&(w=Math.max(30,w)):w=null,h.liveTimeout=w);var j,O,k,x=parseFloat(h.bandwidthEstimate),C=parseFloat(h.bitrateSelection);return h.bandwidthEstimate=Object(r.u)(x)?x:(j=h.defaultBandwidthEstimate,O=parseFloat(j),Object(r.u)(O)?Math.max(O,1):f.bandwidthEstimate),h.bitrateSelection=Object(r.u)(C)?C:f.bitrateSelection,h.liveSyncDuration=(k=h.liveSyncDuration)?k<5?5:k>30?30:k:25,h.backgroundLoading=Object(r.n)(h.backgroundLoading)?h.backgroundLoading:c.Features.backgroundLoading,h},p=n(16),h=n(27),v=n(3),g=n(51),b=n(26),m=n(37),y=n(1);function w(t,e,n){var r=t.attributes;r.playlist=Object(b.a)(e),r.feedData=n}function j(t){return function(t){var e=t.get("playlist");return new Promise((function(n,r){if("string"!=typeof e){var i=t.get("feedData")||{};return w(t,e,i),n()}var o=new g.a;o.on(v.eb,(function(e){var r=e.playlist;delete e.playlist,w(t,r,e),n()})),o.on(v.w,(function(e){w(t,[],{}),r(Object(y.u)(e,y.p))})),o.load(e)}))}(t).then((function(){if(!x(t)){var e=Object(b.b)(t.get("playlist"),t);t.attributes.playlist=e;try{Object(b.e)(e)}catch(t){throw t.code+=y.p,t}var n=t.getProviders(),r=n.choose(e[0].sources[0]),i=r.provider,o=r.name;return"function"==typeof i?i:p.a.html5&&"html5"===o?p.a.html5:n.load(o).catch((function(t){throw Object(y.u)(t,y.q)}))}}))}function O(t){var e=t.get("skin")?t.get("skin").url:void 0;if("string"==typeof e&&!function(t){for(var e=document.styleSheets,n=0,r=e.length;n0&&(n=t(l,n));break;case"title":n.title=Object(o.d)(l);break;case"description":n.description=Object(o.d)(l);break;case"guid":n.mediaid=Object(o.d)(l);break;case"thumbnail":n.image||(n.image=Object(u.j)(l,"url"));break;case"group":t(l,n);break;case"subtitle":var p={};p.file=Object(u.j)(l,"url"),p.kind="captions",Object(u.j)(l,"lang").length>0&&(p.label=(r=Object(u.j)(l,"lang"),i=void 0,(i={zh:"Chinese",nl:"Dutch",en:"English",fr:"French",de:"German",it:"Italian",ja:"Japanese",pt:"Portuguese",ru:"Russian",es:"Spanish"})[r]?i[r]:r)),c.push(p)}}}n.hasOwnProperty("tracks")||(n.tracks=[]);for(var h=0;h0&&(r[f][n]="true"===r[f][n],r[f].label.length||delete r[f].label,e.sources.push(r[f]))}if(i.length){e.tracks=[];for(var d=0;d0&&(i[d][n]="true"===i[d][n],i[d].kind=i[d].kind.length?i[d].kind:"captions",i[d].label.length||delete i[d].label,e.tracks.push(i[d]))}return e},f=n(25);function d(t){for(var e={},n=0;n=4.4):null}},function(t,e,n){"use strict";var r=n(3),i=function(){},o=function(){return!1},u={name:"default"},a={supports:o,play:i,pause:i,preload:i,load:i,stop:i,volume:i,mute:i,seek:i,resize:i,remove:i,destroy:i,setVisibility:i,setFullscreen:i,getFullscreen:o,supportsFullscreen:o,getContainer:i,setContainer:i,getName:function(){return u},getQualityLevels:i,getCurrentQuality:i,setCurrentQuality:i,getAudioTracks:i,getCurrentAudioTrack:i,setCurrentAudioTrack:i,getSeekRange:function(){return{start:0,end:this.getDuration()}},setPlaybackRate:i,getPlaybackRate:function(){return 1},getBandwidthEstimate:function(){return null},getLiveLatency:function(){return null},setControls:i,attachMedia:i,detachMedia:i,init:i,setState:function(t){this.state=t,this.trigger(r.bb,{newstate:t})},sendMediaType:function(t){var e=t[0],n=e.type,i=e.mimeType,o="aac"===n||"mp3"===n||"mpeg"===n||i&&0===i.indexOf("audio/");this.trigger(r.T,{mediaType:o?"audio":"video"})}};e.a=a},function(t,e,n){"use strict";var r,i=n(49),o=n(8),u=n(5),a=[],c=[],s=[],l={},f="screen"in window&&"orientation"in window.screen,d=o.OS.android&&o.Browser.chrome,p=!1;function h(t,e){for(var n=e.length;n--;){var r=e[n];if(t.target===r.getContainer()){r.setIntersection(t);break}}}function v(){a.forEach((function(t){var e=t.model;if(!(e.get("audioMode")||!e.get("controls")||e.get("visibility")<.75)){var n=e.get("state"),r=Object(u.f)();!r&&"paused"===n&&t.api.getFullscreen()?t.api.setFullscreen(!1):"playing"===n&&t.api.setFullscreen(r)}}))}function g(){a.forEach((function(t){t.model.set("activeTab",Object(i.a)())}))}function b(t,e){var n=e.indexOf(t);-1!==n&&e.splice(n,1)}function m(t){s.forEach((function(e){e(t)}))}document.addEventListener("visibilitychange",g),document.addEventListener("webkitvisibilitychange",g),d&&f&&window.screen.orientation.addEventListener("change",v),window.addEventListener("beforeunload",(function(){document.removeEventListener("visibilitychange",g),document.removeEventListener("webkitvisibilitychange",g),window.removeEventListener("scroll",m),d&&f&&window.screen.orientation.removeEventListener("change",v)})),e.a={add:function(t){a.push(t)},remove:function(t){b(t,a)},addScrollHandler:function(t){p||(p=!0,window.addEventListener("scroll",m)),s.push(t)},removeScrollHandler:function(t){var e=s.indexOf(t);-1!==e&&s.splice(e,1)},addWidget:function(t){c.push(t)},removeWidget:function(t){b(t,c)},size:function(){return a.length},observe:function(t){var e;e=window.IntersectionObserver,r||(r=new e((function(t){if(t&&t.length)for(var e=t.length;e--;){var n=t[e];h(n,a),h(n,c)}}),{threshold:[0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1]})),l[t.id]||(l[t.id]=!0,r.observe(t))},unobserve:function(t){r&&l[t.id]&&(delete l[t.id],r.unobserve(t))}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return f}));var r=n(0),i=n(42),o=n(5),u=n(10);function a(t,e){for(var n=0;n
    '),d=f.firstChild,p=d.firstChild,h=d.nextSibling;Object(u.d)([d,h],Object(r.g)({overflow:"auto"},a,s)),Object(u.d)(f,Object(r.g)({},a,s)),this.expandElement=d,this.expandChild=p,this.contractElement=h,this.hiddenElement=f,this.element=e,this.view=n,this.model=i,this.width=0,this.resized=!1,e.firstChild?e.insertBefore(f,e.firstChild):e.appendChild(f),e.addEventListener("scroll",l,!0),c.push(this),l()}var e,n,i;return e=t,(n=[{key:"destroy",value:function(){if(this.view){var t=c.indexOf(this);-1!==t&&c.splice(t,1),this.element.removeEventListener("scroll",l,!0),this.element.removeChild(this.hiddenElement),this.view=this.model=null}}}])&&a(e.prototype,n),i&&a(e,i),t}()},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}n.r(e);var i=setTimeout;function o(){}function u(t){if(!(this instanceof u))throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],d(t,this)}function a(t,e){for(;3===t._state;)t=t._value;0!==t._state?(t._handled=!0,u._immediateFn((function(){var n=1===t._state?e.onFulfilled:e.onRejected;if(null!==n){var r;try{r=n(t._value)}catch(t){return void s(e.promise,t)}c(e.promise,r)}else(1===t._state?c:s)(e.promise,t._value)}))):t._deferreds.push(e)}function c(t,e){try{if(e===t)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"===r(e)||"function"==typeof e)){var n=e.then;if(e instanceof u)return t._state=3,t._value=e,void l(t);if("function"==typeof n)return void d((i=n,o=e,function(){i.apply(o,arguments)}),t)}t._state=1,t._value=e,l(t)}catch(e){s(t,e)}var i,o}function s(t,e){t._state=2,t._value=e,l(t)}function l(t){2===t._state&&0===t._deferreds.length&&u._immediateFn((function(){t._handled||u._unhandledRejectionFn(t._value)}));for(var e=0,n=t._deferreds.length;e2&&void 0!==arguments[2]?arguments[2]:[];if(O.a.debug)return t.apply(e||this,n);try{return t.apply(e||this,n)}catch(e){return new _(t.name,e)}},Error:_,Timer:P.a,log:R.a,genId:D.b,between:N.a,foreach:function(t,e){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&e(n,t[n])},flashVersion:F.a,isIframe:F.m,indexOf:j.l,trim:A.i,pad:A.e,extension:A.a,hms:A.b,seconds:A.g,prefix:A.f,suffix:A.h,noop:function(){}}),q=0;function z(t,e){var n=new x.a(e);return n.on(C.gb,(function(e){t._qoe.tick("ready"),e.setupTime=t._qoe.between("setup","ready")})),n.on("all",(function(e,n){t.trigger(e,n)})),n}function V(t,e){var n=t.plugins;Object.keys(n).forEach((function(t){delete n[t]})),e.get("setupConfig")&&t.trigger("remove"),t.off(),e.playerDestroy(),e.getContainer().removeAttribute("data-jwplayer-id")}function Q(t){var e=++q,n=t.id||"player-".concat(e),r=new P.a,i={},o=z(this,t);r.tick("init"),t.setAttribute("data-jwplayer-id",n),Object.defineProperties(this,{id:{enumerable:!0,get:function(){return n}},uniqueId:{enumerable:!0,get:function(){return e}},plugins:{enumerable:!0,get:function(){return i}},_qoe:{enumerable:!0,get:function(){return r}},version:{enumerable:!0,get:function(){return w.a}},Events:{enumerable:!0,get:function(){return S.a}},utils:{enumerable:!0,get:function(){return B}},_:{enumerable:!0,get:function(){return j.c}}}),Object(j.g)(this,{_events:{},setup:function(e){return r.clear("ready"),r.tick("setup"),V(this,o),(o=z(this,t)).init(e,this),this.on(e.events,null,this)},remove:function(){return function(t){for(var e=v.a.length;e--;)if(v.a[e].uniqueId===t.uniqueId){v.a.splice(e,1);break}}(this),V(this,o),this},qoe:function(){var t=o.getItemQoe();return{setupTime:this._qoe.between("setup","ready"),firstFrame:t.getFirstFrame?t.getFirstFrame():null,player:this._qoe.dump(),item:t.dump()}},addCues:function(t){return Array.isArray(t)&&o.addCues(t),this},getAudioTracks:function(){return o.getAudioTracks()},getBuffer:function(){return o.get("buffer")},getCaptions:function(){return o.get("captions")},getCaptionsList:function(){return o.getCaptionsList()},getConfig:function(){return o.getConfig()},getContainer:function(){return o.getContainer()},getControls:function(){return o.get("controls")},getCues:function(){return o.get("cues")},getCurrentAudioTrack:function(){return o.getCurrentAudioTrack()},getCurrentCaptions:function(){return o.getCurrentCaptions()},getCurrentQuality:function(){return o.getCurrentQuality()},getCurrentTime:function(){return o.get("currentTime")},getDuration:function(){return o.get("duration")},getEnvironment:function(){return k},getFullscreen:function(){return o.get("fullscreen")},getHeight:function(){return o.getHeight()},getItemMeta:function(){return o.get("itemMeta")||{}},getMute:function(){return o.getMute()},getPlaybackRate:function(){return o.get("playbackRate")},getPlaylist:function(){return o.get("playlist")},getPlaylistIndex:function(){return o.get("item")},getPlaylistItem:function(t){if(!B.exists(t))return o.get("playlistItem");var e=this.getPlaylist();return e?e[t]:null},getPosition:function(){return o.get("position")},getProvider:function(){return o.getProvider()},getQualityLevels:function(){return o.getQualityLevels()},getSafeRegion:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return o.getSafeRegion(t)},getState:function(){return o.getState()},getStretching:function(){return o.get("stretching")},getViewable:function(){return o.get("viewable")},getVisualQuality:function(){return o.getVisualQuality()},getVolume:function(){return o.get("volume")},getWidth:function(){return o.getWidth()},setCaptions:function(t){return o.setCaptions(t),this},setConfig:function(t){return o.setConfig(t),this},setControls:function(t){return o.setControls(t),this},setCurrentAudioTrack:function(t){o.setCurrentAudioTrack(t)},setCurrentCaptions:function(t){o.setCurrentCaptions(t)},setCurrentQuality:function(t){o.setCurrentQuality(t)},setFullscreen:function(t){return o.setFullscreen(t),this},setMute:function(t){return o.setMute(t),this},setPlaybackRate:function(t){return o.setPlaybackRate(t),this},setPlaylistItem:function(t,e){return o.setPlaylistItem(t,e),this},setCues:function(t){return Array.isArray(t)&&o.setCues(t),this},setVolume:function(t){return o.setVolume(t),this},load:function(t,e){return o.load(t,e),this},play:function(t){return o.play(t),this},pause:function(t){return o.pause(t),this},playToggle:function(t){switch(this.getState()){case C.pb:case C.jb:return this.pause(t);default:return this.play(t)}},seek:function(t,e){return o.seek(t,e),this},playlistItem:function(t,e){return o.playlistItem(t,e),this},playlistNext:function(t){return o.playlistNext(t),this},playlistPrev:function(t){return o.playlistPrev(t),this},next:function(t){return o.next(t),this},castToggle:function(){return o.castToggle(),this},createInstream:function(){return o.createInstream()},stop:function(){return o.stop(),this},resize:function(t,e){return o.resize(t,e),this},addButton:function(t,e,n,r,i){return o.addButton(t,e,n,r,i),this},removeButton:function(t){return o.removeButton(t),this},attachMedia:function(){return o.attachMedia(),this},detachMedia:function(){return o.detachMedia(),this},isBeforeComplete:function(){return o.isBeforeComplete()},isBeforePlay:function(){return o.isBeforePlay()}})}Object(j.g)(Q.prototype,{on:function(t,e,n){return S.c.call(this,t,e,n)},once:function(t,e,n){return S.d.call(this,t,e,n)},off:function(t,e,n){return S.b.call(this,t,e,n)},trigger:function(t,e){return(e=j.c.isObject(e)?Object(j.g)({},e):{}).type=t,O.a.debug?S.e.call(this,t,e):S.f.call(this,t,e)},getPlugin:function(t){return this.plugins[t]},addPlugin:function(t,e){this.plugins[t]=e,this.on("ready",e.addToPlayer),e.resize&&this.on("resize",e.resizeHandler)},registerPlugin:function(t,e,n){Object(y.b)(t,e,n)},getAdBlock:function(){return!1},playAd:function(t){},pauseAd:function(t){},skipAd:function(){}}),n.d(e,"assignLibraryProperties",(function(){return H})),n.p=Object(h.loadFrom)();var W=function(t){var e,n;if(t?"string"==typeof t?(e=X(t))||(n=document.getElementById(t)):"number"==typeof t?e=v.a[t]:t.nodeType&&(e=X((n=t).id||n.getAttribute("data-jwplayer-id"))):e=v.a[0],e)return e;if(n){var r=new Q(n);return v.a.push(r),r}return{registerPlugin:y.b}};function X(t){for(var e=0;e=0&&(r.metadata.mpegts=n+t)}var a=this.getLiveLatency();null!==a&&(r.latency=a),(this.state===s.pb||this.seeking)&&this.trigger(s.S,r)}},click:function(e){this.trigger(s.n,e)},volumechange:function(){var e=this.video;this.trigger(s.V,{volume:Math.round(100*e.volume)}),this.trigger(s.M,{mute:e.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(s.R))},playing:function(){-1===this.stallTime&&this.setState(s.pb),this.trigger(s.fb)},pause:function(){this.state!==s.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(s.ob))},progress:function(){var e=this.getDuration();if(!(e<=0||e===1/0)){var t=this.video.buffered;if(t&&0!==t.length){var i=Object(u.a)(t.end(t.length-1)/e,0,1);this.trigger(s.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:e,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(s.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==s.mb&&this.state!==s.kb&&this.trigger(s.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},o=i(10);function d(e){return e&&e.length?e.end(e.length-1):0}var l={container:null,volume:function(e){this.video.volume=Math.min(Math.max(0,e/100),1)},mute:function(e){this.video.muted=!!e,this.video.muted||this.video.removeAttribute("muted")},resize:function(e,t,i){var r=this.video,a=r.videoWidth,s=r.videoHeight;if(e&&t&&a&&s){var u={objectFit:"",width:"",height:""};if("uniform"===i){var c=e/t,d=a/s,l=Math.abs(c-d);l<.09&&l>.0025&&(u.objectFit="fill",i="exactfit")}if(n.Browser.ie||n.OS.iOS&&n.OS.version.major<9||n.Browser.androidNative)if("uniform"!==i){u.objectFit="contain";var h=e/t,f=a/s,v=1,T=1;"none"===i?v=T=h>f?Math.ceil(100*s/t)/100:Math.ceil(100*a/e)/100:"fill"===i?v=T=h>f?h/f:f/h:"exactfit"===i&&(h>f?(v=h/f,T=1):(v=1,T=f/h)),Object(o.e)(r,"matrix(".concat(v.toFixed(2),", 0, 0, ").concat(T.toFixed(2),", 0, 0)"))}else u.top=u.left=u.margin="",Object(o.e)(r,"");Object(o.d)(r,u)}},getContainer:function(){return this.container},setContainer:function(e){this.container=e,this.video.parentNode!==e&&e.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var e=this.container;e&&e===this.video.parentNode&&e.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return d(this.video.buffered)-this.video.currentTime<=2}},h={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},f=i(65),v=i(5),T=i(53),g=i(7),m=i(66),k=i(63),b={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function y(e,t){for(var i,r,n,a=e.length,s="",u=t||0;u>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:s+=String.fromCharCode(i);break;case 12:case 13:r=e[u++],s+=String.fromCharCode((31&i)<<6|63&r);break;case 14:r=e[u++],n=e[u++],s+=String.fromCharCode((15&i)<<12|(63&r)<<6|(63&n)<<0)}return s}function p(e){var t=function(e){for(var t="0x",i=0;i>1|(8323072&t)>>2|(2130706432&t)>>3}function x(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(e,t){if(!("value"in t)&&"data"in t&&t.data instanceof ArrayBuffer){var i=new Uint8Array(t.data),r=i.length;t={value:{key:"",data:""}};for(var n=10;n<14&&n0){var o=y(i.subarray(a,a+=u),0);if("PRIV"===t.value.key){if("com.apple.streaming.transportStreamTimestamp"===o){var d=1&p(i.subarray(a,a+=4)),l=p(i.subarray(a,a+=4))+(d?4294967296:0);t.value.data=l}else t.value.data=y(i,a+1);t.value.info=o}else t.value.info=o,t.value.data=y(i,a+1)}else{var h=i[a];t.value.data=1===h||2===h?function(e,t){for(var i=e.length-1,r="",n=t||0;n=0&&n[a].startTime>t.startTime;a--)i.unshift(n[a]),e.removeCue(n[a]);try{e.addCue(t),i.forEach((function(t){return e.addCue(t)}))}catch(e){console.error(e)}e.mode=r}(t,r)}else try{t.addCue(i)}catch(e){console.error(e)}}function B(e,t){t&&t.length&&Object(r.f)(t,(function(t){if(!(n.Browser.ie&&e&&/^(native|subtitle|cc)/.test(t._id))){n.Browser.ie&&"disabled"===t.mode||(t.mode="disabled",t.mode="hidden");for(var i=t.cues.length;i--;)t.removeCue(t.cues[i]);t.embedded||(t.mode="disabled"),t.inuse=!1}}))}function E(e){return"subtitles"===e||"captions"===e}function S(e){var t,i=Object(k.b)(e,this._unknownCount),n=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===e.kind){var a=this.video.textTracks;(t=Object(r.j)(a,{label:n}))||(t=this.video.addTextTrack(e.kind,n,e.language||"")),t.default=e.default,t.mode="disabled",t.inuse=!0}else(t=e).data=t.data||[];return t._id||(t._id=Object(k.a)(e,this._textTracks.length)),t}function L(e){this._textTracks.push(e),this._tracksById[e._id]=e}function I(){if(this._textTracks){var e=this._textTracks.filter((function(e){return e.embedded||"subs"===e.groupid}));this._initTextTracks(),e.forEach((function(e){this._tracksById[e._id]=e})),this._textTracks=e}}function N(e){this.triggerActiveCues(e.currentTarget.activeCues)}function R(e,t,i){var r=e.kind;this._cachedVTTCues[e._id]||(this._cachedVTTCues[e._id]={});var n,a=this._cachedVTTCues[e._id];switch(r){case"captions":case"subtitles":n=i||Math.floor(20*t.startTime);var s="_"+t.line,u=Math.floor(20*t.endTime),c=a[n+s]||a[n+1+s]||a[n-1+s];return!(c&&Math.abs(c-u)<=1)&&(a[n+s]=u,!0);case"metadata":var o=t.data?new Uint8Array(t.data).join(""):t.text;return!a[n=i||t.startTime+o]&&(a[n]=t.endTime,!0);default:return!1}}function M(e){if(e.length>this._textTracks.length)return!0;for(var t=0;t=0&&(T.retries=0);var e=T.getVideoCurrentTime();T.currentTime=e,B&&O!==e&&Z(e),c.timeupdate.call(T),ve(),n.Browser.ie&&Y()},resize:Y,ended:function(){j=-1,Te(),c.ended.call(T)},loadedmetadata:function(){var e=T.getDuration();M&&e===1/0&&(e=0);var t={metadataType:"media",duration:e,height:y.videoHeight,width:y.videoWidth,seekRange:T.getSeekRange()};T.trigger(s.K,t),Y()},durationchange:function(){M||c.progress.call(T)},loadeddata:function(){var e;!function(){if(y.getStartDate){var e=y.getStartDate(),t=e.getTime?e.getTime():NaN;if(t!==T.startDateTime&&!isNaN(t)){T.startDateTime=t;var i=e.toISOString(),r=T.getSeekRange(),n=r.start,a=r.end,u={metadataType:"program-date-time",programDateTime:i,start:n,end:a},c=T.createCue(n,a,JSON.stringify(u));T.addVTTCue({type:"metadata",cue:c}),delete u.metadataType,T.trigger(s.L,{metadataType:"program-date-time",metadata:u})}}}(),c.loadeddata.call(T),function(e){if(S=null,!e)return;if(e.length){for(var t=0;t0&&(t=e.map((function(e,t){return{label:e.label||t}}))),t}function ie(e){T.currentTime=-1,m=e.minDvrWindow,b=e.sources,j=function(e){var i=Math.max(0,j),r=t.qualityLabel;if(e)for(var n=0;n0&&(w=-1,T.seek(e)),e>0&&T.getVideoCurrentTime()!==e&&T.seek(e);var r=te(b);r&&T.trigger(s.I,{levels:r,currentQuality:j}),b.length&&"hls"!==b[0].type&&fe()}function ae(e){S=null,L=-1,p.reason||(p.reason="initial choice",p.level={}),_=!1;var t=document.createElement("source");t.src=e.file,y.src!==t.src&&(y.src=e.file)}function se(){y&&(T.disableTextTrack(),y.removeAttribute("preload"),y.removeAttribute("src"),Object(v.h)(y),Object(o.d)(y,{objectFit:""}),j=-1,!n.Browser.msie&&"load"in y&&y.load())}function ue(){var e=1/0;return["buffered","seekable"].forEach((function(t){for(var i=y[t],n=i?i.length:0;n--;){var a=Math.min(e,i.start(n));Object(r.o)(a)&&(e=a)}})),e}function ce(){var e=0;return["buffered","seekable"].forEach((function(t){for(var i=y[t],n=i?i.length:0;n--;){var a=Math.max(e,i.end(n));Object(r.o)(a)&&(e=a)}})),e}function oe(){for(var e=-1,t=0;t-1&&e1)&&function(e){z=e.end,G=Math.min(0,T.getVideoCurrentTime()-z),$=Object(D.a)()}(t),Object(f.a)(t.end-t.start,m))return G}return e}(T.getVideoCurrentTime())},T.getDuration=function(){if(t.getDurationHook)return t.getDurationHook();var e=y.duration;if(M&&e===1/0&&0===T.getVideoCurrentTime()||isNaN(e))return 0;var i=ce();if(y.duration===1/0&&i){var r=i-ue();Object(f.a)(r,m)&&(e=-r)}return e},T.getSeekRange=function(){var e={start:0,end:T.getDuration()};return y.seekable.length&&(e.end=ce(),e.start=ue()),e},T.getLiveLatency=function(){var e=null,t=ce();return T.isLive()&&t&&(e=t+(Object(D.a)()-$)/1e3-T.getVideoCurrentTime()),e},this.stop=function(){Te(),se(),this.clearTracks(),n.Browser.ie&&y.pause(),this.setState(s.mb)},this.destroy=function(){E=q,J(k,y),this.removeTracksListener(y.audioTracks,"change",oe),this.removeTracksListener(y.textTracks,"change",T.textTrackChangeHandler),this.off()},this.init=function(e){T.retries=0,T.maxRetries=e.adType?0:3,ie(e);var t=b[j];(M=Object(a.a)(t))&&(T.supportsPlaybackRate=!1,k.waiting=q),T.eventsOn_(),b.length&&"hls"!==b[0].type&&this.sendMediaType(b),p.reason=""},this.preload=function(e){ie(e);var t=b[j],i=t.preload||"metadata";"none"!==i&&(y.setAttribute("preload",i),ae(t))},this.load=function(e){ie(e),ne(e.starttime),this.setupSideloadedTracks(e.tracks)},this.play=function(){return E(),re()},this.pause=function(){Te(),E=function(){if(y.paused&&T.getVideoCurrentTime()&&T.isLive()){var e=ce(),t=e-ue(),i=!Object(f.a)(t,m),n=e-T.getVideoCurrentTime();if(i&&e&&(n>15||n<0)){if(C=Math.max(e-10,e-t),!Object(r.o)(C))return;Z(T.getVideoCurrentTime()),y.currentTime=C}}},y.pause()},this.seek=function(e){if(t.seekHook)return t.seekHook(e,y);var i=T.getSeekRange(),r=e;if(e<0&&(r+=i.end),_||(_=!!ce()),_){w=0;try{if(T.seeking=!0,T.isLive()&&Object(f.a)(i.end-i.start,m))if(G=Math.min(0,r-z),e<0)r+=Math.min(12,(Object(D.a)()-$)/1e3);C=r,Z(T.getVideoCurrentTime()),y.currentTime=r}catch(e){T.seeking=!1,w=r}}else w=r,n.Browser.firefox&&y.paused&&re()},this.setVisibility=function(e){(e=!!e)||n.OS.android?Object(o.d)(T.container,{visibility:"visible",opacity:1}):Object(o.d)(T.container,{visibility:"",opacity:0})},this.setFullscreen=function(e){if(e=!!e){try{var t=y.webkitEnterFullscreen||y.webkitEnterFullScreen;t&&t.apply(y)}catch(e){return!1}return T.getFullScreen()}var i=y.webkitExitFullscreen||y.webkitExitFullScreen;return i&&i.apply(y),e},T.getFullScreen=function(){return B||!!y.webkitDisplayingFullscreen},this.setCurrentQuality=function(e){j!==e&&e>=0&&b&&b.length>e&&(j=e,p.reason="api",p.level={},this.trigger(s.J,{currentQuality:e,levels:te(b)}),t.qualityLabel=b[e].label,ne(T.getVideoCurrentTime()||0),re())},this.setPlaybackRate=function(e){y.playbackRate=y.defaultPlaybackRate=e},this.getPlaybackRate=function(){return y.playbackRate},this.getCurrentQuality=function(){return j},this.getQualityLevels=function(){return Array.isArray(b)?b.map((function(e){return function(e){return{bitrate:e.bitrate,label:e.label,width:e.width,height:e.height}}(e)})):[]},this.getName=function(){return{name:Q}},this.setCurrentAudioTrack=le,this.getAudioTracks=function(){return S||[]},this.getCurrentAudioTrack=function(){return L}}Object(r.g)(z.prototype,T.a),z.getName=function(){return{name:"html5"}};t.default=z;var K=220001},57:function(e,t,i){"use strict";i.d(t,"a",(function(){return n}));var r=i(2);function n(e){var t=[],i=(e=Object(r.i)(e)).split("\r\n\r\n");1===i.length&&(i=e.split("\n\n"));for(var n=0;n0&&(n=0),i.length>n+1&&i[n+1]){var a=i[n],s=a.indexOf(" --\x3e ");s>0&&(t.begin=Object(r.g)(a.substr(0,s)),t.end=Object(r.g)(a.substr(s+5)),t.text=i.slice(n+1).join("\r\n"))}return t}},63:function(e,t,i){"use strict";function r(e,t){var i=e.kind||"cc";return e.default||e.defaulttrack?"default":e._id||e.file||i+t}function n(e,t){var i=e.label||e.name||e.language;return i||(i="Unknown CC",(t+=1)>1&&(i+=" ["+t+"]")),{label:i,unknownCount:t}}i.d(t,"a",(function(){return r})),i.d(t,"b",(function(){return n}))},64:function(e,t,i){"use strict";function r(e){return new Promise((function(t,i){if(e.paused)return i(n("NotAllowedError",0,"play() failed."));var r=function(){e.removeEventListener("play",a),e.removeEventListener("playing",s),e.removeEventListener("pause",s),e.removeEventListener("abort",s),e.removeEventListener("error",s)},a=function(){e.addEventListener("playing",s),e.addEventListener("abort",s),e.addEventListener("error",s),e.addEventListener("pause",s)},s=function(e){if(r(),"playing"===e.type)t();else{var a='The play() request was interrupted by a "'.concat(e.type,'" event.');"error"===e.type?i(n("NotSupportedError",9,a)):i(n("AbortError",20,a))}};e.addEventListener("play",a)}))}function n(e,t,i){var r=new Error(i);return r.name=e,r.code=t,r}i.d(t,"a",(function(){return r}))},65:function(e,t,i){"use strict";function r(e,t){return e!==1/0&&Math.abs(e)>=Math.max(a(t),0)}function n(e,t){var i="VOD";return e===1/0?i="LIVE":e<0&&(i=r(e,a(t))?"DVR":"LIVE"),i}function a(e){return void 0===e?120:Math.max(e,0)}i.d(t,"a",(function(){return r})),i.d(t,"b",(function(){return n}))},66:function(e,t,i){"use strict";var r=i(67),n=i(16),a=i(22),s=i(4),u=i(57),c=i(2),o=i(1);function d(e){throw new o.n(null,e)}function l(e,t,r){e.xhr=Object(a.a)(e.file,(function(a){!function(e,t,r,a){var l,h,v=e.responseXML?e.responseXML.firstChild:null;if(v)for("xml"===Object(s.b)(v)&&(v=v.nextSibling);v.nodeType===v.COMMENT_NODE;)v=v.nextSibling;try{if(v&&"tt"===Object(s.b)(v))l=function(e){e||d(306007);var t=[],i=e.getElementsByTagName("p"),r=30,n=e.getElementsByTagName("tt");if(n&&n[0]){var a=parseFloat(n[0].getAttribute("ttp:frameRate"));isNaN(a)||(r=a)}i||d(306005),i.length||(i=e.getElementsByTagName("tt:p")).length||(i=e.getElementsByTagName("tts:p"));for(var s=0;s\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(v){var T=u.getAttribute("begin"),g=u.getAttribute("dur"),m=u.getAttribute("end"),k={begin:Object(c.g)(T,r),text:v};m?k.end=Object(c.g)(m,r):g&&(k.end=k.begin+Object(c.g)(g,r)),t.push(k)}}return t.length||d(306005),t}(e.responseXML),h=f(l),delete t.xhr,r(h);else{var T=e.responseText;T.indexOf("WEBVTT")>=0?i.e(10).then(function(e){return i(97).default}.bind(null,i)).catch(Object(n.c)(301131)).then((function(e){var i=new e(window);h=[],i.oncue=function(e){h.push(e)},i.onflush=function(){delete t.xhr,r(h)},i.parse(T)})).catch((function(e){delete t.xhr,a(Object(o.v)(null,o.b,e))})):(l=Object(u.a)(T),h=f(l),delete t.xhr,r(h))}}catch(e){delete t.xhr,a(Object(o.v)(null,o.b,e))}}(a,e,t,r)}),(function(e,t,i,n){r(Object(o.u)(n,o.b))}))}function h(e){e&&e.forEach((function(e){var t=e.xhr;t&&(t.onload=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()),delete e.xhr}))}function f(e){return e.map((function(e){return new r.a(e.begin,e.end,e.text)}))}i.d(t,"c",(function(){return l})),i.d(t,"a",(function(){return h})),i.d(t,"b",(function(){return f}))},67:function(e,t,i){"use strict";var r=window.VTTCue;function n(e){if("string"!=typeof e)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[e.toLowerCase()]&&e.toLowerCase()}if(!r){(r=function(e,t,i){var r=this;r.hasBeenReset=!1;var a="",s=!1,u=e,c=t,o=i,d=null,l="",h=!0,f="auto",v="start",T="auto",g=100,m="middle";Object.defineProperty(r,"id",{enumerable:!0,get:function(){return a},set:function(e){a=""+e}}),Object.defineProperty(r,"pauseOnExit",{enumerable:!0,get:function(){return s},set:function(e){s=!!e}}),Object.defineProperty(r,"startTime",{enumerable:!0,get:function(){return u},set:function(e){if("number"!=typeof e)throw new TypeError("Start time must be set to a number.");u=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"endTime",{enumerable:!0,get:function(){return c},set:function(e){if("number"!=typeof e)throw new TypeError("End time must be set to a number.");c=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"text",{enumerable:!0,get:function(){return o},set:function(e){o=""+e,this.hasBeenReset=!0}}),Object.defineProperty(r,"region",{enumerable:!0,get:function(){return d},set:function(e){d=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"vertical",{enumerable:!0,get:function(){return l},set:function(e){var t=function(e){return"string"==typeof e&&(!!{"":!0,lr:!0,rl:!0}[e.toLowerCase()]&&e.toLowerCase())}(e);if(!1===t)throw new SyntaxError("An invalid or illegal string was specified.");l=t,this.hasBeenReset=!0}}),Object.defineProperty(r,"snapToLines",{enumerable:!0,get:function(){return h},set:function(e){h=!!e,this.hasBeenReset=!0}}),Object.defineProperty(r,"line",{enumerable:!0,get:function(){return f},set:function(e){if("number"!=typeof e&&"auto"!==e)throw new SyntaxError("An invalid number or illegal string was specified.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"lineAlign",{enumerable:!0,get:function(){return v},set:function(e){var t=n(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");v=t,this.hasBeenReset=!0}}),Object.defineProperty(r,"position",{enumerable:!0,get:function(){return T},set:function(e){if(e<0||e>100)throw new Error("Position must be between 0 and 100.");T=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"size",{enumerable:!0,get:function(){return g},set:function(e){if(e<0||e>100)throw new Error("Size must be between 0 and 100.");g=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"align",{enumerable:!0,get:function(){return m},set:function(e){var t=n(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");m=t,this.hasBeenReset=!0}}),r.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}t.a=r}}]); \ No newline at end of file +(window.webpackJsonpjwplayer=window.webpackJsonpjwplayer||[]).push([[9],{31:function(e,t,i){"use strict";i.r(t);var r=i(0);var n=i(8),a=i(52),s=i(3),c=i(43),u={canplay:function(){this.trigger(s.E)},play:function(){this.stallTime=-1,this.video.paused||this.state===s.pb||this.setState(s.nb)},loadedmetadata:function(){var e={metadataType:"media",duration:this.getDuration(),height:this.video.videoHeight,width:this.video.videoWidth,seekRange:this.getSeekRange()},t=this.drmUsed;t&&(e.drm=t),this.trigger(s.K,e)},timeupdate:function(){var e=this.getVideoCurrentTime(),t=this.getCurrentTime(),i=this.getDuration();if(!isNaN(i)){this.seeking||this.video.paused||this.state!==s.qb&&this.state!==s.nb||this.stallTime===e||(this.stallTime=-1,this.setState(s.pb),this.trigger(s.fb));var r={position:t,duration:i,currentTime:e,seekRange:this.getSeekRange(),metadata:{currentTime:e}};if(this.getPtsOffset){var n=this.getPtsOffset();n>=0&&(r.metadata.mpegts=n+t)}var a=this.getLiveLatency();null!==a&&(r.latency=a),(this.state===s.pb||this.seeking)&&this.trigger(s.S,r)}},click:function(e){this.trigger(s.n,e)},volumechange:function(){var e=this.video;this.trigger(s.V,{volume:Math.round(100*e.volume)}),this.trigger(s.M,{mute:e.muted})},seeked:function(){this.seeking&&(this.seeking=!1,this.trigger(s.R))},playing:function(){-1===this.stallTime&&this.setState(s.pb),this.trigger(s.fb)},pause:function(){this.state!==s.kb&&(this.video.ended||this.video.error||this.getVideoCurrentTime()!==this.getDuration()&&this.setState(s.ob))},progress:function(){var e=this.getDuration();if(!(e<=0||e===1/0)){var t=this.video.buffered;if(t&&0!==t.length){var i=Object(c.a)(t.end(t.length-1)/e,0,1);this.trigger(s.D,{bufferPercent:100*i,position:this.getCurrentTime(),duration:e,currentTime:this.getVideoCurrentTime(),seekRange:this.getSeekRange()})}}},ratechange:function(){this.trigger(s.P,{playbackRate:this.video.playbackRate})},ended:function(){this.videoHeight=0,this.streamBitrate=-1,this.state!==s.mb&&this.state!==s.kb&&this.trigger(s.F)},loadeddata:function(){this.renderNatively&&this.setTextTracks(this.video.textTracks)}},o=i(10);function d(e){return e&&e.length?e.end(e.length-1):0}var l={container:null,volume:function(e){this.video.volume=Math.min(Math.max(0,e/100),1)},mute:function(e){this.video.muted=!!e,this.video.muted||this.video.removeAttribute("muted")},resize:function(e,t,i){var r=this.video,a=r.videoWidth,s=r.videoHeight;if(e&&t&&a&&s){var c={objectFit:"",width:"",height:""};if("uniform"===i){var u=e/t,d=a/s,l=Math.abs(u-d);l<.09&&l>.0025&&(c.objectFit="fill",i="exactfit")}if(n.Browser.ie||n.OS.iOS&&n.OS.version.major<9||n.Browser.androidNative)if("uniform"!==i){c.objectFit="contain";var h=e/t,f=a/s,v=1,T=1;"none"===i?v=T=h>f?Math.ceil(100*s/t)/100:Math.ceil(100*a/e)/100:"fill"===i?v=T=h>f?h/f:f/h:"exactfit"===i&&(h>f?(v=h/f,T=1):(v=1,T=f/h)),Object(o.e)(r,"matrix(".concat(v.toFixed(2),", 0, 0, ").concat(T.toFixed(2),", 0, 0)"))}else c.top=c.left=c.margin="",Object(o.e)(r,"");Object(o.d)(r,c)}},getContainer:function(){return this.container},setContainer:function(e){this.container=e,this.video.parentNode!==e&&e.appendChild(this.video)},remove:function(){this.stop(),this.destroy();var e=this.container;e&&e===this.video.parentNode&&e.removeChild(this.video)},atEdgeOfLiveStream:function(){if(!this.isLive())return!1;return d(this.video.buffered)-this.video.currentTime<=2}},h={eventsOn_:function(){},eventsOff_:function(){},attachMedia:function(){this.eventsOn_()},detachMedia:function(){return this.eventsOff_()}},f=i(65),v=i(5),T=i(53),g=i(7),m=i(66),k=i(63),b={TIT2:"title",TT2:"title",WXXX:"url",TPE1:"artist",TP1:"artist",TALB:"album",TAL:"album"};function y(e,t){for(var i,r,n,a=e.length,s="",c=t||0;c>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:s+=String.fromCharCode(i);break;case 12:case 13:r=e[c++],s+=String.fromCharCode((31&i)<<6|63&r);break;case 14:r=e[c++],n=e[c++],s+=String.fromCharCode((15&i)<<12|(63&r)<<6|(63&n)<<0)}return s}function p(e){var t=function(e){for(var t="0x",i=0;i>1|(8323072&t)>>2|(2130706432&t)>>3}function x(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:[]).reduce((function(e,t){if(!("value"in t)&&"data"in t&&t.data instanceof ArrayBuffer){var i=new Uint8Array(t.data),r=i.length;t={value:{key:"",data:""}};for(var n=10;n<14&&n0){var o=y(i.subarray(a,a+=c),0);if("PRIV"===t.value.key){if("com.apple.streaming.transportStreamTimestamp"===o){var d=1&p(i.subarray(a,a+=4)),l=p(i.subarray(a,a+=4))+(d?4294967296:0);t.value.data=l}else t.value.data=y(i,a+1);t.value.info=o}else t.value.info=o,t.value.data=y(i,a+1)}else{var h=i[a];t.value.data=1===h||2===h?function(e,t){for(var i=e.length-1,r="",n=t||0;n=0&&n[a].startTime>t.startTime;a--)i.unshift(n[a]),e.removeCue(n[a]);try{e.addCue(t),i.forEach((function(t){return e.addCue(t)}))}catch(e){console.error(e)}e.mode=r}(t,r)}else try{t.addCue(i)}catch(e){console.error(e)}}function B(e,t){t&&t.length&&Object(r.f)(t,(function(t){if(!(n.Browser.ie&&e&&/^(native|subtitle|cc)/.test(t._id))){n.Browser.ie&&"disabled"===t.mode||(t.mode="disabled",t.mode="hidden");for(var i=t.cues.length;i--;)t.removeCue(t.cues[i]);t.embedded||(t.mode="disabled"),t.inuse=!1}}))}function E(e){return"subtitles"===e||"captions"===e}function S(e){var t,i=Object(k.b)(e,this._unknownCount),n=i.label;if(this._unknownCount=i.unknownCount,this.renderNatively||"metadata"===e.kind){var a=this.video.textTracks;(t=Object(r.j)(a,{label:n}))||(t=this.video.addTextTrack(e.kind,n,e.language||"")),t.default=e.default,t.mode="disabled",t.inuse=!0}else(t=e).data=t.data||[];return t._id||(t._id=Object(k.a)(e,this._textTracks.length)),t}function L(e){this._textTracks.push(e),this._tracksById[e._id]=e}function I(){if(this._textTracks){var e=this._textTracks.filter((function(e){return e.embedded||"subs"===e.groupid}));this._initTextTracks(),e.forEach((function(e){this._tracksById[e._id]=e})),this._textTracks=e}}function N(e){this.triggerActiveCues(e.currentTarget.activeCues)}function R(e,t,i){var r=e.kind;this._cachedVTTCues[e._id]||(this._cachedVTTCues[e._id]={});var n,a=this._cachedVTTCues[e._id];switch(r){case"captions":case"subtitles":n=i||Math.floor(20*t.startTime);var s="_"+t.line,c=Math.floor(20*t.endTime),u=a[n+s]||a[n+1+s]||a[n-1+s];return!(u&&Math.abs(u-c)<=1)&&(a[n+s]=c,!0);case"metadata":var o=t.data?new Uint8Array(t.data).join(""):t.text;return!a[n=i||t.startTime+o]&&(a[n]=t.endTime,!0);default:return!1}}function M(e){if(e.length>this._textTracks.length)return!0;for(var t=0;t=0&&(T.retries=0);var e=T.getVideoCurrentTime();T.currentTime=e,B&&O!==e&&Z(e),u.timeupdate.call(T),ve(),n.Browser.ie&&Y()},resize:Y,ended:function(){j=-1,Te(),u.ended.call(T)},loadedmetadata:function(){var e=T.getDuration();M&&e===1/0&&(e=0);var t={metadataType:"media",duration:e,height:y.videoHeight,width:y.videoWidth,seekRange:T.getSeekRange()};T.trigger(s.K,t),Y()},durationchange:function(){M||u.progress.call(T)},loadeddata:function(){var e;!function(){if(y.getStartDate){var e=y.getStartDate(),t=e.getTime?e.getTime():NaN;if(t!==T.startDateTime&&!isNaN(t)){T.startDateTime=t;var i=e.toISOString(),r=T.getSeekRange(),n=r.start,a=r.end,c={metadataType:"program-date-time",programDateTime:i,start:n,end:a},u=T.createCue(n,a,JSON.stringify(c));T.addVTTCue({type:"metadata",cue:u}),delete c.metadataType,T.trigger(s.L,{metadataType:"program-date-time",metadata:c})}}}(),u.loadeddata.call(T),function(e){if(S=null,!e)return;if(e.length){for(var t=0;t0&&(t=e.map((function(e,t){return{label:e.label||t}}))),t}function ie(e){T.currentTime=-1,m=e.minDvrWindow,b=e.sources,j=function(e){var i=Math.max(0,j),r=t.qualityLabel;if(e)for(var n=0;n0&&(w=-1,T.seek(e)),e>0&&T.getVideoCurrentTime()!==e&&T.seek(e);var r=te(b);r&&T.trigger(s.I,{levels:r,currentQuality:j}),b.length&&"hls"!==b[0].type&&fe()}function ae(e){S=null,L=-1,p.reason||(p.reason="initial choice",p.level={}),_=!1;var t=document.createElement("source");t.src=e.file,y.src!==t.src&&(y.src=e.file)}function se(){y&&(T.disableTextTrack(),y.removeAttribute("preload"),y.removeAttribute("src"),Object(v.h)(y),Object(o.d)(y,{objectFit:""}),j=-1,!n.Browser.msie&&"load"in y&&y.load())}function ce(){var e=1/0;return["buffered","seekable"].forEach((function(t){for(var i=y[t],n=i?i.length:0;n--;){var a=Math.min(e,i.start(n));Object(r.o)(a)&&(e=a)}})),e}function ue(){var e=0;return["buffered","seekable"].forEach((function(t){for(var i=y[t],n=i?i.length:0;n--;){var a=Math.max(e,i.end(n));Object(r.o)(a)&&(e=a)}})),e}function oe(){for(var e=-1,t=0;t-1&&e1)&&function(e){z=e.end,G=Math.min(0,T.getVideoCurrentTime()-z),$=Object(D.a)()}(t),Object(f.a)(t.end-t.start,m))return G}return e}(T.getVideoCurrentTime())},T.getDuration=function(){if(t.getDurationHook)return t.getDurationHook();var e=y.duration;if(M&&e===1/0&&0===T.getVideoCurrentTime()||isNaN(e))return 0;var i=ue();if(y.duration===1/0&&i){var r=i-ce();Object(f.a)(r,m)&&(e=-r)}return e},T.getSeekRange=function(){var e={start:0,end:T.getDuration()};return y.seekable.length&&(e.end=ue(),e.start=ce()),e},T.getLiveLatency=function(){var e=null,t=ue();return T.isLive()&&t&&(e=t+(Object(D.a)()-$)/1e3-T.getVideoCurrentTime()),e},this.stop=function(){Te(),se(),this.clearTracks(),n.Browser.ie&&y.pause(),this.setState(s.mb)},this.destroy=function(){E=q,J(k,y),this.removeTracksListener(y.audioTracks,"change",oe),this.removeTracksListener(y.textTracks,"change",T.textTrackChangeHandler),this.off()},this.init=function(e){T.retries=0,T.maxRetries=e.adType?0:3,ie(e);var t=b[j];(M=Object(a.a)(t))&&(T.supportsPlaybackRate=!1,k.waiting=q),T.eventsOn_(),b.length&&"hls"!==b[0].type&&this.sendMediaType(b),p.reason=""},this.preload=function(e){ie(e);var t=b[j],i=t.preload||"metadata";"none"!==i&&(y.setAttribute("preload",i),ae(t))},this.load=function(e){ie(e),ne(e.starttime),this.setupSideloadedTracks(e.tracks)},this.play=function(){return E(),re()},this.pause=function(){Te(),E=function(){if(y.paused&&T.getVideoCurrentTime()&&T.isLive()){var e=ue(),t=e-ce(),i=!Object(f.a)(t,m),n=e-T.getVideoCurrentTime();if(i&&e&&(n>15||n<0)){if(C=Math.max(e-10,e-t),!Object(r.o)(C))return;Z(T.getVideoCurrentTime()),y.currentTime=C}}},y.pause()},this.seek=function(e){if(!t.seekHook||!t.seekHook(e,y)){var i=T.getSeekRange(),r=e;if(e<0&&(r+=i.end),_||(_=!!ue()),_){w=0;try{if(T.seeking=!0,T.isLive()&&Object(f.a)(i.end-i.start,m))if(G=Math.min(0,r-z),e<0)r+=Math.min(12,(Object(D.a)()-$)/1e3);C=r,Z(T.getVideoCurrentTime()),y.currentTime=r}catch(e){T.seeking=!1,w=r}}else w=r,n.Browser.firefox&&y.paused&&re()}},this.setVisibility=function(e){(e=!!e)||n.OS.android?Object(o.d)(T.container,{visibility:"visible",opacity:1}):Object(o.d)(T.container,{visibility:"",opacity:0})},this.setFullscreen=function(e){if(e=!!e){try{var t=y.webkitEnterFullscreen||y.webkitEnterFullScreen;t&&t.apply(y)}catch(e){return!1}return T.getFullScreen()}var i=y.webkitExitFullscreen||y.webkitExitFullScreen;return i&&i.apply(y),e},T.getFullScreen=function(){return B||!!y.webkitDisplayingFullscreen},this.setCurrentQuality=function(e){j!==e&&e>=0&&b&&b.length>e&&(j=e,p.reason="api",p.level={},this.trigger(s.J,{currentQuality:e,levels:te(b)}),t.qualityLabel=b[e].label,ne(T.getVideoCurrentTime()||0),re())},this.setPlaybackRate=function(e){y.playbackRate=y.defaultPlaybackRate=e},this.getPlaybackRate=function(){return y.playbackRate},this.getCurrentQuality=function(){return j},this.getQualityLevels=function(){return Array.isArray(b)?b.map((function(e){return function(e){return{bitrate:e.bitrate,label:e.label,width:e.width,height:e.height}}(e)})):[]},this.getName=function(){return{name:Q}},this.setCurrentAudioTrack=le,this.getAudioTracks=function(){return S||[]},this.getCurrentAudioTrack=function(){return L}}Object(r.g)(z.prototype,T.a),z.getName=function(){return{name:"html5"}};t.default=z;var K=220001},57:function(e,t,i){"use strict";i.d(t,"a",(function(){return n}));var r=i(2);function n(e){var t=[],i=(e=Object(r.i)(e)).split("\r\n\r\n");1===i.length&&(i=e.split("\n\n"));for(var n=0;n0&&(n=0),i.length>n+1&&i[n+1]){var a=i[n],s=a.indexOf(" --\x3e ");s>0&&(t.begin=Object(r.g)(a.substr(0,s)),t.end=Object(r.g)(a.substr(s+5)),t.text=i.slice(n+1).join("\r\n"))}return t}},63:function(e,t,i){"use strict";function r(e,t){var i=e.kind||"cc";return e.default||e.defaulttrack?"default":e._id||e.file||i+t}function n(e,t){var i=e.label||e.name||e.language;return i||(i="Unknown CC",(t+=1)>1&&(i+=" ["+t+"]")),{label:i,unknownCount:t}}i.d(t,"a",(function(){return r})),i.d(t,"b",(function(){return n}))},64:function(e,t,i){"use strict";function r(e){return new Promise((function(t,i){if(e.paused)return i(n("NotAllowedError",0,"play() failed."));var r=function(){e.removeEventListener("play",a),e.removeEventListener("playing",s),e.removeEventListener("pause",s),e.removeEventListener("abort",s),e.removeEventListener("error",s)},a=function(){e.addEventListener("playing",s),e.addEventListener("abort",s),e.addEventListener("error",s),e.addEventListener("pause",s)},s=function(e){if(r(),"playing"===e.type)t();else{var a='The play() request was interrupted by a "'.concat(e.type,'" event.');"error"===e.type?i(n("NotSupportedError",9,a)):i(n("AbortError",20,a))}};e.addEventListener("play",a)}))}function n(e,t,i){var r=new Error(i);return r.name=e,r.code=t,r}i.d(t,"a",(function(){return r}))},65:function(e,t,i){"use strict";function r(e,t){return e!==1/0&&Math.abs(e)>=Math.max(a(t),0)}function n(e,t){var i="VOD";return e===1/0?i="LIVE":e<0&&(i=r(e,a(t))?"DVR":"LIVE"),i}function a(e){return void 0===e?120:Math.max(e,0)}i.d(t,"a",(function(){return r})),i.d(t,"b",(function(){return n}))},66:function(e,t,i){"use strict";var r=i(67),n=i(16),a=i(22),s=i(4),c=i(57),u=i(2),o=i(1);function d(e){throw new o.n(null,e)}function l(e,t,r){e.xhr=Object(a.a)(e.file,(function(a){!function(e,t,r,a){var l,h,v=e.responseXML?e.responseXML.firstChild:null;if(v)for("xml"===Object(s.b)(v)&&(v=v.nextSibling);v.nodeType===v.COMMENT_NODE;)v=v.nextSibling;try{if(v&&"tt"===Object(s.b)(v))l=function(e){e||d(306007);var t=[],i=e.getElementsByTagName("p"),r=30,n=e.getElementsByTagName("tt");if(n&&n[0]){var a=parseFloat(n[0].getAttribute("ttp:frameRate"));isNaN(a)||(r=a)}i||d(306005),i.length||(i=e.getElementsByTagName("tt:p")).length||(i=e.getElementsByTagName("tts:p"));for(var s=0;s\s+<").replace(/(<\/?)tts?:/g,"$1").replace(//g,"\r\n");if(v){var T=c.getAttribute("begin"),g=c.getAttribute("dur"),m=c.getAttribute("end"),k={begin:Object(u.g)(T,r),text:v};m?k.end=Object(u.g)(m,r):g&&(k.end=k.begin+Object(u.g)(g,r)),t.push(k)}}return t.length||d(306005),t}(e.responseXML),h=f(l),delete t.xhr,r(h);else{var T=e.responseText;T.indexOf("WEBVTT")>=0?i.e(10).then(function(e){return i(97).default}.bind(null,i)).catch(Object(n.c)(301131)).then((function(e){var i=new e(window);h=[],i.oncue=function(e){h.push(e)},i.onflush=function(){delete t.xhr,r(h)},i.parse(T)})).catch((function(e){delete t.xhr,a(Object(o.v)(null,o.b,e))})):(l=Object(c.a)(T),h=f(l),delete t.xhr,r(h))}}catch(e){delete t.xhr,a(Object(o.v)(null,o.b,e))}}(a,e,t,r)}),(function(e,t,i,n){r(Object(o.u)(n,o.b))}))}function h(e){e&&e.forEach((function(e){var t=e.xhr;t&&(t.onload=null,t.onreadystatechange=null,t.onerror=null,"abort"in t&&t.abort()),delete e.xhr}))}function f(e){return e.map((function(e){return new r.a(e.begin,e.end,e.text)}))}i.d(t,"c",(function(){return l})),i.d(t,"a",(function(){return h})),i.d(t,"b",(function(){return f}))},67:function(e,t,i){"use strict";var r=window.VTTCue;function n(e){if("string"!=typeof e)return!1;return!!{start:!0,middle:!0,end:!0,left:!0,right:!0}[e.toLowerCase()]&&e.toLowerCase()}if(!r){(r=function(e,t,i){var r=this;r.hasBeenReset=!1;var a="",s=!1,c=e,u=t,o=i,d=null,l="",h=!0,f="auto",v="start",T="auto",g=100,m="middle";Object.defineProperty(r,"id",{enumerable:!0,get:function(){return a},set:function(e){a=""+e}}),Object.defineProperty(r,"pauseOnExit",{enumerable:!0,get:function(){return s},set:function(e){s=!!e}}),Object.defineProperty(r,"startTime",{enumerable:!0,get:function(){return c},set:function(e){if("number"!=typeof e)throw new TypeError("Start time must be set to a number.");c=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"endTime",{enumerable:!0,get:function(){return u},set:function(e){if("number"!=typeof e)throw new TypeError("End time must be set to a number.");u=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"text",{enumerable:!0,get:function(){return o},set:function(e){o=""+e,this.hasBeenReset=!0}}),Object.defineProperty(r,"region",{enumerable:!0,get:function(){return d},set:function(e){d=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"vertical",{enumerable:!0,get:function(){return l},set:function(e){var t=function(e){return"string"==typeof e&&(!!{"":!0,lr:!0,rl:!0}[e.toLowerCase()]&&e.toLowerCase())}(e);if(!1===t)throw new SyntaxError("An invalid or illegal string was specified.");l=t,this.hasBeenReset=!0}}),Object.defineProperty(r,"snapToLines",{enumerable:!0,get:function(){return h},set:function(e){h=!!e,this.hasBeenReset=!0}}),Object.defineProperty(r,"line",{enumerable:!0,get:function(){return f},set:function(e){if("number"!=typeof e&&"auto"!==e)throw new SyntaxError("An invalid number or illegal string was specified.");f=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"lineAlign",{enumerable:!0,get:function(){return v},set:function(e){var t=n(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");v=t,this.hasBeenReset=!0}}),Object.defineProperty(r,"position",{enumerable:!0,get:function(){return T},set:function(e){if(e<0||e>100)throw new Error("Position must be between 0 and 100.");T=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"size",{enumerable:!0,get:function(){return g},set:function(e){if(e<0||e>100)throw new Error("Size must be between 0 and 100.");g=e,this.hasBeenReset=!0}}),Object.defineProperty(r,"align",{enumerable:!0,get:function(){return m},set:function(e){var t=n(e);if(!t)throw new SyntaxError("An invalid or illegal string was specified.");m=t,this.hasBeenReset=!0}}),r.displayState=void 0}).prototype.getCueAsHTML=function(){return window.WebVTT.convertCueToDOMTree(window,this.text)}}t.a=r}}]); \ No newline at end of file diff --git a/ui/v2.5/public/jwplayer/vttparser.js b/ui/v2.5/public/jwplayer/vttparser.js index 571ab7065..33a47c742 100644 --- a/ui/v2.5/public/jwplayer/vttparser.js +++ b/ui/v2.5/public/jwplayer/vttparser.js @@ -1,6 +1,6 @@ /*! JW Player version 8.11.5 -Copyright (c) 2019, JW Player, All Rights Reserved +Copyright (c) 2020, JW Player, All Rights Reserved https://github.com/jwplayer/jwplayer/blob/v8.11.5/README.md This source code and its use and distribution is subject to the terms and conditions of the applicable license agreement. @@ -15,7 +15,7 @@ The following software is used under Apache License 2.0 ************************************************************************************************** vtt.js v0.13.0 -Copyright (c) 2019 Mozilla (http://mozilla.org) +Copyright (c) 2020 Mozilla (http://mozilla.org) https://github.com/mozilla/vtt.js/blob/v0.13.0/LICENSE * * * diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index 32b3e73c1..92299389b 100755 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -20,9 +20,9 @@ import Scenes from "./components/Scenes/Scenes"; import { Settings } from "./components/Settings/Settings"; import { Stats } from "./components/Stats"; import Studios from "./components/Studios/Studios"; -import { TagList } from "./components/Tags/TagList"; import { SceneFilenameParser } from "./components/SceneFilenameParser/SceneFilenameParser"; import Movies from "./components/Movies/Movies"; +import Tags from "./components/Tags/Tags"; // Set fontawesome/free-solid-svg as default fontawesome icons library.add(fas); @@ -51,7 +51,7 @@ export const App: React.FC = () => { - + diff --git a/ui/v2.5/src/components/Changelog/Changelog.tsx b/ui/v2.5/src/components/Changelog/Changelog.tsx index e5ec22014..d35f01a09 100644 --- a/ui/v2.5/src/components/Changelog/Changelog.tsx +++ b/ui/v2.5/src/components/Changelog/Changelog.tsx @@ -1,11 +1,19 @@ import React from "react"; import { useChangelogStorage } from "src/hooks"; import Version from "./Version"; -import { V010, V011, V020, V021 } from "./versions"; +import { V010, V011, V020, V021, V030 } from "./versions"; const Changelog: React.FC = () => { const [{ data, loading }, setOpenState] = useChangelogStorage(); + const stashVersion = process.env.REACT_APP_STASH_VERSION; + const buildTime = process.env.REACT_APP_DATE; + + let buildDate; + if (buildTime) { + buildDate = buildTime.substring(0, buildTime.indexOf(" ")); + } + if (loading) return <>; const openState = data?.versions ?? {}; @@ -21,12 +29,20 @@ const Changelog: React.FC = () => { return ( <>

    Changelog:

    + + + @@ -35,7 +51,6 @@ const Changelog: React.FC = () => { date="2020-06-06" openState={openState} setOpenState={setVersionOpenState} - defaultOpen > diff --git a/ui/v2.5/src/components/Changelog/Version.tsx b/ui/v2.5/src/components/Changelog/Version.tsx index 7808ef803..b274a7c3d 100644 --- a/ui/v2.5/src/components/Changelog/Version.tsx +++ b/ui/v2.5/src/components/Changelog/Version.tsx @@ -36,7 +36,7 @@ const Version: React.FC = ({ {version} ( {date ? ( - + ) : ( = ({ -
    {children}
    +
    {children}
    diff --git a/ui/v2.5/src/components/Changelog/versions/index.ts b/ui/v2.5/src/components/Changelog/versions/index.ts index c0b03708f..1d3060a63 100644 --- a/ui/v2.5/src/components/Changelog/versions/index.ts +++ b/ui/v2.5/src/components/Changelog/versions/index.ts @@ -2,3 +2,4 @@ export { default as V010 } from "./v010"; export { default as V011 } from "./v011"; export { default as V020 } from "./v020"; export { default as V021 } from "./v021"; +export { default as V030 } from "./v030"; diff --git a/ui/v2.5/src/components/Changelog/versions/v030.tsx b/ui/v2.5/src/components/Changelog/versions/v030.tsx new file mode 100644 index 000000000..fa445bf52 --- /dev/null +++ b/ui/v2.5/src/components/Changelog/versions/v030.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import ReactMarkdown from "react-markdown"; + +const markup = ` +#### 💥 **Note: After upgrading, the next scan will populate all scenes with oshash hashes. MD5 calculation can be disabled after populating the oshash for all scenes. See \`Hashing Algorithms\` in the \`Configuration\` section of the manual for details. ** + +### ✨ New Features +* Show and allow creation of unknown performers/tags/studios/movies in the scraper dialog. +* Add support for scraping movie details. +* Add support for JSON scrapers. +* Add support for plugin tasks. +* Add oshash algorithm for hashing scene video files. Enabled by default on new systems. +* Support (re-)generation of generated content for specific scenes. +* Add tag thumbnails, tags grid view and tag page. +* Add post-scrape dialog. +* Add various keyboard shortcuts (see manual). +* Support deleting multiple scenes. +* Add in-app help manual. +* Add support for custom served folders. +* Add support for parent/child studios. + +### 🎨 Improvements +* Support cbz galleries. +* Improve sprite generation performance. +* Make preview generation more fault-tolerant. +* Allow clearing of images and querying on missing images. +* Allow free-editing of scene movie number. +* Allow adding performers and studios from selectors. +* Add support for chrome dp in xpath scrapers. +* Allow customisation of preview video generation. +* Add support for live transcoding in Safari. +* Add mapped and fixed post-processing scraping options. +* Add random sorting for performers. +* Search for files which have low or upper case supported filename extensions. +* Add dialog when pasting movie images. +* Allow click and click-drag selection after selecting scene. +* Added multi-scene edit dialog. +* Moved images to separate tables, increasing performance. +* Add gallery grid view. +* Add is-missing scene filter for gallery query. +* Don't import galleries with no images, and delete galleries with no images during clean. +* Show pagination at top as well as bottom of the page. +* Add split xpath post-processing action. +* Improved the layout of the scene page. +* Show rating as stars in scene page. +* Add reload scrapers button. + +### 🐛 Bug fixes +* Fix directories with video name extensions being detected as files to be scanned. +* Fix issues moving generated files between file systems. +* Fix formatted dates using incorrect timezone. + +`; + +export default () => ; diff --git a/ui/v2.5/src/components/Galleries/GalleryCard.tsx b/ui/v2.5/src/components/Galleries/GalleryCard.tsx new file mode 100644 index 000000000..cf72fe4c6 --- /dev/null +++ b/ui/v2.5/src/components/Galleries/GalleryCard.tsx @@ -0,0 +1,71 @@ +import { Card, Button, ButtonGroup } from "react-bootstrap"; +import React from "react"; +import { Link } from "react-router-dom"; +import * as GQL from "src/core/generated-graphql"; +import { FormattedPlural } from "react-intl"; +import { HoverPopover, Icon, TagLink } from "../Shared"; + +interface IProps { + gallery: GQL.GalleryDataFragment; + zoomIndex: number; +} + +export const GalleryCard: React.FC = ({ gallery, zoomIndex }) => { + function maybeRenderScenePopoverButton() { + if (!gallery.scene) return; + + const popoverContent = ( + + ); + + return ( + + + + + + ); + } + + function maybeRenderPopoverButtonGroup() { + if (gallery.scene) { + return ( + <> +
    + + {maybeRenderScenePopoverButton()} + + + ); + } + } + + return ( + + + {gallery.files.length > 0 ? ( + {gallery.path} + ) : undefined} + +
    +
    {gallery.path}
    + + {gallery.files.length}  + + . + +
    + {maybeRenderPopoverButtonGroup()} +
    + ); +}; diff --git a/ui/v2.5/src/components/Galleries/GalleryList.tsx b/ui/v2.5/src/components/Galleries/GalleryList.tsx index 6067c82bf..e3e31e926 100644 --- a/ui/v2.5/src/components/Galleries/GalleryList.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryList.tsx @@ -5,21 +5,35 @@ import { FindGalleriesQueryResult } from "src/core/generated-graphql"; import { useGalleriesList } from "src/hooks"; import { ListFilterModel } from "src/models/list-filter/filter"; import { DisplayMode } from "src/models/list-filter/types"; +import { GalleryCard } from "./GalleryCard"; export const GalleryList: React.FC = () => { const listData = useGalleriesList({ + zoomable: true, renderContent, }); function renderContent( result: FindGalleriesQueryResult, - filter: ListFilterModel + filter: ListFilterModel, + selectedIds: Set, + zoomIndex: number ) { if (!result.data || !result.data.findGalleries) { return; } if (filter.displayMode === DisplayMode.Grid) { - return

    TODO

    ; + return ( +
    + {result.data.findGalleries.galleries.map((gallery) => ( + + ))} +
    + ); } if (filter.displayMode === DisplayMode.List) { return ( diff --git a/ui/v2.5/src/components/Galleries/styles.scss b/ui/v2.5/src/components/Galleries/styles.scss index ec64677fa..6d7e58f23 100644 --- a/ui/v2.5/src/components/Galleries/styles.scss +++ b/ui/v2.5/src/components/Galleries/styles.scss @@ -5,3 +5,12 @@ } } /* stylelint-enable selector-class-pattern */ + +.gallery-card { + padding: 0.5rem; + + &-image { + object-fit: contain; + vertical-align: middle; + } +} diff --git a/ui/v2.5/src/components/Help/Manual.tsx b/ui/v2.5/src/components/Help/Manual.tsx new file mode 100644 index 000000000..038c31b52 --- /dev/null +++ b/ui/v2.5/src/components/Help/Manual.tsx @@ -0,0 +1,166 @@ +import React, { useState } from "react"; +import { Modal, Container, Row, Col, Nav, Tab } from "react-bootstrap"; +import Introduction from "src/docs/en/Introduction.md"; +import Tasks from "src/docs/en/Tasks.md"; +import AutoTagging from "src/docs/en/AutoTagging.md"; +import JSONSpec from "src/docs/en/JSONSpec.md"; +import Configuration from "src/docs/en/Configuration.md"; +import Interface from "src/docs/en/Interface.md"; +import Galleries from "src/docs/en/Galleries.md"; +import Scraping from "src/docs/en/Scraping.md"; +import Plugins from "src/docs/en/Plugins.md"; +import Contributing from "src/docs/en/Contributing.md"; +import SceneFilenameParser from "src/docs/en/SceneFilenameParser.md"; +import KeyboardShortcuts from "src/docs/en/KeyboardShortcuts.md"; +import Help from "src/docs/en/Help.md"; +import { Page } from "./Page"; + +interface IManualProps { + show: boolean; + onClose: () => void; +} + +export const Manual: React.FC = ({ show, onClose }) => { + const content = [ + { + key: "Introduction.md", + title: "Introduction", + content: Introduction, + }, + { + key: "Configuration.md", + title: "Configuration", + content: Configuration, + }, + { + key: "Interface.md", + title: "Interface Options", + content: Interface, + }, + { + key: "Tasks.md", + title: "Tasks", + content: Tasks, + }, + { + key: "AutoTagging.md", + title: "Auto Tagging", + content: AutoTagging, + className: "indent-1", + }, + { + key: "SceneFilenameParser.md", + title: "Scene Filename Parser", + content: SceneFilenameParser, + className: "indent-1", + }, + { + key: "JSONSpec.md", + title: "JSON Specification", + content: JSONSpec, + className: "indent-1", + }, + { + key: "Galleries.md", + title: "Image Galleries", + content: Galleries, + }, + { + key: "Scraping.md", + title: "Metadata Scraping", + content: Scraping, + }, + { + key: "Plugins.md", + title: "Plugins", + content: Plugins, + }, + { + key: "KeyboardShortcuts.md", + title: "Keyboard Shortcuts", + content: KeyboardShortcuts, + }, + { + key: "Contributing.md", + title: "Contributing", + content: Contributing, + }, + { + key: "Help.md", + title: "Further Help", + content: Help, + }, + ]; + + const [activeTab, setActiveTab] = useState(content[0].key); + + // links to other manual pages are specified as "/help/page.md" + // intercept clicks to these pages and set the tab accordingly + function interceptLinkClick( + event: React.MouseEvent + ) { + if (event.target instanceof HTMLAnchorElement) { + const href = (event.target as HTMLAnchorElement).getAttribute("href"); + if (href && href.startsWith("/help")) { + const newKey = (event.target as HTMLAnchorElement).pathname.substring( + "/help/".length + ); + setActiveTab(newKey); + event.preventDefault(); + } + } + } + + return ( + + + Help + + + + setActiveTab(k)} + id="manual-tabs" + > + + + + + + + {content.map((c) => { + return ( + + + + ); + })} + + + + + + + + ); +}; diff --git a/ui/v2.5/src/components/Help/Page.tsx b/ui/v2.5/src/components/Help/Page.tsx new file mode 100644 index 000000000..1ab94109a --- /dev/null +++ b/ui/v2.5/src/components/Help/Page.tsx @@ -0,0 +1,22 @@ +import React, { useEffect, useState } from "react"; +import ReactMarkdown from "react-markdown"; + +interface IPageProps { + // page is a markdown module + // eslint-disable-next-line @typescript-eslint/no-explicit-any + page: any; +} + +export const Page: React.FC = ({ page }) => { + const [markdown, setMarkdown] = useState(""); + + useEffect(() => { + if (!markdown) { + fetch(page) + .then((res) => res.text()) + .then((text) => setMarkdown(text)); + } + }, [page, markdown]); + + return ; +}; diff --git a/ui/v2.5/src/components/Help/styles.scss b/ui/v2.5/src/components/Help/styles.scss new file mode 100644 index 000000000..93f6f3975 --- /dev/null +++ b/ui/v2.5/src/components/Help/styles.scss @@ -0,0 +1,41 @@ +.manual { + color: $text-color; + + .close { + color: $text-color; + } + + &-container { + padding-left: 1px; + padding-right: 5px; + } + + &-header, + &-body { + background-color: #30404d; + color: $text-color; + overflow-y: hidden; + } + + .indent-1 { + padding-left: 2rem; + } + + .manual-content, + .manual-toc { + max-height: calc(100vh - 10rem); + overflow-y: auto; + } + + @media (max-width: 992px) { + .modal-body { + overflow-y: auto; + + .manual-content, + .manual-toc { + max-height: inherit; + overflow-y: hidden; + } + } + } +} diff --git a/ui/v2.5/src/components/List/AddFilter.tsx b/ui/v2.5/src/components/List/AddFilter.tsx index 5ed6f1b37..1aec305c4 100644 --- a/ui/v2.5/src/components/List/AddFilter.tsx +++ b/ui/v2.5/src/components/List/AddFilter.tsx @@ -30,6 +30,15 @@ export const AddFilter: React.FC = ( const valueStage = useRef(criterion.value); + // configure keyboard shortcuts + useEffect(() => { + Mousetrap.bind("f", () => setIsOpen(true)); + + return () => { + Mousetrap.unbind("f"); + }; + }); + // Configure if we are editing an existing criterion useEffect(() => { if (!props.editingCriterion) { @@ -117,6 +126,7 @@ export const AddFilter: React.FC = ( as="select" onChange={onChangedModifierSelect} value={criterion.modifier} + className="btn-secondary" > {criterion.modifierOptions.map((c) => (