diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f7f795c..6233a4ecf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: - master - staging pull_request: + types: [opened, synchronize, reopened, labeled] jobs: test: @@ -44,13 +45,21 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 + - name: Set up Node + uses: actions/setup-node@v7 + with: + node-version: 22 + + - name: Enable pnpm + run: corepack enable + - name: Setup config run: | cp config/settings.yml.template config/settings.yml cp config/database.yml.template config/database.yml - name: Cache dependencies - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: vendor/bundle key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} @@ -86,3 +95,11 @@ jobs: - name: Show images run: docker images + + deploy-staging: + needs: test + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') + uses: ./.github/workflows/deploy.yml + with: + ref: ${{ github.event.pull_request.head.ref }} + secrets: inherit diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a459df0c1..0acd3d902 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,10 +5,102 @@ name: Deploy staging (disabled) on: workflow_dispatch: + inputs: + ref: + description: Branch to deploy + required: false + type: string + workflow_call: + inputs: + ref: + description: Branch to deploy + required: true + type: string jobs: deploy: if: false runs-on: ubuntu-latest steps: +<<<<<<< HEAD - run: echo "VOC staging deploy is disabled on freifunk/voctoweb" +======= + - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref || github.ref_name }} + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - uses: webfactory/ssh-agent@v0.10.0 + with: + # Name of the variable configured in Secrets of our GitHub organisation + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + - name: Add server to known_hosts + run: ssh-keyscan -p 22 -H app.media.test.c3voc.de >> ~/.ssh/known_hosts + - name: Deploy + env: + CAP_REPO: https://github.com/voc/voctoweb.git + CAP_USER: media + CAP_BRANCH: ${{ inputs.ref || github.ref_name }} + CAP_HOST: app.media.test.c3voc.de + CAP_PORT: 22 + SKIP_TAG: "true" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bundle exec cap staging deploy + + clear-deploy-labels: + needs: deploy + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'deploy') + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Remove deploy label from other PRs + uses: actions/github-script@v9 + with: + script: | + const { owner, repo } = context.repo; + const currentPr = context.payload.pull_request.number; + const prs = await github.paginate(github.rest.pulls.list, { + owner, repo, state: "open", per_page: 100, + }); + for (const pr of prs) { + if (pr.number === currentPr) continue; + if (pr.labels.some((l) => l.name === "deploy")) { + await github.rest.issues.removeLabel({ + owner, repo, issue_number: pr.number, name: "deploy", + }); + } + } + + sync-deploy-label: + needs: deploy + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Move deploy label to PR for the deployed branch + uses: actions/github-script@v9 + with: + script: | + const { owner, repo } = context.repo; + const ref = ${{ toJSON(inputs.ref || github.ref_name) }}; + const prs = await github.paginate(github.rest.pulls.list, { + owner, repo, state: "open", per_page: 100, + }); + for (const pr of prs) { + const hasLabel = pr.labels.some((l) => l.name === "deploy"); + if (pr.head.ref === ref) { + if (!hasLabel) { + await github.rest.issues.addLabels({ + owner, repo, issue_number: pr.number, labels: ["deploy"], + }); + } + } else if (hasLabel) { + await github.rest.issues.removeLabel({ + owner, repo, issue_number: pr.number, name: "deploy", + }); + } + } +>>>>>>> voc/main diff --git a/.gitignore b/.gitignore index e35e96db1..0e54e86d0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ docker/ssh # Ignore assets public/assets +/node_modules +/app/assets/builds/* +!/app/assets/builds/.keep # Ignore secret tokens and configs config/database.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fb365159b..5ef7719b0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -392,7 +392,7 @@ Naming/MethodParameterName: # ForbiddenPrefixes: is_, has_, have_ # AllowedMethods: is_a? # MethodDefinitionMacros: define_method, define_singleton_method -Naming/PredicateName: +Naming/PredicatePrefix: Exclude: - 'spec/**/*' - 'app/models/concerns/storage.rb' diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..b46575154 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,25 @@ +# Agents.md + +Guidance for using LLM-based coding assistants on this repository. + +## LLM Usage Policy + +Using an LLM to help write code is fine. Reviewers just might want to know when it happened, so they can weigh the review accordingly — code that reads unusually verbose, follows an unfamiliar pattern, or touches a lot of files at once often has an LLM behind it, and reviewers benefit from knowing that up front. + +**Rule:** if an LLM generated a substantial part of a commit (not just autocomplete or minor suggestions), add a trailer to the commit message: + +``` +Assisted-by: LLM +``` + +- Use the generic `LLM` value — don't name the specific product/vendor. The point is disclosure to reviewers, not advertising a tool. +- Trivial LLM use (autocomplete, one-line suggestions, boilerplate you'd have typed anyway) doesn't need a trailer. +- You're still fully responsible for the code you commit. Review, understand, and test it before committing, regardless of how it was produced. + +## Example + +``` +fix: handle nil schedule in conference import + +Assisted-by: LLM +``` diff --git a/Dockerfile b/Dockerfile index 74917536f..56591804d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,13 +5,20 @@ ARG RUBY_VERSION=3.4 FROM ruby:${RUBY_VERSION}-alpine # Install runtime dependencies -# Node.js is used for JavaScript compression via the uglifier gem +# Node.js and pnpm are used to bundle JavaScript (esbuild) and CSS (sass) +# via jsbundling-rails / cssbundling-rails. +# Keep PNPM_VERSION in sync with "packageManager" in package.json. +ARG PNPM_VERSION=12.3.4 RUN set -eux; \ apk add --no-cache \ dumb-init \ nodejs \ + npm \ tzdata \ - ; + ; \ + npm install -g "pnpm@${PNPM_VERSION}"; \ + npm cache clean --force; \ + pnpm --version WORKDIR /voctoweb @@ -30,7 +37,7 @@ RUN set -eux; \ gcc \ libffi-dev \ libxml2-dev \ - libxslt-dev \ + libxslt-dev \ make \ musl-dev \ patch \ diff --git a/Gemfile b/Gemfile index d136f963a..83446c8f5 100644 --- a/Gemfile +++ b/Gemfile @@ -2,8 +2,12 @@ source 'https://rubygems.org' gem 'openssl' +# json 3.0 changed JSON.parse's options arg to keyword-only, which breaks +# ActiveSupport::JSON.decode's positional call in this Rails version. +gem 'json', '< 3' + # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 7.2.2' +gem 'rails', '~> 8.0' gem 'dotenv-rails' gem 'activeadmin' @@ -54,24 +58,10 @@ gem 'pg', group: :postgresql gem 'elasticsearch-model', '~> 8.0' gem 'elasticsearch-rails', '~> 8.0' -# Use SCSS for stylesheets -gem 'sass-rails' -gem 'bootstrap-sass', '~> 3.4.1' - -# Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '>= 1.3.0' - -# Use CoffeeScript for .js.coffee assets and views -gem 'coffee-rails' - -# See https://github.com/sstephenson/execjs#readme for more supported runtimes -# gem 'therubyracer', platforms: :ruby - -# Use jquery as the JavaScript library -gem 'jquery-rails' - -# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks -gem 'turbolinks' +# Asset pipeline: Propshaft (static) + jsbundling-rails/esbuild + cssbundling-rails/sass, managed via pnpm +gem 'propshaft' +gem 'jsbundling-rails' +gem 'cssbundling-rails' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder' diff --git a/Gemfile.lock b/Gemfile.lock index 35787ab0d..f915a59c0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,49 +10,49 @@ GEM specs: aasm (6.0.0) concurrent-ruby (~> 1.0) - actioncable (7.2.3.2) - actionpack (= 7.2.3.2) - activesupport (= 7.2.3.2) + action_text-trix (2.1.19) + railties + actioncable (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.3.2) - actionpack (= 7.2.3.2) - activejob (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + actionmailbox (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) - actionmailer (7.2.3.2) - actionpack (= 7.2.3.2) - actionview (= 7.2.3.2) - activejob (= 7.2.3.2) - activesupport (= 7.2.3.2) + actionmailer (8.1.3.1) + actionpack (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activesupport (= 8.1.3.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.3.2) - actionview (= 7.2.3.2) - activesupport (= 7.2.3.2) - cgi + actionpack (8.1.3.1) + actionview (= 8.1.3.1) + activesupport (= 8.1.3.1) nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4, < 3.3) + rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.3.2) - actionpack (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + actiontext (8.1.3.1) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.3.2) - activesupport (= 7.2.3.2) + actionview (8.1.3.1) + activesupport (= 8.1.3.1) builder (~> 3.1) - cgi erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) @@ -66,40 +66,41 @@ GEM kaminari (>= 1.2.1) railties (>= 6.1) ransack (>= 4.0) - activejob (7.2.3.2) - activesupport (= 7.2.3.2) + activejob (8.1.3.1) + activesupport (= 8.1.3.1) globalid (>= 0.3.6) - activemodel (7.2.3.2) - activesupport (= 7.2.3.2) - activerecord (7.2.3.2) - activemodel (= 7.2.3.2) - activesupport (= 7.2.3.2) + activemodel (8.1.3.1) + activesupport (= 8.1.3.1) + activerecord (8.1.3.1) + activemodel (= 8.1.3.1) + activesupport (= 8.1.3.1) timeout (>= 0.4.0) - activestorage (7.2.3.2) - actionpack (= 7.2.3.2) - activejob (= 7.2.3.2) - activerecord (= 7.2.3.2) - activesupport (= 7.2.3.2) + activestorage (8.1.3.1) + actionpack (= 8.1.3.1) + activejob (= 8.1.3.1) + activerecord (= 8.1.3.1) + activesupport (= 8.1.3.1) marcel (~> 1.0) - activesupport (7.2.3.2) + activesupport (8.1.3.1) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) - minitest (>= 5.1, < 6) + minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) aes_key_wrap (1.1.0) airbrussh (1.6.1) sshkit (>= 1.6.1, != 1.7.0) api-pagination (7.1.0) - apollo-federation (3.10.3) + apollo-federation (3.10.5) google-protobuf (< 5) graphql arbre (1.7.0) @@ -107,18 +108,13 @@ GEM ruby2_keywords (>= 0.0.2) ast (2.4.3) attr_required (1.0.2) - autoprefixer-rails (10.4.21.0) - execjs (~> 2) backport (1.2.0) base64 (0.3.0) bcrypt (3.1.22) bcrypt_pbkdf (1.1.2) benchmark (0.5.0) - bigdecimal (4.1.2) + bigdecimal (4.1.3) bindata (2.5.1) - bootstrap-sass (3.4.1) - autoprefixer-rails (>= 5.2.1) - sassc (>= 2.0.0) builder (3.3.0) bullet (8.1.3) activesupport (>= 3.0.0) @@ -142,20 +138,14 @@ GEM capistrano (>= 3.9.0) capistrano-bundler sidekiq (>= 7.0) - cgi (0.5.2) coderay (1.1.3) - coffee-rails (5.0.0) - coffee-script (>= 2.2.0) - railties (>= 5.2.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) commander (5.0.0) highline (~> 3.0.0) concurrent-ruby (1.3.8) connection_pool (2.5.5) crass (1.0.7) + cssbundling-rails (1.4.3) + railties (>= 6.0.0) csv (3.3.5) date (3.5.1) devise (5.0.4) @@ -190,7 +180,6 @@ GEM exception_notification (5.0.1) actionmailer (>= 7.1, < 9) activesupport (>= 7.1, < 9) - execjs (2.10.1) factory_bot (6.6.0) activesupport (>= 6.1.0) factory_bot_rails (6.5.1) @@ -220,13 +209,13 @@ GEM goldiloader (6.0.0) activerecord (>= 7.2, < 8.3) activesupport (>= 7.2, < 8.3) - google-protobuf (4.35.1) + google-protobuf (4.36.2) bigdecimal rake (~> 13.3) - google-protobuf (4.35.1-x86_64-darwin) + google-protobuf (4.36.2-x86_64-darwin) bigdecimal rake (~> 13.3) - google-protobuf (4.35.1-x86_64-linux-gnu) + google-protobuf (4.36.2-x86_64-linux-gnu) bigdecimal rake (~> 13.3) graphiql-rails (1.10.5) @@ -235,7 +224,11 @@ GEM base64 graphql-query-resolver (0.2.0) graphql (~> 1.0, >= 1.0.0) +<<<<<<< HEAD haml (7.3.1) +======= + haml (7.5.1) +>>>>>>> voc/main prism (>= 1.1.0) temple (>= 0.8.2) thor @@ -267,6 +260,8 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + jsbundling-rails (1.3.1) + railties (>= 6.0.0) json (2.21.2) json-jwt (1.17.1) activesupport (>= 4.2) @@ -293,7 +288,7 @@ GEM kramdown (~> 2.0) language_server-protocol (3.17.0.6) lint_roller (1.1.0) - listen (3.10.0) + listen (3.10.1) logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -368,7 +363,7 @@ GEM openssl (4.0.2) orm_adapter (0.5.0) ostruct (0.6.3) - parallel (2.1.0) + parallel (2.2.0) parlour (9.1.2) commander (~> 5.0) parser @@ -377,13 +372,17 @@ GEM parser (3.3.12.0) ast (~> 2.4.1) racc - pg (1.6.3) - pg (1.6.3-x86_64-darwin) - pg (1.6.3-x86_64-linux) + pg (1.7.0) + pg (1.7.0-x86_64-darwin) + pg (1.7.0-x86_64-linux) pp (0.6.4) prettyprint prettyprint (0.2.0) prism (1.9.0) + propshaft (1.3.2) + actionpack (>= 7.0.0) + activesupport (>= 7.0.0) + rack pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) @@ -393,7 +392,7 @@ GEM pry (>= 0.13, < 0.17) pry-rails (0.3.11) pry (>= 0.13.0) - psych (5.4.0) + psych (5.5.0) date stringio public_suffix (7.0.5) @@ -426,20 +425,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (7.2.3.2) - actioncable (= 7.2.3.2) - actionmailbox (= 7.2.3.2) - actionmailer (= 7.2.3.2) - actionpack (= 7.2.3.2) - actiontext (= 7.2.3.2) - actionview (= 7.2.3.2) - activejob (= 7.2.3.2) - activemodel (= 7.2.3.2) - activerecord (= 7.2.3.2) - activestorage (= 7.2.3.2) - activesupport (= 7.2.3.2) + rails (8.1.3.1) + actioncable (= 8.1.3.1) + actionmailbox (= 8.1.3.1) + actionmailer (= 8.1.3.1) + actionpack (= 8.1.3.1) + actiontext (= 8.1.3.1) + actionview (= 8.1.3.1) + activejob (= 8.1.3.1) + activemodel (= 8.1.3.1) + activerecord (= 8.1.3.1) + activestorage (= 8.1.3.1) + activesupport (= 8.1.3.1) bundler (>= 1.15.0) - railties (= 7.2.3.2) + railties (= 8.1.3.1) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -451,10 +450,9 @@ GEM rails-html-sanitizer (1.7.1) loofah (~> 2.25, >= 2.25.2) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.2.3.2) - actionpack (= 7.2.3.2) - activesupport (= 7.2.3.2) - cgi + railties (8.1.3.1) + actionpack (= 8.1.3.1) + activesupport (= 8.1.3.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -470,7 +468,11 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) +<<<<<<< HEAD rbs (4.1.3) +======= + rbs (4.2.0) +>>>>>>> voc/main logger prism (>= 1.6.0) tsort @@ -483,7 +485,7 @@ GEM redis-client (= 0.30.1) redis-client (0.30.1) connection_pool - regexp_parser (2.12.0) + regexp_parser (2.13.0) reline (0.6.3) io-console (~> 0.5) responders (3.2.0) @@ -494,8 +496,8 @@ GEM rexml (3.4.4) rss (0.3.3) rexml - rubocop (1.89.0) - json (~> 2.3) + rubocop (1.91.0) + json (>= 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) parallel (>= 1.10) @@ -512,16 +514,6 @@ GEM rexml ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - sass-rails (6.0.0) - sassc-rails (~> 2.1, >= 2.1.1) - sassc (2.4.0) - ffi (~> 1.9) - sassc-rails (2.1.2) - railties (>= 4.0.0) - sassc (>= 2.0) - sprockets (> 3.0) - sprockets-rails - tilt sawyer (0.9.3) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) @@ -538,7 +530,7 @@ GEM logger (>= 1.6.2) rack (>= 3.1.0) redis-client (>= 0.23.2) - solargraph (0.60.3) + solargraph (0.60.4) ast (~> 2.4.3) backport (~> 1.2) benchmark (~> 0.4) @@ -563,7 +555,7 @@ GEM yard (~> 0.9, >= 0.9.24) yard-activesupport-concern (~> 0.0) yard-solargraph (~> 0.1) - sorbet-runtime (0.6.13421) + sorbet-runtime (0.6.13506) sord (7.1.0) commander (~> 5.0) parlour (~> 9.1) @@ -571,14 +563,6 @@ GEM rbs (>= 3.0, < 5) sorbet-runtime yard - sprockets (4.2.2) - concurrent-ruby (~> 1.0) - logger - rack (>= 2.2.4, < 4) - sprockets-rails (3.5.2) - actionpack (>= 6.1) - activesupport (>= 6.1) - sprockets (>= 3.0.0) sshkit (1.25.0) base64 logger @@ -597,16 +581,11 @@ GEM tilt (2.9.0) timeout (0.6.1) tsort (0.2.0) - turbolinks (5.2.1) - turbolinks-source (~> 5.2) - turbolinks-source (5.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.1) - execjs (>= 0.3.0, < 3) - unicode-display_width (3.2.0) - unicode-emoji (~> 4.1) - unicode-emoji (4.2.0) + unicode-display_width (3.3.0) + unicode-emoji (~> 4.3) + unicode-emoji (4.3.0) uniform_notifier (1.18.0) uri (1.1.1) useragent (0.16.11) @@ -643,15 +622,14 @@ DEPENDENCIES api-pagination apollo-federation bcrypt_pbkdf - bootstrap-sass (~> 3.4.1) bullet capistrano (~> 3.20.0) capistrano-bundler capistrano-rails capistrano-rvm capistrano-sidekiq - coffee-rails connection_pool (< 3) + cssbundling-rails devise dotenv-rails ed25519 @@ -666,7 +644,8 @@ DEPENDENCIES graphql-query-resolver haml jbuilder - jquery-rails + jsbundling-rails + json (< 3) kaminari listen minitest (~> 5.1) @@ -676,26 +655,24 @@ DEPENDENCIES omniauth_openid_connect openssl pg + propshaft pry-byebug pry-rails puma puma_worker_killer rack-cors - rails (~> 7.2.2) + rails (~> 8.0) rails-controller-testing rbs redcarpet redis rss ruby-graphviz - sass-rails sdoc search_object search_object_graphql sidekiq solargraph - turbolinks - uglifier (>= 1.3.0) yajl-ruby BUNDLED WITH diff --git a/README.md b/README.md index 147d57a90..b68cc3efd 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,15 @@ Voctoweb is a rails application that provides a “YouTube like” user interfac ## APIs - Every talk (alias **event**, in other systems also called lecture or session) is assigned to exactly one **conference** (e.g. the _congress_ or lecture series like _datengarten_ or _openchaos_) and consists of multiple files alias **recordings**. These files can be video or audio recordings of the talk in different formats and languages (live-translation), subtitle tracks as srt or slides as pdf. + Every talk (alias **event**, in other systems also called lecture, session or generic item) is assigned to exactly one **conference** (e.g. the _congress_ or lecture series like _datengarten_ or _openchaos_) and consists of multiple files alias **recordings**. These files can be video or audio recordings of the talk in different formats and languages (live-translation), subtitle tracks as srt or slides as pdf. ### Public GraphQL API The newest API endpoint is at https://media.ccc.de/graphql, implementing a GraphQL endpoint with Apollo Federation. This allows clients to only request the attributes they need, while all data needed per screen can be fetched in a single request. We tried to clean up the type names and call talks `lecture` and files `resources` (previously known as recordings). Please create issues if you are missing anything. +#### Query a lecture with video format preference + ``` graphql query LectureQueryExample { lectureBySlug(slug: "36c3-10652-bahnmining_-_punktlichkeit_ist_eine_zier") { @@ -29,6 +31,11 @@ query LectureQueryExample { persons slug originalLanguage + video(prefer: [AV1, WEBM]) { + label + url + mimeType + } videos { label url @@ -38,6 +45,52 @@ query LectureQueryExample { } ``` +#### Query a conference with images + +``` graphql +query ConferenceQueryExample { + conference(id: "36c3") { + acronym + title + logo(prefer: SVG) { + url + mimeType + type + } + images { + url + mimeType + type + } + } +} +``` + +#### Query promoted items/talks + +``` graphql +query PromotedExample { + promoted(first: 10) { + nodes { + guid + title + promoted + conference { + acronym + title + } + video(prefer: MP4) { + url + mimeType + } + } + pageInfo { + hasNextPage + } + } +} +``` + ### Public JSON API @@ -192,6 +245,14 @@ You can now reach the voctoweb frontend at `http://localhost.c3voc.de/`. The bac The whole application directory is mounted into the containers, so all changes you make to the files are reflected inside the application automatically. To run commands inside the voctoweb container, run `docker compose run voctoweb $COMMAND`. If you ever need to rebuild the containers (because of new dependencies, for example), run the `docker compose build` command again. +JavaScript and CSS are bundled with esbuild and sass into `app/assets/builds/`, which is not checked in. `bin/docker-dev-up` builds them once; after changing anything under `app/javascript/` or `app/assets/stylesheets/` (or `package.json`), rebuild them with: + +```bash +docker compose run voctoweb sh -c 'pnpm install && pnpm run build && pnpm run build:css' +``` + +The container keeps its own `node_modules` in a named Docker volume, so a `node_modules` directory built on the host (macOS, for example) is not used inside the Linux container. Remove it with `docker compose down -v` if you ever need a clean reinstall. + Image and video files in `docker/content` are tried first, if missing live data from media.ccc.de is used. ## Install for Production diff --git a/app/admin/conference.rb b/app/admin/conference.rb index f674466ff..ef499cbd9 100644 --- a/app/admin/conference.rb +++ b/app/admin/conference.rb @@ -12,6 +12,7 @@ column 'UI Path', :slug column :recordings_path column :images_path + column :schedule_url column :created_at do |conference| l(conference.created_at, format: :pretty_datetime) end diff --git a/app/assets/builds/.keep b/app/assets/builds/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js deleted file mode 100644 index 699bfd7b1..000000000 --- a/app/assets/config/manifest.js +++ /dev/null @@ -1,10 +0,0 @@ -//= link_tree ../images -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css -//= link graphiql/rails/application.js -//= link graphiql/rails/application.css - -// mediaelement icon sprites referenced directly via asset_path() in the player partials -//= link mejs-controls.svg -//= link mejs-skip-back.svg -//= link mejs-jump-forward.svg \ No newline at end of file diff --git a/app/assets/javascripts/active_admin.js b/app/assets/javascripts/active_admin.js deleted file mode 100644 index d2b66c59f..000000000 --- a/app/assets/javascripts/active_admin.js +++ /dev/null @@ -1 +0,0 @@ -//= require active_admin/base diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index 0036b3729..000000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,48 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. -// -// Read Sprockets README (https://github.com/rails/sprockets#directives) for details -// about supported directives. -// -//= require replacehash -//= require jquery -//= require jquery_ujs -//= require bootstrap-sprockets -//= require turbolinks - -//= require purl.min -//= require handlebars.min-latest -//= require jquery.bxslider-v4.2.1d-ssfrontend -//= require slider -//= require theme -//= require feed_toggle -//= require mastodon-share - -//= require mirrorbrain-fix -//= require subtitle-loader -//= require player-deeplinks -//= require mediaelement-and-player -//= require mejs-player -//= require player-gestures -//= require source-chooser/source-chooser -//= require speed/speed -//= require postroll/postroll -//= require skip-back/skip-back -//= require jump-forward/jump-forward -//= require playlist/playlist -// when adding plugins here, also add them to oembed-player.js - -//= require player.umd -//= require clappr-thumbnails-plugin -//= require clappr-playback-rate-plugin - -//= require relive-seek - -//= require timelens -//= require activate-timelens diff --git a/app/assets/javascripts/clappr-dash-shaka-playback.js b/app/assets/javascripts/clappr-dash-shaka-playback.js deleted file mode 100644 index 49d24a4a8..000000000 --- a/app/assets/javascripts/clappr-dash-shaka-playback.js +++ /dev/null @@ -1,1362 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("clappr")); - else if(typeof define === 'function' && define.amd) - define(["clappr"], factory); - else if(typeof exports === 'object') - exports["DashShakaPlayback"] = factory(require("clappr")); - else - root["DashShakaPlayback"] = factory(root["Clappr"]); -})(window, function(__WEBPACK_EXTERNAL_MODULE_clappr__) { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "dist/"; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/clappr-dash-shaka-playback.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/shaka-player/dist/shaka-player.compiled.js": -/*!*****************************************************************!*\ - !*** ./node_modules/shaka-player/dist/shaka-player.compiled.js ***! - \*****************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -/* WEBPACK VAR INJECTION */(function(global) {(function(){var innerGlobal=typeof window!="undefined"?window:global;var exportTo={};(function(window,global){var p,aa="function"==typeof Object.defineProperties?Object.defineProperty:function(b,c,d){b!=Array.prototype&&b!=Object.prototype&&(b[c]=d.value)},ca="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this;function da(){da=function(){};ca.Symbol||(ca.Symbol=ea)}var ea=function(){var b=0;return function(c){return"jscomp_symbol_"+(c||"")+b++}}(); -function fa(){da();var b=ca.Symbol.iterator;b||(b=ca.Symbol.iterator=ca.Symbol("iterator"));"function"!=typeof Array.prototype[b]&&aa(Array.prototype,b,{configurable:!0,writable:!0,value:function(){return ha(this)}});fa=function(){}}function ha(b){var c=0;return ia(function(){return cg&&(g=Math.max(g+f,0));g=g}});ka("Array.prototype.find",function(b){return b?b:function(b,d){return Pa(this,b,d).ee}});var Sa=this;Sa.a=!0; -function z(b,c){var d=b.split("."),e=Sa;d[0]in e||!e.execScript||e.execScript("var "+d[0]);for(var f;d.length&&(f=d.shift());)d.length||void 0===c?e[f]?e=e[f]:e=e[f]={}:e[f]=c}function Ta(b,c){function d(){}d.prototype=c.prototype;b.$f=c.prototype;b.prototype=new d;b.prototype.constructor=b;b.ng=function(b,d,g){return c.prototype[d].apply(b,Array.prototype.slice.call(arguments,2))}};/* - - Copyright 2016 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -function Ua(b){this.c=Math.exp(Math.log(.5)/b);this.b=this.a=0}function Wa(b,c,d){var e=Math.pow(b.c,c);d=d*(1-e)+e*b.a;isNaN(d)||(b.a=d,b.b+=c)}function Xa(b){return b.a/(1-Math.pow(b.c,b.b))};function Ya(){this.b=new Ua(2);this.c=new Ua(5);this.a=0}Ya.prototype.getBandwidthEstimate=function(b){return 128E3>this.a?b:Math.min(Xa(this.b),Xa(this.c))};function Za(){}function $a(){}function ab(){}window.console&&window.console.log.bind&&($a=console.warn.bind(console),Za=console.error.bind(console));var bb=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/;function cb(b){var c;b instanceof cb?(db(this,b.sa),this.Xa=b.Xa,this.oa=b.oa,hb(this,b.tb),this.fa=b.fa,ib(this,b.a.clone()),this.Oa=b.Oa):b&&(c=String(b).match(bb))?(db(this,c[1]||"",!0),this.Xa=jb(c[2]||""),this.oa=jb(c[3]||"",!0),hb(this,c[4]),this.fa=jb(c[5]||"",!0),ib(this,c[6]||"",!0),this.Oa=jb(c[7]||"")):this.a=new kb(null)}p=cb.prototype;p.sa="";p.Xa="";p.oa="";p.tb=null;p.fa="";p.Oa=""; -p.toString=function(){var b=[],c=this.sa;c&&b.push(lb(c,mb,!0),":");if(c=this.oa){b.push("//");var d=this.Xa;d&&b.push(lb(d,mb,!0),"@");b.push(encodeURIComponent(c).replace(/%25([0-9a-fA-F]{2})/g,"%$1"));c=this.tb;null!=c&&b.push(":",String(c))}if(c=this.fa)this.oa&&"/"!=c.charAt(0)&&b.push("/"),b.push(lb(c,"/"==c.charAt(0)?nb:ob,!0));(c=this.a.toString())&&b.push("?",c);(c=this.Oa)&&b.push("#",lb(c,pb));return b.join("")}; -p.resolve=function(b){var c=this.clone();"data"===c.sa&&(c=new cb);var d=!!b.sa;d?db(c,b.sa):d=!!b.Xa;d?c.Xa=b.Xa:d=!!b.oa;d?c.oa=b.oa:d=null!=b.tb;var e=b.fa;if(d)hb(c,b.tb);else if(d=!!b.fa){if("/"!=e.charAt(0))if(this.oa&&!this.fa)e="/"+e;else{var f=c.fa.lastIndexOf("/");-1!=f&&(e=c.fa.substr(0,f+1)+e)}if(".."==e||"."==e)e="";else if(-1!=e.indexOf("./")||-1!=e.indexOf("/.")){f=0==e.lastIndexOf("/",0);e=e.split("/");for(var g=[],h=0;hc)throw Error("Bad port number "+c);b.tb=c}else b.tb=null}function ib(b,c,d){c instanceof kb?b.a=c:(d||(c=lb(c,qb)),b.a=new kb(c))} -function jb(b,c){return b?c?decodeURI(b):decodeURIComponent(b):""}function lb(b,c,d){return"string"==typeof b?(b=encodeURI(b).replace(c,rb),d&&(b=b.replace(/%25([0-9a-fA-F]{2})/g,"%$1")),b):null}function rb(b){b=b.charCodeAt(0);return"%"+(b>>4&15).toString(16)+(b&15).toString(16)}var mb=/[#\/\?@]/g,ob=/[#\?:]/g,nb=/[#\?]/g,qb=/[#\?@]/g,pb=/#/g;function kb(b){this.a=b||null}p=kb.prototype;p.ea=null;p.Xb=null; -p.add=function(b,c){if(!this.ea&&(this.ea={},this.Xb=0,this.a))for(var d=this.a.split("&"),e=0;e=b.i)if(b.c)b.a=1,b.b=b.f;else return d["return"](Promise.reject());e=b.a;b.a++;if(0==e)return d["return"]();f=b.b*(1+(2*Math.random()-1)*b.h);return u(d,new Promise(function(b){(new B(b)).O(f/1E3)}),2);case 2:b.b*=b.g,v(d)}})})}function ub(){return{maxAttempts:2,baseDelay:1E3,backoffFactor:2,fuzzFactor:.5,timeout:0}};function D(b,c,d,e){for(var f=[],g=3;g=e.priority)Qb[b]={priority:d,uf:c}}K.registerScheme=Rb;K.unregisterScheme=function(b){delete Qb[b]};K.prototype.wf=function(b){this.a.add(b)};K.prototype.registerRequestFilter=K.prototype.wf; -K.prototype.bg=function(b){this.a["delete"](b)};K.prototype.unregisterRequestFilter=K.prototype.bg;K.prototype.re=function(){this.a.clear()};K.prototype.clearAllRequestFilters=K.prototype.re;K.prototype.xf=function(b){this.b.add(b)};K.prototype.registerResponseFilter=K.prototype.xf;K.prototype.cg=function(b){this.b["delete"](b)};K.prototype.unregisterResponseFilter=K.prototype.cg;K.prototype.se=function(){this.b.clear()};K.prototype.clearAllResponseFilters=K.prototype.se; -function Sb(b,c){return{uris:b,method:"GET",body:null,headers:{},allowCrossSiteCredentials:!1,retryParameters:c,licenseRequestType:null}}K.prototype.destroy=function(){this.f=!0;this.a.clear();this.b.clear();return this.g.destroy()};K.prototype.destroy=K.prototype.destroy; -K.prototype.request=function(b,c){var d=this,e=new Tb;if(this.f){var f=Promise.reject(new D(2,7,7001));f["catch"](function(){});return new Ub(f,function(){return Promise.resolve()},e)}c.method=c.method||"GET";c.headers=c.headers||{};c.retryParameters=c.retryParameters?Ib(c.retryParameters):ub();c.uris=Ib(c.uris);f=Vb(this,b,c);var g=f.T(function(){return Wb(d,b,c,new tb(c.retryParameters,!1),0,null,e)}),h=g.T(function(c){return Xb(d,b,c)}),k=Date.now(),l=0;f.promise.then(function(){l=Date.now()-k}, -function(){});var m=0;g.promise.then(function(){m=Date.now()},function(){});f=h.T(function(c){var e=Date.now()-m,f=c.response;f.timeMs+=l;f.timeMs+=e;c.Ue||!d.c||f.fromCache||1!=b||d.c(f.timeMs,f.data.byteLength);return f},function(b){b&&(b.severity=2);throw b;});f=new Ub(f.promise,f.Fd,e);Ob(this.g,f);return f};K.prototype.request=K.prototype.request; -function Vb(b,c,d){var e=Cb(void 0),f={};b=r(b.a);for(var g=b.next();!g.done;f={$c:f.$c},g=b.next())f.$c=g.value,e=e.T(function(b){return function(){return b.$c(c,d)}}(f));return e.T(void 0,function(b){if(b&&7001==b.code)throw b;throw new D(2,1,1006,b);})} -function Wb(b,c,d,e,f,g,h){var k=new cb(d.uris[f]),l=k.sa,m=!1;l||(l=location.protocol,l=l.slice(0,-1),db(k,l),d.uris[f]=k.toString());var n=(l=Qb[l])?l.uf:null;if(!n)return Ab(new D(2,1,1E3,k));var q;return Db(zb(e)).T(function(){if(b.f)return Bb();q=Date.now();return n(d.uris[f],d,c,function(d,e,f){b.c&&1==c&&(b.c(d,e),m=!0,h.a=f)})}).T(function(b){void 0==b.timeMs&&(b.timeMs=Date.now()-q);return{response:b,Ue:m}},function(h){if(h&&7001==h.code)throw h;if(b.f)return Bb();if(h&&1==h.severity)return b.dispatchEvent(new I("retry", -{error:h instanceof D?h:null})),f=(f+1)%d.uris.length,Wb(b,c,d,e,f,h);throw h||g;})}function Xb(b,c,d){var e=Cb(void 0);b=r(b.b);for(var f=b.next();!f.done;f=b.next())e=e.T(f.value.bind(null,c,d.response));return e.T(function(){return d},function(b){if(b&&7001==b.code)throw b;var c=2;b instanceof D&&(c=b.severity);throw new D(c,1,1007,b);})}function Tb(){this.a=0}K.NumBytesRemainingClass=Tb;function Ub(b,c,d){H.call(this,b,c);this.b=d}var Yb=Ub;Yb.prototype=ma(H.prototype); -Yb.prototype.constructor=Yb;if(sa)sa(Yb,H);else for(var Zb in H)if("prototype"!=Zb)if(Object.defineProperties){var $b=Object.getOwnPropertyDescriptor(H,Zb);$b&&Object.defineProperty(Yb,Zb,$b)}else Yb[Zb]=H[Zb];Yb.$f=H.prototype;K.PendingRequest=Ub;Ub.all=Eb;Ub.notAbortable=Db;Ub.completed=Cb;Ub.aborted=Bb;Ub.failed=Ab;function ac(){this.b=new Hb}ac.prototype.a=function(){bc(this);this.b=null};function L(b,c,d,e){b.b&&(c=new cc(c,d,e),b.b.push(d,c))}function dc(b,c,d,e){function f(g){b.ma(c,d,f);e(g)}L(b,c,d,f)}ac.prototype.ma=function(b,c,d){if(this.b)for(var e=this.b.get(c)||[],f=0;f=b[c]}.bind(null,c);if(0==c[0]&&0==c[2])return uc(b,!1);if(0==c[1]&&0==c[3])return uc(b,!0);if(d(0)&&d(1)&&d(2)&&d(3))return sc(b);throw new D(2,2,2003);}z("shaka.util.StringUtils.fromBytesAutoDetect",xc); -function yc(b){b=encodeURIComponent(b);b=unescape(b);for(var c=new Uint8Array(b.length),d=0;dd&&(d+=Math.pow(2,32));d="0x"+d.toString(16)}b.g(new D(2,6,6006,c.message,c,d))}})} -p.Nd=function(b){this.f.delayLicenseRequestUntilPlayed&&this.m.paused&&!this.ja?this.F.push(b):Uc(this,b)}; -function Uc(b,c){var d=c.target,e=b.b.get(d),f=b.a.licenseServerUri,g=b.f.advanced[b.a.keySystem];"individualization-request"==c.messageType&&g&&g.individualizationServer&&(f=g.individualizationServer);f=Sb([f],b.f.retryParameters);f.body=c.message;f.method="POST";f.licenseRequestType=c.messageType;"com.microsoft.playready"!=b.a.keySystem&&"com.chromecast.playready"!=b.a.keySystem||ed(f);b.a.keySystem.startsWith("com.apple.fps")&&fd(f);b.w.nb.request(2,f).promise.then(function(b){if(this.c)return Promise.reject(); -this.a.keySystem.startsWith("com.apple.fps")&&gd(b);return d.update(b.data).then(function(){var b=this;this.w.onEvent(new I("drmsessionupdate"));e&&(e.ua&&e.ua.resolve(),(new B(function(){e.loaded=!0;dd(b)&&b.j.resolve()})).O(hd))}.bind(this))}.bind(b),function(b){this.c||(b=new D(2,6,6007,b),this.g(b),e&&e.ua&&e.ua.reject(b))}.bind(b))["catch"](function(b){this.c||(b=new D(2,6,6008,b.message),this.g(b),e&&e.ua&&e.ua.reject(b))}.bind(b))} -function ed(b){var c=uc(b.body,!0,!0);if(c.includes("PlayReadyKeyMessage")){c=(new DOMParser).parseFromString(c,"application/xml");for(var d=c.getElementsByTagName("HttpHeader"),e=0;e"===c.substr(0,5)&&""===c.substr(-6)&&(c=c.slice(5,-6));try{c=JSON.parse(c).ckc}catch(d){}b.data=Bc(c).buffer} -p.Ld=function(b){b=b.target;var c=this.b.get(b),d=!1;b.keyStatuses.forEach(function(b,e){if("string"==typeof e){var f=e;e=b;b=f}if("com.microsoft.playready"==this.a.keySystem&&16==e.byteLength&&!pc("Tizen")){f=new DataView(e);var g=f.getUint32(0,!0),l=f.getUint16(4,!0),m=f.getUint16(6,!0);f.setUint32(0,g,!1);f.setUint16(4,l,!1);f.setUint16(6,m,!1)}"com.microsoft.playready"==this.a.keySystem&&"status-pending"==b&&(b="usable");"status-pending"!=b&&(c.loaded=!0);"expired"==b&&(d=!0);f=Dc(new Uint8Array(e)); -this.ka.set(f,b)}.bind(this));var e=b.expiration-Date.now();(0>e||d&&1E3>e)&&c&&!c.ua&&(this.b["delete"](b),b.close()["catch"](function(){}));dd(this)&&(this.j.resolve(),this.K.O(id))};function Hc(b){var c=b.ka,d=b.S;d.clear();c.forEach(function(b,c){return d.set(c,b)});c=Array.from(d.values());c.length&&c.every(function(b){return"expired"==b})&&b.g(new D(2,6,6014));b.w.gc(hc(d))} -function jd(){function b(b){return t(function h(){var c,f,m;return y(h,function(h){switch(h.l){case 1:return ya(h,2),u(h,navigator.requestMediaKeySystemAccess(b,d),4);case 4:return c=h.s,m=(f=c.getConfiguration().sessionTypes)?f.includes("persistent-license"):!1,pc("Tizen 3")&&(m=!1),e.set(b,{persistentState:m}),u(h,c.createMediaKeys(),5);case 5:Aa(h,0);break;case 2:Da(h),e.set(b,null),v(h)}})})}var c=[{contentType:'video/mp4; codecs="avc1.42E01E"'},{contentType:'video/webm; codecs="vp8"'}],d=[{videoCapabilities:c, -persistentState:"required",sessionTypes:["persistent-license"]},{videoCapabilities:c}],e=new Map;c="org.w3.clearkey com.widevine.alpha com.microsoft.playready com.apple.fps.3_0 com.apple.fps.2_0 com.apple.fps.1_0 com.apple.fps com.adobe.primetime".split(" ").map(function(c){return b(c)});return Promise.all(c).then(function(){return hc(e)})} -function kd(b,c){var d=c.audio,e=c.video;if(d&&d.encrypted&&!ad(b,ic(d.mimeType,d.codecs))||e&&e.encrypted&&!ad(b,ic(e.mimeType,e.codecs)))return!1;var f=b.keySystem();return 0==c.drmInfos.length||c.drmInfos.some(function(b){return b.keySystem==f})} -function ld(b,c){if(!b.length)return c;if(!c.length)return b;for(var d=[],e=0;eb.end(0)-b.start(0)?null:b.length?b.end(b.length-1):null}function qd(b,c,d){d=void 0===d?0:d;return!b||!b.length||1==b.length&&1E-6>b.end(0)-b.start(0)||c>b.end(b.length-1)?!1:c+d>=b.start(0)}function rd(b,c){if(!b||!b.length||1==b.length&&1E-6>b.end(0)-b.start(0))return 0;for(var d=0,e=b.length-1;0<=e&&b.end(e)>c;--e)d+=b.end(e)-Math.max(b.start(e),c);return d} -function sd(b){if(!b)return[];for(var c=[],d=0;d>4).toString(16);f+=(h&15).toString(16);d=d.replace(e[0],f)}return d}function Cd(b,c){b.f=!0;b.b=new G;b.g=[];b.c=[];var d=new Uint8Array(c);b.a.push(d);b.a.flush();b.f&&b.b.reject(new D(2,3,3018));return b.b} -yd.prototype.i=function(b){this.c=b.captions;var c=new Uint8Array(b.data.byteLength+b.initSegment.byteLength);c.set(b.initSegment,0);c.set(b.data,b.initSegment.byteLength);this.g.push(c)};yd.prototype.h=function(){var b={data:Fc.apply(null,this.g),captions:this.c};this.b.resolve(b);this.f=!1};function Dd(b,c,d){this.startTime=b;this.direction=Ed;this.endTime=c;this.payload=d;this.region=new Fd;this.position=null;this.positionAlign=Gd;this.size=100;this.textAlign=Hd;this.writingMode=Id;this.lineInterpretation=Jd;this.line=null;this.lineHeight="";this.lineAlign=Kd;this.displayAlign=Ld;this.backgroundColor=this.color="";this.backgroundImage=null;this.fontSize="";this.fontWeight=Md;this.fontStyle=Nd;this.fontFamily="";this.textDecoration=[];this.wrapLine=!0;this.id=""}z("shaka.text.Cue",Dd); -var Gd="auto";Dd.positionAlign={LEFT:"line-left",RIGHT:"line-right",CENTER:"center",AUTO:Gd};var Hd="center",Od={LEFT:"left",RIGHT:"right",CENTER:Hd,START:"start",END:"end"};Dd.textAlign=Od;var Ld="after",Pd={BEFORE:"before",CENTER:"center",AFTER:Ld};Dd.displayAlign=Pd;var Ed="ltr";Dd.direction={HORIZONTAL_LEFT_TO_RIGHT:Ed,HORIZONTAL_RIGHT_TO_LEFT:"rtl"};var Id="horizontal-tb";Dd.writingMode={HORIZONTAL_TOP_TO_BOTTOM:Id,VERTICAL_LEFT_TO_RIGHT:"vertical-lr",VERTICAL_RIGHT_TO_LEFT:"vertical-rl"}; -var Jd=0;Dd.lineInterpretation={LINE_NUMBER:Jd,PERCENTAGE:1};var Kd="start",Qd={CENTER:"center",START:Kd,END:"end"};Dd.lineAlign=Qd;var Md=400;Dd.fontWeight={NORMAL:Md,BOLD:700};var Nd="normal",Rd={NORMAL:Nd,ITALIC:"italic",OBLIQUE:"oblique"};Dd.fontStyle=Rd;Dd.textDecoration={UNDERLINE:"underline",LINE_THROUGH:"lineThrough",OVERLINE:"overline"}; -function Fd(){this.id="";this.regionAnchorY=this.regionAnchorX=this.viewportAnchorY=this.viewportAnchorX=0;this.height=this.width=100;this.viewportAnchorUnits=this.widthUnits=this.heightUnits=Sd;this.scroll=Td}z("shaka.text.CueRegion",Fd);var Sd=1;Fd.units={PX:0,PERCENTAGE:Sd,LINES:2};var Td="";Fd.scrollMode={NONE:Td,UP:"up"};function Ud(b){this.g=null;this.c=b;this.f=this.m=0;this.h=Infinity;this.b=this.a=null;this.j="";this.i=new Map}var Vd={};z("shaka.text.TextEngine.registerParser",function(b,c){Vd[b]=c});z("shaka.text.TextEngine.unregisterParser",function(b){delete Vd[b]});function Wd(b){return Vd[b]||window.muxjs&&"application/cea-608"==b?!0:!1}Ud.prototype.destroy=function(){this.c=this.g=null;this.i.clear();return Promise.resolve()};function Xd(b,c){"application/cea-608"!=c&&(b.g=new Vd[c])} -Ud.prototype.Gc=function(b){var c={periodStart:0,segmentStart:null,segmentEnd:0};try{return this.g.parseMedia(new Uint8Array(b),c)[0].startTime}catch(d){throw new D(2,2,2009,d);}}; -function Yd(b,c,d,e){return Promise.resolve().then(function(){if(this.g&&this.c)if(null==d||null==e)this.g.parseInit(new Uint8Array(c));else{var b={periodStart:this.m,segmentStart:d,segmentEnd:e};b=this.g.parseMedia(new Uint8Array(c),b).filter(function(b){return b.startTime>=this.f&&b.startTime=this.b||(b<=this.a&&c>=this.b?this.a=this.b=null:b<=this.a&&cthis.a&&c>=this.b&&(this.b=b))}.bind(this))};Ud.prototype.me=function(b){this.c.append(b)};Ud.prototype.appendCues=Ud.prototype.me; -Ud.prototype.nc=function(b,c){this.j=b;var d=this.i.get(b);if(d)for(var e=r(d.keys()),f=e.next();!f.done;f=e.next())if(f=d.get(f.value))f=f.filter(function(b){return b.endTime<=c}),this.c.append(f)};Ud.prototype.setSelectedClosedCaptionId=Ud.prototype.nc; -function Zd(b,c,d,e,f){var g=d+" "+e,h=new Map;c=r(c);for(var k=c.next();!k.done;k=c.next()){var l=k.value;k=l.stream;h.has(k)||h.set(k,new Map);h.get(k).has(g)||h.get(k).set(g,[]);l.startTime+=f;l.endTime+=f;l.startTime>=b.f&&l.startTimed.end(0)-d.start(0)?null:1==d.length&&0>d.start(0)?0:d.length?d.start(0):null;return d}function fe(b,c){return"text"==c?b.a.b:pd(ee(b,c))}function ge(b,c,d){if("text"==c)return b=b.a,null==b.b||b.bc&&(c+=.001);this.b[b].timestampOffset=c;this.qb(b)};p.Kf=function(b,c,d){this.b[b].appendWindowStart=0;this.b[b].appendWindowEnd=d;this.b[b].appendWindowStart=c;this.qb(b)}; -p.Sf=function(b){this.c[b][0].p.reject(new D(2,3,3014,this.f.error?this.f.error.code:0))};p.qb=function(b){var c=this.c[b][0];c&&(c.p.resolve(),me(this,b))};function ie(b,c,d){if(b.v)return Promise.reject();d={start:d,p:new G};b.c[c].push(d);if(1==b.c[c].length)try{d.start()}catch(e){"QuotaExceededError"==e.name?d.p.reject(new D(2,3,3017,c)):d.p.reject(new D(2,3,3015,e)),me(b,c)}return d.p} -function le(b,c){if(b.v)return Promise.reject();var d=[],e;for(e in b.b){var f=new G,g={start:function(b){b.resolve()}.bind(null,f),p:f};b.c[e].push(g);d.push(f);1==b.c[e].length&&g.start()}return Promise.all(d).then(function(){try{c()}catch(l){var b=Promise.reject(new D(2,3,3015,l))}for(var d in this.b)me(this,d);return b}.bind(b),function(){return Promise.reject()}.bind(b))}function me(b,c){b.c[c].shift();var d=b.c[c][0];if(d)try{d.start()}catch(e){d.p.reject(new D(2,3,3015,e)),me(b,c)}};function ne(b,c){b=M(b);c=M(c);return b.split("-")[0]==c.split("-")[0]}function oe(b,c){b=M(b);c=M(c);var d=b.split("-"),e=c.split("-");return d[0]==e[0]&&1==d.length&&2==e.length}function M(b){var c=b.split("-");b=c[0]||"";c=c[1]||"";b=b.toLowerCase();b=pe.get(b)||b;return(c=c.toUpperCase())?b+"-"+c:b}function qe(b){return b.language?M(b.language):b.audio&&b.audio.language?M(b.audio.language):b.video&&b.video.language?M(b.video.language):"und"} -function re(b,c){for(var d=M(b),e=new Set,f=r(c),g=f.next();!g.done;g=f.next())e.add(M(g.value));f=r(e);for(g=f.next();!g.done;g=f.next())if(g=g.value,g==d)return g;f=r(e);for(g=f.next();!g.done;g=f.next())if(g=g.value,oe(g,d))return g;f=r(e);for(g=f.next();!g.done;g=f.next()){var h=g=g.value,k=d;h=M(h);k=M(k);h=h.split("-");k=k.split("-");if(2==h.length&&2==k.length&&h[0]==k[0])return g}e=r(e);for(g=e.next();!g.done;g=e.next())if(f=g.value,oe(d,f))return f;return null} -var pe=new Map([["aar","aa"],["abk","ab"],["afr","af"],["aka","ak"],["alb","sq"],["amh","am"],["ara","ar"],["arg","an"],["arm","hy"],["asm","as"],["ava","av"],["ave","ae"],["aym","ay"],["aze","az"],["bak","ba"],["bam","bm"],["baq","eu"],["bel","be"],["ben","bn"],["bih","bh"],["bis","bi"],["bod","bo"],["bos","bs"],["bre","br"],["bul","bg"],["bur","my"],["cat","ca"],["ces","cs"],["cha","ch"],["che","ce"],["chi","zh"],["chu","cu"],["chv","cv"],["cor","kw"],["cos","co"],["cre","cr"],["cym","cy"],["cze", -"cs"],["dan","da"],["deu","de"],["div","dv"],["dut","nl"],["dzo","dz"],["ell","el"],["eng","en"],["epo","eo"],["est","et"],["eus","eu"],["ewe","ee"],["fao","fo"],["fas","fa"],["fij","fj"],["fin","fi"],["fra","fr"],["fre","fr"],["fry","fy"],["ful","ff"],["geo","ka"],["ger","de"],["gla","gd"],["gle","ga"],["glg","gl"],["glv","gv"],["gre","el"],["grn","gn"],["guj","gu"],["hat","ht"],["hau","ha"],["heb","he"],["her","hz"],["hin","hi"],["hmo","ho"],["hrv","hr"],["hun","hu"],["hye","hy"],["ibo","ig"],["ice", -"is"],["ido","io"],["iii","ii"],["iku","iu"],["ile","ie"],["ina","ia"],["ind","id"],["ipk","ik"],["isl","is"],["ita","it"],["jav","jv"],["jpn","ja"],["kal","kl"],["kan","kn"],["kas","ks"],["kat","ka"],["kau","kr"],["kaz","kk"],["khm","km"],["kik","ki"],["kin","rw"],["kir","ky"],["kom","kv"],["kon","kg"],["kor","ko"],["kua","kj"],["kur","ku"],["lao","lo"],["lat","la"],["lav","lv"],["lim","li"],["lin","ln"],["lit","lt"],["ltz","lb"],["lub","lu"],["lug","lg"],["mac","mk"],["mah","mh"],["mal","ml"],["mao", -"mi"],["mar","mr"],["may","ms"],["mkd","mk"],["mlg","mg"],["mlt","mt"],["mon","mn"],["mri","mi"],["msa","ms"],["mya","my"],["nau","na"],["nav","nv"],["nbl","nr"],["nde","nd"],["ndo","ng"],["nep","ne"],["nld","nl"],["nno","nn"],["nob","nb"],["nor","no"],["nya","ny"],["oci","oc"],["oji","oj"],["ori","or"],["orm","om"],["oss","os"],["pan","pa"],["per","fa"],["pli","pi"],["pol","pl"],["por","pt"],["pus","ps"],["que","qu"],["roh","rm"],["ron","ro"],["rum","ro"],["run","rn"],["rus","ru"],["sag","sg"],["san", -"sa"],["sin","si"],["slk","sk"],["slo","sk"],["slv","sl"],["sme","se"],["smo","sm"],["sna","sn"],["snd","sd"],["som","so"],["sot","st"],["spa","es"],["sqi","sq"],["srd","sc"],["srp","sr"],["ssw","ss"],["sun","su"],["swa","sw"],["swe","sv"],["tah","ty"],["tam","ta"],["tat","tt"],["tel","te"],["tgk","tg"],["tgl","tl"],["tha","th"],["tib","bo"],["tir","ti"],["ton","to"],["tsn","tn"],["tso","ts"],["tuk","tk"],["tur","tr"],["twi","tw"],["uig","ug"],["ukr","uk"],["urd","ur"],["uzb","uz"],["ven","ve"],["vie", -"vi"],["vol","vo"],["wel","cy"],["wln","wa"],["wol","wo"],["xho","xh"],["yid","yi"],["yor","yo"],["zha","za"],["zho","zh"],["zul","zu"]]);function se(b,c,d){function e(b,c,d){return b>=c&&b<=d}var f=b.video;return f&&f.width&&f.height&&!(e(f.width,c.minWidth,Math.min(c.maxWidth,d.width))&&e(f.height,c.minHeight,Math.min(c.maxHeight,d.height))&&e(f.width*f.height,c.minPixels,c.maxPixels))||!e(b.bandwidth,c.minBandwidth,c.maxBandwidth)?!1:!0}function te(b,c,d){var e=!1;b.forEach(function(b){var f=b.allowedByApplication;b.allowedByApplication=se(b,c,d);f!=b.allowedByApplication&&(e=!0)});return e} -function ue(b,c,d,e){e.variants=e.variants.filter(function(e){if(b&&b.N&&!kd(b,e))return!1;var f=e.audio;e=e.video;return f&&!be(f)||e&&!be(e)||f&&c&&!ve(f,c)||e&&d&&!ve(e,d)?!1:!0});e.textStreams=e.textStreams.filter(function(b){return Wd(ic(b.mimeType,b.codecs))})}function ve(b,c){return b.mimeType!=c.mimeType||b.codecs.split(".")[0]!=c.codecs.split(".")[0]?!1:!0} -function we(b){var c=b.audio,d=b.video,e=c?c.codecs:null,f=d?d.codecs:null,g=[];f&&g.push(f);e&&g.push(e);var h=[];d&&h.push(d.mimeType);c&&h.push(c.mimeType);h=h[0]||null;var k=[];c&&k.push(c.kind);d&&k.push(d.kind);k=k[0]||null;var l=new Set;c&&c.roles.forEach(function(b){return l.add(b)});d&&d.roles.forEach(function(b){return l.add(b)});b={id:b.id,active:!1,type:"variant",bandwidth:b.bandwidth,language:b.language,label:null,kind:k,width:null,height:null,frameRate:null,mimeType:h,codecs:g.join(", "), -audioCodec:e,videoCodec:f,primary:b.primary,roles:Array.from(l),audioRoles:null,videoId:null,audioId:null,channelsCount:null,audioBandwidth:null,videoBandwidth:null,originalVideoId:null,originalAudioId:null,originalTextId:null};d&&(b.videoId=d.id,b.originalVideoId=d.originalId,b.width=d.width||null,b.height=d.height||null,b.frameRate=d.frameRate||null,b.videoBandwidth=d.bandwidth||null);c&&(b.audioId=c.id,b.originalAudioId=c.originalId,b.channelsCount=c.channelsCount,b.audioBandwidth=c.bandwidth|| -null,b.label=c.label,b.audioRoles=c.roles);return b}function xe(b){return{id:b.id,active:!1,type:"text",bandwidth:0,language:b.language,label:b.label,kind:b.kind||null,width:null,height:null,frameRate:null,mimeType:b.mimeType,codecs:b.codecs||null,audioCodec:null,videoCodec:null,primary:b.primary,roles:b.roles,audioRoles:null,videoId:null,audioId:null,channelsCount:null,audioBandwidth:null,videoBandwidth:null,originalVideoId:null,originalAudioId:null,originalTextId:b.originalId}} -function ye(b){b.__shaka_id||(b.__shaka_id=ze++);return b.__shaka_id}var ze=0;function Ae(b){return{id:ye(b),active:!1,type:"",bandwidth:0,language:M(b.language),label:b.label,kind:b.kind,width:null,height:null,frameRate:null,mimeType:null,codecs:null,audioCodec:null,videoCodec:null,primary:!1,roles:[],audioRoles:null,videoId:null,audioId:null,channelsCount:null,audioBandwidth:null,videoBandwidth:null,originalVideoId:null,originalAudioId:null,originalTextId:null}} -function Be(b){return b.allowedByApplication&&b.allowedByKeySystem}function Ce(b){return b.filter(function(b){return Be(b)})} -function De(b,c){var d=b.filter(function(b){return b.audio&&b.audio.channelsCount}),e=new Map;d=r(d);for(var f=d.next();!f.done;f=d.next()){f=f.value;var g=f.audio.channelsCount;e.has(g)||e.set(g,[]);e.get(g).push(f)}d=Array.from(e.keys());if(0==d.length)return b;f=d.filter(function(b){return b<=c});return f.length?e.get(Math.max.apply(null,f)):e.get(Math.min.apply(null,d))} -function Ee(b,c,d){var e=b,f=b.filter(function(b){return b.primary});f.length&&(e=f);var g=e.length?e[0].language:"";e=e.filter(function(b){return b.language==g});if(c){var h=re(M(c),b.map(function(b){return b.language}));h&&(e=b.filter(function(b){return M(b.language)==h}))}if(d){if(b=Fe(e,d),b.length)return b}else if(b=e.filter(function(b){return 0==b.roles.length}),b.length)return b;b=e.map(function(b){return b.roles}).reduce(td.uc,[]);return b.length?Fe(e,b[0]):e} -function Fe(b,c){return b.filter(function(b){return b.roles.includes(c)})}function Ge(b,c,d){for(var e=0;e=f.bandwidth/this.a.bandwidthDowngradeTarget&&c<=g&&(d=f)}this.g=Date.now();return d};N.prototype.chooseVariant=N.prototype.chooseVariant;N.prototype.enable=function(){this.f=!0}; -N.prototype.enable=N.prototype.enable;N.prototype.disable=function(){this.f=!1};N.prototype.disable=N.prototype.disable;N.prototype.segmentDownloaded=function(b,c){var d=this.b;if(!(16E3>c)){var e=8E3*c/b,f=b/1E3;d.a+=c;Wa(d.b,f,e);Wa(d.c,f,e)}if(null!=this.g&&this.f)a:{if(!this.i){if(!(128E3<=this.b.a))break a;this.i=!0}else if(Date.now()-this.g<1E3*this.a.switchInterval)break a;d=this.chooseVariant();this.b.getBandwidthEstimate(this.a.defaultBandwidthEstimate);this.h(d)}}; -N.prototype.segmentDownloaded=N.prototype.segmentDownloaded;N.prototype.getBandwidthEstimate=function(){return this.b.getBandwidthEstimate(this.a.defaultBandwidthEstimate)};N.prototype.getBandwidthEstimate=N.prototype.getBandwidthEstimate;N.prototype.setVariants=function(b){this.c=b};N.prototype.setVariants=N.prototype.setVariants;N.prototype.configure=function(b){this.a=b};N.prototype.configure=N.prototype.configure; -function Ie(b,c){b&&(c=c.filter(function(c){return se(c,b,{width:Infinity,height:Infinity})}));return c.sort(function(b,c){return b.bandwidth-c.bandwidth})};function Je(b,c){this.a=b;this.b=c}Je.prototype.toString=function(){return"v"+this.a+"."+this.b};function Ke(b,c){var d=new Je(2,6),e=Le,f=e.a,g=d.b-f.b;(0<(d.a-f.a||g)?e.c:e.b)(e.a,d,b,c)}function Me(b,c,d,e){$a([d,"has been deprecated and will be removed in",c,". We are currently at version",b,". Additional information:",e].join(" "))}function Ne(b,c,d,e){Za([d,"has been deprecated and has been removed in",c,". We are now at version",b,". Additional information:",e].join(""))}var Le=null;var Oe="ended play playing pause pausing ratechange seeked seeking timeupdate volumechange".split(" "),Pe="buffered currentTime duration ended loop muted paused playbackRate seeking videoHeight videoWidth volume".split(" "),Qe=["loop","playbackRate"],Re=["pause","play"],Se="abrstatuschanged adaptation buffering emsg error loading streaming texttrackvisibility timelineregionadded timelineregionenter timelineregionexit trackschanged unloading variantchanged textchanged".split(" "),Te={getAssetUri:2, -getAudioLanguages:2,getAudioLanguagesAndRoles:2,getBufferedInfo:2,getConfiguration:2,getExpiration:2,getPlaybackRate:2,getTextLanguages:2,getTextLanguagesAndRoles:2,getTextTracks:2,getStats:5,getVariantTracks:2,isAudioOnly:10,isBuffering:1,isInProgress:1,isLive:10,isTextTrackVisible:1,keySystem:10,seekRange:1,usingEmbeddedTextTrack:2,getLoadMode:10},Ue={getPlayheadTimeAsDate:1,getPresentationStartTimeAsDate:20},Ve=[["getConfiguration","configure"]],We=[["isTextTrackVisible","setTextTrackVisibility"]], -Xe="addTextTrack cancelTrickPlay configure resetConfiguration retryStreaming selectAudioLanguage selectEmbeddedTextTrack selectTextLanguage selectTextTrack selectVariantTrack setTextTrackVisibility trickPlay".split(" "),Ye=["attach","detach","load","unload"]; -function Ze(b){return JSON.stringify(b,function(b,d){if("function"!=typeof d){if(d instanceof Event||d instanceof I){var c={},f;for(f in d){var g=d[f];g&&"object"==typeof g?"detail"==f&&(c[f]=g):f in Event||(c[f]=g)}return c}if(d instanceof TimeRanges)for(c={__type__:"TimeRanges",length:d.length,start:[],end:[]},f=0;fd?"-Infinity":"Infinity":d;return c}})}function $e(b){return JSON.parse(b,function(b,d){return"NaN"==d?NaN:"-Infinity"==d?-Infinity:"Infinity"==d?Infinity:d&&"object"==typeof d&&"TimeRanges"==d.__type__?af(d):d&&"object"==typeof d&&"Uint8Array"==d.__type__?new Uint8Array(d.entries):d})}function af(b){return{length:b.length,start:function(c){return b.start[c]},end:function(c){return b.end[c]}}};function bf(b,c,d,e,f,g){this.N=b;this.f=new B(c);this.S=d;this.j=!1;this.F=e;this.K=f;this.w=g;this.b=this.h=!1;this.C="";this.i=null;this.m=this.Jd.bind(this);this.o=this.ef.bind(this);this.a={video:{},player:{}};this.v=0;this.c={};this.g=null}var cf=!1,df=null;p=bf.prototype;p.destroy=function(){ef(this);df&&ff(this);this.f&&(this.f.stop(),this.f=null);this.K=this.F=null;this.b=this.h=!1;this.o=this.m=this.g=this.c=this.a=this.i=null;return Promise.resolve()};p.da=function(){return this.b}; -p.Xc=function(){return this.C};p.init=function(){if(window.chrome&&chrome.cast&&chrome.cast.isAvailable){delete window.__onGCastApiAvailable;this.h=!0;this.f.oc();var b=new chrome.cast.SessionRequest(this.N);b=new chrome.cast.ApiConfig(b,this.Kd.bind(this),this.jf.bind(this),"origin_scoped");chrome.cast.initialize(b,function(){},function(){});cf&&this.f.O(.02);(b=df)&&b.status!=chrome.cast.SessionStatus.STOPPED?this.Kd(b):df=null}else window.__onGCastApiAvailable=function(b){b&&this.init()}.bind(this)}; -p.cd=function(b){this.i=b;this.b&&gf({type:"appData",appData:this.i})};p.cast=function(b){if(!this.h)return Promise.reject(new D(1,8,8E3));if(!cf)return Promise.reject(new D(1,8,8001));if(this.b)return Promise.reject(new D(1,8,8002));this.g=new G;chrome.cast.requestSession(this.Vc.bind(this,b),this.Id.bind(this));return this.g};p.Zb=function(){this.b&&(ef(this),df&&(ff(this),df.stop(function(){},function(){}),df=null))}; -p.get=function(b,c){if("video"==b){if(Re.includes(c))return this.Ud.bind(this,b,c)}else if("player"==b){if(Ue[c]&&!this.get("player","isLive")())return function(){};if(Xe.includes(c))return this.Ud.bind(this,b,c);if(Ye.includes(c))return this.zf.bind(this,b,c);if(Te[c])return this.Rd.bind(this,b,c)}return this.Rd(b,c)};p.set=function(b,c,d){this.a[b][c]=d;gf({type:"set",targetName:b,property:c,value:d})}; -p.Vc=function(b,c){df=c;c.addUpdateListener(this.m);c.addMessageListener("urn:x-cast:com.google.shaka.v2",this.o);this.Jd();gf({type:"init",initState:b,appData:this.i});this.g.resolve()};p.Id=function(b){var c=8003;switch(b.code){case "cancel":c=8004;break;case "timeout":c=8005;break;case "receiver_unavailable":c=8006}this.g.reject(new D(2,8,c,b))};p.Rd=function(b,c){return this.a[b][c]}; -p.Ud=function(b,c,d){for(var e=[],f=2;f LA_URL"))?b.textContent:""} -function wf(b,c,d){var e=[];d=r(d);for(var f=d.next();!f.done;f=d.next()){f=f.value;var g=tf.get(f.Xd);if(g){var h=vd(g,f.init||b);if(g=Af.get(g))h.licenseServerUri=g(f);e.push(h)}else for(f=c(f.node)||[],f=r(f),h=f.next();!h.done;h=f.next())e.push(h.value)}return e} -var Af=(new Map).set("com.widevine.alpha",function(b){return(b=P.td(b.node,"urn:microsoft","laurl"))?b.getAttribute("licenseUrl")||"":""}).set("com.microsoft.playready",function(b){b=P.td(b.node,"urn:microsoft:playready","pro");if(!b)return"";b=Bc(b.textContent);b=yf(b.buffer).filter(function(b){return 1===b.type})[0];if(!b)return"";b=uc(b.value,!0);return(b=P.Pd(b,"WRMHEADER"))?zf(b):""});function vf(b){var c=[];b=r(b);for(var d=b.next();!d.done;d=b.next())(d=Bf(d.value))&&c.push(d);return c} -function Bf(b){var c=b.getAttribute("schemeIdUri"),d=P.getAttributeNS(b,"urn:mpeg:cenc:2013","default_KID"),e=P.ud(b,"urn:mpeg:cenc:2013","pssh").map(P.ac);if(!c)return null;c=c.toLowerCase();if(d&&(d=d.replace(/-/g,"").toLowerCase(),d.includes(" ")))throw new D(2,4,4009);var f=[];try{f=e.map(function(b){return{initDataType:"cenc",initData:Bc(b),keyId:null}})}catch(g){throw new D(2,4,4007);}return{node:b,Xd:c,keyId:d,init:0x)if(q+1=x)break;x=Math.ceil((x-w)/A)-1}else{if(Infinity==l)break;else if(w/f>=l)break;x=Math.ceil((l*f-w)/A)-1}0this.I.byteLength&&Kf();var c=new Uint8Array(this.I.buffer,this.I.byteOffset+this.a,b);this.a+=b;return new Uint8Array(c)};R.prototype.readBytes=R.prototype.Va; -R.prototype.J=function(b){this.a+b>this.I.byteLength&&Kf();this.a+=b};R.prototype.skip=R.prototype.J;R.prototype.Wd=function(b){this.ab||b>this.I.byteLength)&&Kf();this.a=b};R.prototype.seek=R.prototype.seek;R.prototype.Wc=function(){for(var b=this.a;this.pa()&&0!=this.I.getUint8(this.a);)this.a+=1;b=new Uint8Array(this.I.buffer,this.I.byteOffset+b,this.a-b);this.a+=1;return sc(b)}; -R.prototype.readTerminatedString=R.prototype.Wc;function Kf(){throw new D(2,3,3E3);};function S(){this.c=[];this.b=[];this.a=!1}z("shaka.util.Mp4Parser",S);S.prototype.G=function(b,c){var d=Lf(b);this.c[d]=0;this.b[d]=c;return this};S.prototype.box=S.prototype.G;S.prototype.ca=function(b,c){var d=Lf(b);this.c[d]=1;this.b[d]=c;return this};S.prototype.fullBox=S.prototype.ca;S.prototype.stop=function(){this.a=!0};S.prototype.stop=S.prototype.stop; -S.prototype.parse=function(b,c){var d=new Uint8Array(b);d=new R(new DataView(d.buffer,d.byteOffset,d.byteLength),0);for(this.a=!1;d.pa()&&!this.a;)this.ic(0,d,c)};S.prototype.parse=S.prototype.parse; -S.prototype.ic=function(b,c,d){var e=c.aa(),f=c.D(),g=c.D();switch(f){case 0:f=c.I.byteLength-e;break;case 1:f=c.ub()}var h=this.b[g];if(h){var k=null,l=null;1==this.c[g]&&(l=c.D(),k=l>>>24,l&=16777215);g=e+f;d&&g>c.I.byteLength&&(g=c.I.byteLength);g-=c.aa();c=0>24&255,b>>16&255,b>>8&255,b&255)}S.typeToString=Pf;function Qf(b,c,d,e){var f,g=(new S).ca("sidx",function(b){f=Rf(c,e,d,b)});b&&g.parse(b);if(f)return f;throw new D(2,3,3004);} -function Rf(b,c,d,e){var f=[];e.reader.J(4);var g=e.reader.D();if(0==g)throw new D(2,3,3005);if(0==e.version){var h=e.reader.D();var k=e.reader.D()}else h=e.reader.ub(),k=e.reader.ub();e.reader.J(2);var l=e.reader.Jb();b=b+e.size+k;for(k=0;k>>31;m&=2147483647;var q=e.reader.D();e.reader.J(4);if(1==n)throw new D(2,3,3006);f.push(new Q(f.length,h/g-c,(h+q)/g-c,function(){return d},b,b+m-1));h+=q;b+=m}e.parser.stop();return f};function T(b){this.a=b}z("shaka.media.SegmentIndex",T);T.prototype.destroy=function(){this.a=null;return Promise.resolve()};T.prototype.destroy=T.prototype.destroy;T.prototype.find=function(b){for(var c=this.a.length-1;0<=c;--c){var d=this.a[c];if(b>=d.startTime&&bb||b>=this.a.length?null:this.a[b]};T.prototype.get=T.prototype.get;T.prototype.offset=function(b){for(var c=0;cg.startTime?0==d&&c.push(g):(.1b){this.a.splice(0,c);return}this.a=[]};T.prototype.evict=T.prototype.zc;function Sf(b,c){for(;b.a.length;)if(b.a[b.a.length-1].startTime>=c)b.a.pop();else break;for(;b.a.length;)if(0>=b.a[0].endTime)b.a.shift();else break;if(0!=b.a.length){var d=b.a[b.a.length-1];b.a[b.a.length-1]=new Q(d.position,d.startTime,c,d.c,d.b,d.a)}};function Tf(b){this.b=b;this.a=new R(b,0);Uf||(Uf=[new Uint8Array([255]),new Uint8Array([127,255]),new Uint8Array([63,255,255]),new Uint8Array([31,255,255,255]),new Uint8Array([15,255,255,255,255]),new Uint8Array([7,255,255,255,255,255]),new Uint8Array([3,255,255,255,255,255,255]),new Uint8Array([1,255,255,255,255,255,255,255])])}var Uf;Tf.prototype.pa=function(){return this.a.pa()}; -function Vf(b){var c=Wf(b);if(7=d&&!(c&1<<8-d);d++);if(8b||d&&b>=d?null:Math.floor(b/e)},getSegmentReference:function(b){var c=b*e,n=c+e;d&&(n=Math.min(n,d));return 0>n||d&&c>=d?null:new Q(b,c,n,function(){var d=Cf(h,l,b+f,k,c*g);return ud(m,[d])},0,null)}}} -function ng(b,c){for(var d=[],e=0;eb.o||b.i.O(Math.max(3,b.o-c,Xa(b.v)))} -function xg(b,c,d){c=c||{contentType:"",mimeType:"",codecs:"",emsgSchemeIdUris:[],frameRate:void 0,Sc:null};d=d||c.la;var e=P.rb,f=P.ve,g=P.P(b,"BaseURL").map(P.ac),h=b.getAttribute("contentType")||c.contentType,k=b.getAttribute("mimeType")||c.mimeType,l=b.getAttribute("codecs")||c.codecs;f=P.H(b,"frameRate",f)||c.frameRate;var m=P.P(b,"InbandEventStream"),n=c.emsgSchemeIdUris.slice();m=r(m);for(var q=m.next();!q.done;q=m.next())q=q.value.getAttribute("schemeIdUri"),n.includes(q)||n.push(q);m=P.P(b, -"AudioChannelConfiguration");m=Cg(m)||c.Sc;h||(h=Ag(k,l));return{la:ud(d,g),Lb:P.Yb(b,"SegmentBase")||c.Lb,Ga:P.Yb(b,"SegmentList")||c.Ga,Nb:P.Yb(b,"SegmentTemplate")||c.Nb,width:P.H(b,"width",e)||c.width,height:P.H(b,"height",e)||c.height,contentType:h,mimeType:k,codecs:l,frameRate:f,emsgSchemeIdUris:n,id:b.getAttribute("id"),Sc:m}} -function Cg(b){for(var c=0;c>=1;return b}}}return null}function Bg(b){var c=b.Lb?1:0;c+=b.Ga?1:0;c+=b.Nb?1:0;if(0==c)return"text"==b.contentType||"application"==b.contentType?!0:!1;1!=c&&(b.Lb&&(b.Ga=null),b.Nb=null);return!0} -function Dg(b,c,d,e){c=ud(c,[d]);c=Sb(c,b.b.retryParameters);c.method=e;c=b.a.networkingEngine.request(4,c);Ob(b.g,c);return c.promise.then(function(b){if("HEAD"==e){if(!b.headers||!b.headers.date)return 0;b=b.headers.date}else b=sc(b.data);b=Date.parse(b);return isNaN(b)?0:b-Date.now()})} -function wg(b,c,d){d=d.map(function(b){return{scheme:b.getAttribute("schemeIdUri"),value:b.getAttribute("value")}});var e=b.b.dash.clockSyncUri;!d.length&&e&&d.push({scheme:"urn:mpeg:dash:utc:http-head:2014",value:e});return td.te(d,function(b){var d=b.scheme;b=b.value;switch(d){case "urn:mpeg:dash:utc:http-head:2014":case "urn:mpeg:dash:utc:http-head:2012":return Dg(this,c,b,"HEAD");case "urn:mpeg:dash:utc:http-xsdate:2014":case "urn:mpeg:dash:utc:http-iso:2014":case "urn:mpeg:dash:utc:http-xsdate:2012":case "urn:mpeg:dash:utc:http-iso:2012":return Dg(this, -c,b,"GET");case "urn:mpeg:dash:utc:direct:2014":case "urn:mpeg:dash:utc:direct:2012":return d=Date.parse(b),isNaN(d)?0:d-Date.now();case "urn:mpeg:dash:utc:http-ntp:2014":case "urn:mpeg:dash:utc:ntp:2014":case "urn:mpeg:dash:utc:sntp:2014":return $a("NTP UTCTiming scheme is not supported"),Promise.reject();default:return $a("Unrecognized scheme in UTCTiming element",d),Promise.reject()}}.bind(b))["catch"](function(){$a("A UTCTiming element should always be given in live manifests! This content may not play on clients with bad clocks!"); -return 0})}p.rf=function(b,c,d){var e=P.rb,f=d.getAttribute("schemeIdUri")||"",g=d.getAttribute("value")||"",h=P.H(d,"timescale",e)||1;P.P(d,"Event").forEach(function(d){var k=P.H(d,"presentationTime",e)||0,m=P.H(d,"duration",e)||0;k=k/h+b;m=k+m/h;null!=c&&(k=Math.min(k,b+c),m=Math.min(m,b+c));d={schemeIdUri:f,value:g,startTime:k,endTime:m,id:d.getAttribute("id")||"",eventElement:d};this.a.onTimelineRegionAdded(d)}.bind(this))}; -p.Cf=function(b,c,d){b=og(b,c,d,this.b.retryParameters);b=this.a.networkingEngine.request(1,b);Ob(this.g,b);return b.promise.then(function(b){return b.data})};function Ag(b,c){return Wd(ic(b,c))?"text":b.split("/")[0]}U.Zc("mpd",pg);U.Kb("application/dash+xml",pg);function Eg(b,c,d,e){this.b=b;this.type=c;this.a=d;this.segments=e||null}function Fg(b,c,d,e){this.id=b;this.name=c;this.a=d;this.value=void 0===e?null:e}Fg.prototype.toString=function(){function b(b){return b.name+"="+(isNaN(Number(b.value))?'"'+b.value+'"':b.value)}var c="#"+this.name,d=this.a?this.a.map(b):[];this.value&&d.unshift(this.value);0c.length||"data"!=c[0])throw new D(2,1,1004,b);c=c.slice(1).join(":").split(",");if(2>c.length)throw new D(2,1,1004,b);var d=c[0];c=window.decodeURIComponent(c.slice(1).join(","));d=d.split(";");var e=null;1w.Pc&&(w.stream.presentationTimeOffset=-x,w.Mb.offset(x))}else for(b.b.ta(l),b.b.offset(-h),E=r(b.a.values()),F=E.next();!F.done;F=E.next())A=F.value,A.stream.presentationTimeOffset= -h,A.Mb.offset(-h),Sf(A.Mb,l);b.m={presentationTimeline:b.b,periods:[g],offlineSessionIds:[],minBufferTime:0};v(e)}})})} -function dh(b,c){return t(function e(){var f,g,h,k,l,m,n,q,x,w,A,C;return y(e,function(e){switch(e.l){case 1:return f=Jg,g=td,h=c.a,k=f.hb(c.a,"EXT-X-MEDIA"),l=k.filter(function(b){return"SUBTITLES"==fh(b,"TYPE")}.bind(b)),m=l.map(function(b){return gh(this,b)}.bind(b)),n=k.filter(function(b){return"CLOSED-CAPTIONS"==fh(b,"TYPE")}),hh(b,n),u(e,Promise.all(m),2);case 2:return q=e.s,x=f.hb(h,"EXT-X-STREAM-INF"),w=x.map(function(b){return ih(this,b,c)}.bind(b)),u(e,Promise.all(w),3);case 3:return A= -e.s,C=A.reduce(g.uc,[]),C=C.filter(function(b){return null!=b}),e["return"]({startTime:0,variants:C,textStreams:q})}})})} -function ih(b,c,d){return t(function f(){var g,h,k,l,m,n,q,x,w,A,C,E,F,V,Y,wa,Ba,ba,na,xa,ja,Va,Ca,vb,Ia,eb,fb;return y(f,function(f){switch(f.l){case 1:g=wd;h=Jg;k=Hg(c,"CODECS","avc1.42E01E,mp4a.40.2");l=jh(k.split(/\s*,\s*/));m=c.getAttribute("RESOLUTION");q=n=null;x=Hg(c,"FRAME-RATE");w=Number(fh(c,"BANDWIDTH"));m&&(A=m.value.split("x"),n=A[0],q=A[1]);C=h.hb(d.a,"EXT-X-MEDIA");C=C.filter(function(b){return"CLOSED-CAPTIONS"!=fh(b,"TYPE")});C=C.filter(function(b){var c=Hg(b,"URI")||"";return"SUBTITLES"== -(Hg(b,"TYPE")||"")||""!=c});E=Hg(c,"AUDIO");F=Hg(c,"VIDEO");E?C=h.Cc(C,"AUDIO",E):F&&(C=h.Cc(C,"VIDEO",F));if(V=kh(g.na,l)){if(Y=Hg(c,"SUBTITLES"))wa=h.Cc(C,"SUBTITLES",Y),wa.length&&(Ba=b.C.get(wa[0].id),Ba.stream.codecs=V);Kb(l,V)}ba=C.map(function(b){return lh(this,b,l)}.bind(b));na=[];xa=[];return u(f,Promise.all(ba),2);case 2:Va=f.s;Va=Va.filter(function(b){return null!=b});E?na=Va:F&&(xa=Va);vb=!1;na.length||xa.length?na.length?(eb=fh(c,"URI"),fb=na[0].pc,eb==fb?(Ca=g.wb,vb=!0):Ca=g.La):Ca= -g.wb:1==l.length?(Ia=kh(g.La,l),Ca=m||x||Ia?g.La:g.wb):(Ca=g.La,l=[l.join(",")]);if(vb){f.A(3);break}return u(f,mh(b,c,l,Ca),4);case 4:ja=f.s;case 3:if(ja)ja.stream.type==g.wb?na=[ja]:xa=[ja];else if(null===ja)return f["return"]([]);xa&&nh(xa);na&&nh(na);return f["return"](oh(b,na,xa,w,n,q,x))}})})}function nh(b){b.forEach(function(b){var c=b.stream.codecs.split(",");c=c.filter(function(b){return"mp4a.40.34"!=b});b.stream.codecs=c.join(",")})} -function oh(b,c,d,e,f,g,h){d.forEach(function(b){if(b=b.stream)b.width=Number(f)||void 0,b.height=Number(g)||void 0,b.frameRate=Number(h)||void 0}.bind(b));c.length||(c=[null]);d.length||(d=[null]);var k=[];c=r(c);for(var l=c.next();!l.done;l=c.next()){l=l.value;for(var m=r(d),n=m.next();!n.done;n=m.next()){var q=n.value;n=l?l.stream:null;var x=q?q.stream:null,w=l?l.drmInfos:null,A=q?q.drmInfos:null;q=(q?q.pc:"")+" - "+(l?l.pc:"");var C=void 0;if(n&&x)if(w.length&&A.length?0>4,0!=e&&2!=e||c(),3==e&&(e=b.ha(),b.J(e)),1!=b.D()>>8)b.seek(d+188),e=b.ha(),71!=e&&(b.seek(d+192),e=b.ha()),71!=e&&(b.seek(d+204),e=b.ha()),71!=e&&c(),b.Wd(1);else return b.J(3),d=b.ha()>>6,0!=d&&1!=d||c(),0==b.ha()&&c(),d=b.ha(),e=b.Jb(),b=b.Jb(),(1073741824*((d&14)>>1)+((e&65534)<<14|(b&65534)>>1))/9E4} -function Dh(b,c,d){b=ic(b,c);if(!Wd(b))return 0;c=new Ud(null);Xd(c,b);return c.Gc(d)}function jh(b){var c=new Set,d=[];b=r(b);for(var e=b.next();!e.done;e=b.next()){e=e.value;var f=lc(e)[0];c.has(f)||(d.push(e),c.add(f))}return d}function kh(b,c){for(var d=Eh[b],e=0;ef-.5)){c=!0;break a}}c=!1}return c};function Zh(b,c,d,e,f){var g=this;this.b=b;this.w=c;this.v=d;this.m=f;this.g=new ac;this.j=!1;this.o=b.readyState;this.f=!1;this.c=e;this.i=!1;L(this.g,b,"waiting",function(){return $h(g)});this.h=(new B(function(){$h(g)})).Ia(.25)}Zh.prototype.a=function(){this.g&&(this.g.a(),this.g=null);null!=this.h&&(this.h.stop(),this.h=null);this.c&&(this.c.a(),this.c=null);this.b=this.w=this.m=null};Zh.prototype.Uc=function(){this.i=!0;$h(this)}; -function $h(b){if(0!=b.b.readyState){if(b.b.seeking){if(!b.j)return}else b.j=!1;if(!b.b.paused){b.b.readyState!=b.o&&(b.f=!1,b.o=b.b.readyState);var c=b.v.smallGapLimit,d=b.b.currentTime,e=b.b.buffered;a:{if(e&&e.length&&!(1==e.length&&1E-6>e.end(0)-e.start(0))){var f=pc("Edge/")||pc("Trident/")||pc("Tizen")||pc("CrKey")?.5:.1;for(var g=0;gd&&(0==g||e.end(g-1)-d<=f)){f=g;break a}}f=null}if(null==f){if(b.c){b=b.c;d=b.g;e=Wh(d);d=d.a.currentTime;f=Date.now()/1E3;if(b.c!= -d||b.h!=e)b.f=f,b.c=d,b.h=e;d=f-b.f;d>=b.i&&e&&b.b(b.c,d)}}else if(0!=f||b.i){g=e.start(f);var h=b.w.wa();if(!(g>=h)){h=g-d;c=h<=c;var k=!1;.001>h||(c||b.f||(b.f=!0,d=new I("largegap",{currentTime:d,gapSize:h}),d.cancelable=!0,b.m(d),b.v.jumpLargeGaps&&!d.defaultPrevented&&(k=!0)),!c&&!k)||(0!=f&&e.end(f-1),b.b.currentTime=g)}}}}};function ai(b){var c=this;this.f=b;this.b=new Set;this.c=(new B(function(){bi(c,!1)})).Ia(.25)}ai.prototype.a=function(){this.c.stop();for(var b=r(this.b),c=b.next();!c.done;c=b.next())c.value.a();this.b.clear()};function bi(b,c){for(var d=r(b.b),e=d.next();!e.done;e=d.next())e.value.h(b.f.currentTime,c)};function ci(b){var c=[];b=r(b);for(var d=b.next();!d.done;d=b.next()){d=r(d.value.variants);for(var e=d.next();!e.done;e=d.next())c.push(e.value)}return c}function di(b,c){for(var d=null,e=r(b),f=e.next();!f.done;f=e.next())f=f.value,c>=f.startTime&&(d=f);return d};function ei(b){this.f=b;this.b=null;this.c=function(){}}ei.prototype.a=function(){this.b=this.f=null;this.c=function(){}};ei.prototype.h=function(b){var c=this.b,d=this.f.periods;b=di(d,b)||d[0];c!=b&&this.c(b);this.b=b};function fi(b,c){b.c=c};function gi(b){var c=this;this.b=b;this.g=!1;this.f=this.b.cc();this.c=new B(function(){c.b.Ed(.25*c.f)})}gi.prototype.a=function(){this.c&&(this.c.stop(),this.c=null);this.b=null};gi.prototype.set=function(b){this.f=b;hi(this)};function hi(b){b.c.stop();var c=b.g?0:b.f;0<=c?b.b.cc()!=c&&b.b.ed(c):(b.c.Ia(.25),0!=b.b.cc()&&b.b.ed(0))};function ii(b,c,d){this.b=b;this.g=c;this.h=d;this.c=new ac;this.f=new ji(b);0Math.abs(b.b.currentTime-c)?ni(b):(dc(b.c,b.b,"seeking",function(){ni(b)}),oi(b.f,0==b.b.currentTime?c:b.b.currentTime))}function ni(b){L(b.c,b.b,"seeking",function(){return b.g()})}function ji(b){var c=this;this.c=b;this.i=10;this.h=this.g=this.f=0;this.b=new B(function(){0>=c.f?c.b.stop():c.c.currentTime!=c.g?c.b.stop():(c.c.currentTime=c.h,c.f--)})}ji.prototype.a=function(){this.b&&(this.b.stop(),this.b=null);this.c=null}; -function oi(b,c){b.g=b.c.currentTime;b.h=c;b.f=b.i;b.c.currentTime=c;b.b.Ia(.1)};function pi(b){function c(){d.g=!0;null!=d.c&&(d.b.currentTime=d.c)}var d=this;this.b=b;this.g=!1;this.c=null;this.f=new ac;0==this.b.readyState?dc(this.f,this.b,"loadedmetadata",c):c()}pi.prototype.a=function(){this.f&&(this.f.a(),this.f=null);this.b=null};pi.prototype.o=function(b){this.c=this.g?this.c:b};pi.prototype.i=function(){return(this.g?this.b.currentTime:this.c)||0};pi.prototype.v=function(){}; -function qi(b,c,d,e,f,g){var h=this;this.c=b;this.b=c.presentationTimeline;this.C=c.minBufferTime||0;this.h=d;this.w=f;this.m=null;this.g=new Zh(b,c.presentationTimeline,d,ri(b,d),g);this.f=new ii(b,function(){a:{var b=h.g;b.j=!0;b.i=!1;b.f=!1;var c=mi(h.f);b=si(h,c);if(.001d-c&&(c=d-3);bb.b.Y()?b.b.ib():b.b.wa():0>c&&(c=b.b.wa()+c);return vi(b,ui(b,c))}qi.prototype.v=function(){this.g.Uc()};function vi(b,c){var d=b.b.Y();return c>=d?d-b.h.durationBackoff:c}function si(b,c){var d=qd.bind(null,b.c.buffered),e=Math.max(b.C,b.h.rebufferingGoal),f=b.h.safeSeekOffset,g=b.b.ib(),h=b.b.wa(),k=b.b.Y();3>h-g&&(g=h-3);var l=b.b.Cb(e),m=b.b.Cb(f);e=b.b.Cb(e+f);return c>=k?vi(b,c):c>h?h:c=l||d(c)?c:e} -function ui(b,c){var d=b.b.ib();if(cd?d:c}function ri(b,c){if(!c.stallEnabled)return null;var d=c.stallSkip,e=new Vh(new Yh(b),c.stallThreshold);Xh(e,function(){b.currentTime+=d});return e};function wi(){this.c=function(){};this.b=new Set}wi.prototype.a=function(){this.c=function(){};this.b.clear()};function xi(b,c){b.c=c};function yi(b){var c=this;this.i=b;this.g=new Map;this.b=function(){};this.c=function(){};this.f=function(){};this.j=[{Za:null,Ya:zi,Sa:function(b,e){return c.b(b,e)}},{Za:Ai,Ya:zi,Sa:function(b,e){return c.b(b,e)}},{Za:Bi,Ya:zi,Sa:function(b,e){return c.b(b,e)}},{Za:zi,Ya:Ai,Sa:function(b,e){return c.c(b,e)}},{Za:zi,Ya:Bi,Sa:function(b,e){return c.c(b,e)}},{Za:Ai,Ya:Bi,Sa:function(b,e){return c.f(b,e)}},{Za:Bi,Ya:Ai,Sa:function(b,e){return c.f(b,e)}}]} -yi.prototype.a=function(){this.i=null;this.g.clear();this.b=function(){};this.c=function(){};this.f=function(){}};yi.prototype.h=function(b,c){for(var d=r(this.i.b),e=d.next();!e.done;e=d.next()){e=e.value;var f=this.g.get(e),g=be.endTime?Bi:zi;this.g.set(e,g);for(var h=r(this.j),k=h.next();!k.done;k=h.next())k=k.value,k.Za==f&&k.Ya==g&&k.Sa(e,c)}};function Ci(b,c,d,e){b.b=c;b.c=d;b.f=e}var Ai=1,zi=2,Bi=3;function Di(b,c){this.a=c;this.c=b;this.g=null;this.j=1;this.v=Promise.resolve();this.h=[];this.i=new Map;this.b=new Map;this.o=!1;this.F=null;this.C=this.f=this.m=!1;this.w=0}Di.prototype.destroy=function(){for(var b=r(this.b.values()),c=b.next();!c.done;c=b.next())Ei(c.value);this.b.clear();this.i.clear();this.g=this.h=this.v=this.c=this.a=null;this.f=!0;return Promise.resolve()}; -Di.prototype.configure=function(b){this.g=b;this.F=new tb({maxAttempts:Math.max(b.retryParameters.maxAttempts,2),baseDelay:b.retryParameters.baseDelay,backoffFactor:b.retryParameters.backoffFactor,fuzzFactor:b.retryParameters.fuzzFactor,timeout:0},!0)}; -Di.prototype.start=function(){var b=this;return t(function d(){var e,f,g;return y(d,function(d){switch(d.l){case 1:return e=b.a.Qa(),f=Fi(b,e),g=b.a.Hd(b.c.periods[f]),g.variant||g.text?u(d,Gi(b,g.variant?g.variant.audio:null,g.variant?g.variant.video:null,g.text,e),2):d["return"](new D(2,5,5005));case 2:if(b.f)return d["return"]();b.a&&b.a.bf&&b.a.bf();v(d)}})})};function Hi(b){return Ii(b,"audio")}function Ji(b){return Ii(b,"video")} -function Ii(b,c){var d=b.b.get(c);return d?d.Fa||d.stream:null}function Ki(b,c){return t(function e(){var f,g,h,k,l,m,n,q;return y(e,function(e){switch(e.l){case 1:return f=wd,u(e,je(b.a.L,f.na),2);case 2:return b.w++,b.C=!1,g=b.w,h=b.a.L,k=new Map,l=new Set,k.set(f.na,c),l.add(c),u(e,h.init(k,!1),3);case 3:return b.f?e["return"]():u(e,Li(b,l),4);case 4:if(b.f)return e["return"]();b.w!=g||b.b.has(f.na)||b.C||(m=b.a.Qa(),n=Fi(b,m),q=Mi(c,n,0),b.b.set(f.na,q),Ni(b,q,0));v(e)}})})} -function Oi(b,c){var d=b.b.get("video");if(d){var e=d.stream;if(e)if(c){var f=e.trickModeVideo;f&&!d.Fa&&(Pi(b,f,!1,0),d.Fa=e)}else if(e=d.Fa)d.Fa=null,Pi(b,e,!0,0)}}function Qi(b,c,d,e){c.video&&Pi(b,c.video,d,e);c.audio&&Pi(b,c.audio,d,e)} -function Pi(b,c,d,e){var f=b.b.get(c.type);if(!f&&"text"==c.type&&b.g.ignoreTextStreamFailures)Ki(b,c);else if(f){var g=Ri(b,c);if(d&&g!=f.ya)b.b.forEach(function(c){Si(b,c)});else{f.Fa&&(c.trickModeVideo?(f.Fa=c,c=c.trickModeVideo):f.Fa=null);var h=b.h[g];h&&h.vb&&(h=b.i.get(c.id))&&h.vb&&f.stream!=c&&("text"==c.type&&ce(b.a.L,ic(c.mimeType,c.codecs)),f.stream=c,f.ec=!0,Ti(b,f,g)&&f.Hb.abort(),d&&(f.Na?f.qc=!0:f.Ea?(f.Ja=!0,f.Wb=e,f.qc=!0):(Ei(f),Ui(b,f,!0,e))))}}} -function Ti(b,c,d){if(!c.Hb)return!1;var e=b.a.Qa(),f=fe(b.a.L,c.type);b=(b=Vi(b,c,e,f,d))?b.a?b.a-b.b:null:null;if(null==b)return!1;(d=c.stream.initSegmentReference)&&(b+=(d.a?d.a-d.b:null)||0);return c.Hb.b.a>b} -function Wi(b){function c(c){var f=b.a.L;"text"==c?(c=f.a,c=null==c.a||null==c.b?!1:d>=c.a&&dc?b.a.L.ta(c):b.a.L.ta(Math.pow(2,32))} -function Zi(b,c){if(!b.f&&!c.Ea&&null!=c.Ba&&!c.Na)if(c.Ba=null,c.Ja)Ui(b,c,c.qc,c.Wb);else{try{var d=$i(b,c);null!=d&&(Ni(b,c,d),c.Eb=!1)}catch(e){aj(b,e);return}d=Array.from(b.b.values());bj(b,c);b.o&&d.every(function(b){return b.endOfStream})&&b.a.L.endOfStream().then(function(){if(!this.f){var b=this.a.L.Y();b=b.c.presentationTimeline.Y())return c.endOfStream=!0,"video"==c.type&&(f=b.b.get("text"))&&"application/cea-608"==f.stream.mimeType&&(f.endOfStream=!0),null;c.endOfStream=!1;c.ya=h;if(h!=g)return null; -if(k>=l)return.5;h=fe(b.a.L,c.type);h=Vi(b,c,e,h,g);if(!h)return 1;var m=Infinity;Array.from(b.b.values()).forEach(function(c){d(c)||(c=cj(b,c,e),m=Math.min(m,c))});if(f>=m+b.c.presentationTimeline.a)return 1;c.Vd=0;dj(b,c,e,g,h);return null}function cj(b,c,d){if(!c.lb||!c.xa)return Math.max(d,c.Vd);d=Ri(b,c.lb);return b.c.periods[d].startTime+c.xa.endTime} -function Vi(b,c,d,e,f){if(c.xa&&c.stream==c.lb)return ej(b,c,f,c.xa.position+1);c.xa?(d=Ri(b,c.lb),d=c.stream.findSegmentPosition(Math.max(0,b.c.periods[d].startTime+c.xa.endTime-b.c.periods[f].startTime))):d=c.stream.findSegmentPosition(Math.max(0,(e||d)-b.c.periods[f].startTime));if(null==d)return null;var g=null;null==e&&(g=ej(b,c,f,Math.max(0,d-1)));return g||ej(b,c,f,d)} -function ej(b,c,d,e){d=b.c.periods[d];c=c.stream.getSegmentReference(e);if(!c)return null;e=b.c.presentationTimeline;b=e.Db();e=e.jb();return d.startTime+c.endTimee?null:c} -function dj(b,c,d,e,f){var g=b.c.periods[e],h=c.stream,k=b.c.presentationTimeline.Y(),l=b.c.periods[e+1];e=fj(b,c,e,Math.max(0,g.startTime-.1),l?l.startTime+.01:k);c.Ea=!0;c.ec=!1;k=gj(b,c,f);Promise.all([e,k]).then(function(b){if(!this.f&&!this.m)return hj(this,c,d,g,h,f,b[1])}.bind(b)).then(function(){this.f||this.m||(c.Ea=!1,c.Yc=!1,c.Ja||this.a.Uc(),Ni(this,c,0),ij(this,h))}.bind(b))["catch"](function(b){this.f||this.m||(c.Ea=!1,"text"==c.type&&this.g.ignoreTextStreamFailures?this.b["delete"]("text"): -7001==b.code?(c.Ea=!1,c.Ba=null,Ni(this,c,0)):3017==b.code?jj(this,c,b):(c.Eb=!0,b.severity=2,aj(this,b)))}.bind(b))}function jj(b,c,d){if(!Array.from(b.b.values()).some(function(b){return b!=c&&b.Yc})){var e=Math.round(100*b.j);if(20=d?Promise.resolve():b.a.L.remove(c.type,f,f+d).then(function(){}.bind(b))} -function ij(b,c){if(!b.o){var d=Array.from(b.b.values());if(1!=d.length||"text"!=d[0].type)b.o=d.every(function(b){return"text"==b.type?!0:!b.Ja&&!b.Na&&b.xa});if(b.o){d=Ri(b,c);b.h[d]||Yi(b,d).then(function(){this.f||this.a.Gd()}.bind(b))["catch"](td.Gb);for(d=0;d=d&&202!=d)return f&&(e=f),{uri:e,data:c,headers:b,fromCache:!!b["x-shaka-from-cache"]};f=null;try{f=xc(c)}catch(h){}throw new D(401==d||403==d?2:1,1,1001,e,d,f,b,g);};function nj(b,c,d,e){var f=new nj.b;gc(c.headers).forEach(function(b,c){f.append(c,b)});var g=new nj.a,h={pd:!1,ce:!1};b=nj.h(b,d,{body:c.body||void 0,headers:f,method:c.method,signal:g.signal,credentials:c.allowCrossSiteCredentials?"include":void 0},h,e);b=new H(b,function(){h.pd=!0;g.abort();return Promise.resolve()});if(c=c.retryParameters.timeout){var k=new B(function(){h.ce=!0;g.abort()});k.O(c/1E3);b["finally"](function(){k.stop()})}return b}z("shaka.net.HttpFetchPlugin",nj); -nj.h=function(b,c,d,e,f){return t(function h(){var k,l,m,n,q,x,w,A,C,E,F,V,Y,wa;return y(h,function(h){switch(h.l){case 1:return k=nj.g,l=nj.c,x=q=0,w=Date.now(),ya(h,2),u(h,k(b,d),4);case 4:return m=h.s,A=m.clone().body.getReader(),E=(C=m.headers.get("Content-Length"))?parseInt(C,10):0,F=function(b){function c(){return t(function ja(){var d,e;return y(ja,function(h){switch(h.l){case 1:return ya(h,2),u(h,A.read(),4);case 4:d=h.s;Aa(h,3);break;case 2:return Da(h),h["return"]();case 3:d.done||(q+=d.value.byteLength); -e=Date.now();if(100c;if(!n||!q){f.A(3);break}return u(f,k.updateManifestExpiration(g.key(),c),3);case 3:return Ea(f),u(f,h.destroy(),10);case 10:Fa(f,0);break;case 2:Da(f), -f.A(3)}})})};U.Kb("application/x-offline-manifest",kk);function lk(b){var c=ck(b);return c&&"manifest"==c.a?lk.a(b):c&&"segment"==c.a?lk.b(c.key(),c):Ab(new D(2,1,9004,b))}z("shaka.offline.OfflineScheme",lk);lk.a=function(b){b={uri:b,data:new ArrayBuffer(0),headers:{"content-type":"application/x-offline-manifest"}};return Cb(b)};lk.b=function(b,c){var d=new Cj;return Cb(void 0).T(function(){return d.init()}).T(function(){return Gj(d,c.ra(),c.$())}).T(function(b){return b.getSegments([c.key()])}).T(function(b){return{uri:c,data:b[0].data,headers:{}}})["finally"](function(){return d.destroy()})}; -Rb("offline",lk);function mk(b,c,d){return t(function f(){var g,h,k,l,m,n;return y(f,function(f){switch(f.l){case 1:g=[];for(var q=[],w=r(d),A=w.next();!A.done;A=w.next()){A=A.value;for(var C=!1,E=r(q),F=E.next();!F.done;F=E.next())if(F=F.value,nk(F.info,A)){F.sessionIds.push(A.sessionId);C=!0;break}C||q.push({info:A,sessionIds:[A.sessionId]})}h=r(q);k=h.next();case 2:if(k.done){f.A(4);break}l=k.value;m=ok(b,c,l);return u(f,m,5);case 5:n=f.s;g=g.concat(n);k=h.next();f.A(2);break;case 4:return f["return"](g)}})})} -function ok(b,c,d){return t(function f(){var g,h;return y(f,function(f){switch(f.l){case 1:return g=new Gc({nb:c,onError:function(){},gc:function(){},onExpirationUpdated:function(){},onEvent:function(){}}),ya(f,2),g.configure(b),u(f,Oc(g,d.info.keySystem,d.info.licenseUri,d.info.serverCertificate,d.info.audioCapabilities,d.info.videoCapabilities),4);case 4:Aa(f,3);break;case 2:return Da(f),u(f,g.destroy(),5);case 5:return f["return"]([]);case 3:return ya(f,6),u(f,Vc(g),8);case 8:Aa(f,7);break;case 6:return Da(f), -u(f,g.destroy(),9);case 9:return f["return"]([]);case 7:return h=[],u(f,Promise.all(d.sessionIds.map(function(b){return t(function n(){return y(n,function(c){switch(c.l){case 1:return ya(c,2),u(c,Yc(g,b),4);case 4:h.push(b);Aa(c,0);break;case 2:Da(c),v(c)}})})})),10);case 10:return u(f,g.destroy(),11);case 11:return f["return"](h)}})})} -function nk(b,c){function d(b,c){return b.robustness==c.robustness&&b.contentType==c.contentType}return b.keySystem==c.keySystem&&b.licenseUri==c.licenseUri&&Mb(b.audioCapabilities,c.audioCapabilities,d)&&Mb(b.videoCapabilities,c.videoCapabilities,d)};function pk(b,c){var d=qk(),e=this;this.g=c;this.c=b;this.i=d;this.h=null;this.f=[];this.b=this.a=null;this.j=!0;this.m=Promise.resolve().then(function(){return rk(e)})}pk.prototype.destroy=function(){var b=this;return t(function d(){var e;return y(d,function(d){switch(d.l){case 1:return b.j=!1,b.b&&b.b.abort(),sk(b),u(d,b.m,2);case 2:b.a&&b.a.qa.Ua();for(var f=r(b.f),h=f.next();!h.done;h=f.next())e=h.value,e.qa.Ua();b.a=null;b.f=[];b.g=null;v(d)}})})}; -function tk(b,c){var d={pb:function(){},fc:function(){},Ua:function(){},onError:function(){},hc:function(){},rg:function(){}};b.f.push({create:c,qa:d});b.b&&b.b.abort();sk(b);return d} -function rk(b){return t(function d(){return y(d,function(d){switch(d.l){case 1:if(b.j){if(0==b.f.length||b.a&&!b.a.Ra)var e=!1;else{b.a&&(b.a.qa.Ua(),b.a=null);e=b.f.shift();var g=e.create(b.i);g?(e.qa.pb(),b.a={node:g.node,payload:g.payload,Ra:g.Ra,qa:e.qa}):e.qa.hc();e=!0}e?e=Promise.resolve():b.a?e=uk(b):(b.g.af(b.c),b.h=new G,e=b.h);return u(d,e,1)}d.A(0)}})})} -function uk(b){return t(function d(){var e,f;return y(d,function(d){switch(d.l){case 1:return b.c=b.g.Ke(b.c,b.i,b.a.node,b.a.payload),ya(d,2),b.b=b.g.ue(b.c,b.i,b.a.payload),u(d,b.b.promise,4);case 4:b.b=null;b.c==b.a.node&&(b.a.qa.fc(),b.a=null);Aa(d,0);break;case 2:e=Da(d);if(7001==e.code)b.a.qa.Ua();else b.a.qa.onError(e);b.a=null;b.b=null;f=b;return u(d,b.g.handleError(b.i,e),5);case 5:f.c=d.s,v(d)}})})}function sk(b){b.h&&(b.h.resolve(),b.h=null)};function vk(b){this.a=null;for(var c=0;cb});return!0};vk.prototype.remove=vk.prototype.remove; -vk.prototype.append=function(b){for(var c=xk,d=[],e=0;e=b.endTime)return null;var c=new VTTCue(b.startTime,b.endTime,b.payload);c.lineAlign=b.lineAlign;c.positionAlign=b.positionAlign;c.size=b.size;try{c.align=b.textAlign}catch(d){}"center"==b.textAlign&&"center"!=c.align&&(c.align="middle");"vertical-lr"==b.writingMode?c.vertical="lr":"vertical-rl"==b.writingMode&&(c.vertical="rl");1==b.lineInterpretation&&(c.snapToLines=!1);null!=b.line&&(c.line=b.line);null!=b.position&&(c.position=b.position);return c} -function wk(b,c){var d=b.mode;b.mode="showing"==d?"showing":"hidden";for(var e=b.cues,f=e.length-1;0<=f;f--){var g=e[f];g&&c(g)&&b.removeCue(g)}b.mode=d};function yk(b,c,d,e,f){var g=f in e,h=!0,k;for(k in c){var l=f+"."+k,m=g?e[f]:d[k];g||k in d?void 0===c[k]?void 0===m||g?delete b[k]:b[k]=Ib(m):m.constructor==Object&&c[k]&&c[k].constructor==Object?(b[k]||(b[k]=Ib(m)),l=yk(b[k],c[k],m,e,l),h=h&&l):typeof c[k]!=typeof m||null==c[k]||c[k].constructor!=m.constructor?h=!1:b[k]=c[k]:h=!1}return h}z("shaka.util.ConfigUtils.mergeConfigObjects",yk);function zk(){var b=5E5,c=Infinity;navigator.connection&&navigator.connection.type&&(b=1E6*navigator.connection.downlink,navigator.connection.saveData&&(c=360));var d={retryParameters:ub(),servers:{},clearKeys:{},advanced:{},delayLicenseRequestUntilPlayed:!1},e={retryParameters:ub(),availabilityWindowOverride:NaN,dash:{customScheme:function(b){if(b)return null},clockSyncUri:"",ignoreDrmInfo:!1,xlinkFailGracefully:!1,defaultPresentationDelay:10,ignoreMinBufferTime:!1,autoCorrectDrift:!0}},f={retryParameters:ub(), -failureCallback:function(){},rebufferingGoal:2,bufferingGoal:10,bufferBehind:30,ignoreTextStreamFailures:!1,alwaysStreamText:!1,startAtSegmentBoundary:!1,smallGapLimit:.5,jumpLargeGaps:!1,durationBackoff:1,forceTransmuxTS:!1,safeSeekOffset:5,stallEnabled:!0,stallThreshold:1,stallSkip:.1};pc("Web0S")&&(f.stallEnabled=!1);var g={trackSelectionCallback:function(b){return b},progressCallback:function(){},usePersistentLicense:!0},h={drm:d,manifest:e,streaming:f,offline:g,abrFactory:N,abr:{enabled:!0,defaultBandwidthEstimate:b, -switchInterval:8,bandwidthUpgradeTarget:.85,bandwidthDowngradeTarget:.95,restrictions:{minWidth:0,maxWidth:Infinity,minHeight:0,maxHeight:c,minPixels:0,maxPixels:Infinity,minBandwidth:0,maxBandwidth:Infinity}},preferredAudioLanguage:"",preferredTextLanguage:"",preferredVariantRole:"",preferredTextRole:"",preferredAudioChannelCount:2,restrictions:{minWidth:0,maxWidth:Infinity,minHeight:0,maxHeight:Infinity,minPixels:0,maxPixels:Infinity,minBandwidth:0,maxBandwidth:Infinity},playRangeStart:0,playRangeEnd:Infinity, -textDisplayFactory:function(){return null}};g.trackSelectionCallback=function(b){return Ak(b,h.preferredAudioLanguage)};return h}function Bk(b,c,d){var e={".drm.servers":"",".drm.clearKeys":"",".drm.advanced":{distinctiveIdentifierRequired:!1,persistentStateRequired:!1,videoRobustness:"",audioRobustness:"",serverCertificate:new Uint8Array(0),individualizationServer:""}};return yk(b,c,d||zk(),e,"")} -function Ak(b,c){var d=b.filter(function(b){return"variant"==b.type}),e=[],f=re(c,d.map(function(b){return b.language}));f&&(e=d.filter(function(b){return M(b.language)==f}));0==e.length&&(e=d.filter(function(b){return b.primary}));0==e.length&&(d.map(function(b){return b.language}),e=d);var g=e.filter(function(b){return b.height&&480>=b.height});g.length&&(g.sort(function(b,c){return c.height-b.height}),e=g.filter(function(b){return b.height==g[0].height}));d=[];if(e.length){var h=Math.floor(e.length/ -2);e.sort(function(b,c){return b.bandwidth-c.bandwidth});d.push(e[h])}e=r(b);for(h=e.next();!h.done;h=e.next())h=h.value,"text"==h.type&&d.push(h);return d};function Ck(){this.a=null;this.b=[]}function Dk(b,c){if(null==b.a)b.a={timestamp:Date.now()/1E3,state:c,duration:0};else{var d=Date.now()/1E3;b.a.duration=d-b.a.timestamp;b.a.state!=c&&(b.b.push(b.a),b.a={timestamp:d,state:c,duration:0})}}function Ek(b,c){var d=0;b.a&&b.a.state==c&&(d+=b.a.duration);for(var e=r(b.b),f=e.next();!f.done;f=e.next())f=f.value,d+=f.state==c?f.duration:0;return d} -function Fk(b){function c(b){return{timestamp:b.timestamp,state:b.state,duration:b.duration}}for(var d=[],e=r(b.b),f=e.next();!f.done;f=e.next())d.push(c(f.value));b.a&&d.push(c(b.a));return d};function Gk(){this.b=this.c=null;this.a=[]}function Hk(b,c,d){b.b!=c&&(b.b=c,b.a.push({timestamp:Date.now()/1E3,id:c.id,type:"text",fromAdaptation:d,bandwidth:null}))};function Ik(){this.f=this.j=this.c=this.h=this.i=this.g=this.m=NaN;this.a=new Ck;this.b=new Gk};function X(b,c){var d=this;J.call(this);this.g=Jk;this.a=null;this.$a=!1;this.i=new ac;this.rc=this.j=this.xb=this.b=this.m=this.f=this.Pb=this.V=this.Qb=this.K=this.bb=this.o=this.F=this.h=this.N=null;this.Ad=1E9;this.Tb=new Set;this.eb=!0;this.ka=null;this.yd=!1;this.wd=0;this.ja=null;this.C=new Gh;this.c=Kk(this);this.Ub={width:Infinity,height:Infinity};this.v=null;this.Rb=new Ph(this.c.preferredAudioLanguage,this.c.preferredVariantRole,this.c.preferredAudioChannelCount);this.Ka=this.c.preferredTextLanguage; -this.yb=this.c.preferredTextRole;c&&c(this);this.N=Lk(this);L(this.i,window,"online",function(){d.ad()});this.w={name:"detach"};this.S={name:"attach"};this.Ca={name:"unload"};this.yc={name:"manifest-parser"};this.xc={name:"manifest"};this.ab={name:"media-source"};this.sc={name:"drm-engine"};this.W={name:"load"};this.Ac={name:"src-equals-drm-engine"};this.cb={name:"src-equals"};var e=new Map;e.set(this.S,function(b,c){return Db(Mk(d,b,c))});e.set(this.w,function(b){b.u&&(d.i.ma(b.u,"error"),b.u=null); -d.a=null;b=Promise.resolve();return Db(b)});e.set(this.Ca,function(b){return Db(Nk(d,b))});e.set(this.ab,function(b){b=Ok(d,b);return Db(b)});e.set(this.yc,function(b,c){var e=Pk(d,b,c);return Db(e)});e.set(this.xc,function(b){return Qk(d,b)});e.set(this.sc,function(){var b=Rk(d);return Db(b)});e.set(this.W,function(b,c){return Db(Sk(d,b,c))});e.set(this.Ac,function(b){b=Tk(d,b);return Db(b)});e.set(this.cb,function(b,c){return Uk(d,b,c)});this.fb=new pk(this.w,{Ke:function(b,c,e,k){var f=null;b== -d.w&&(f=e==d.w?d.w:d.S);b==d.S&&(f=e==d.w||c.u!=k.u?d.w:e==d.S?d.S:e==d.ab||e==d.W?d.ab:e==d.cb?d.Ac:null);b==d.ab&&(f=e==d.W&&c.u==k.u?d.yc:d.Ca);b==d.yc&&(f=Vk(d.W,d.xc,d.Ca,e,c,k));b==d.xc&&(f=Vk(d.W,d.sc,d.Ca,e,c,k));b==d.sc&&(f=Vk(d.W,d.W,d.Ca,e,c,k));b==d.Ac&&(f=e==d.cb&&c.u==k.u?d.cb:d.Ca);if(b==d.W||b==d.cb)f=d.Ca;b==d.Ca&&(f=k.u&&c.u==k.u?d.S:d.w);return f},ue:function(b,c,h){d.dispatchEvent(new I("onstatechange",{state:b.name}));return e.get(b)(c,h)},handleError:function(b){return t(function h(){return y(h, -function(c){switch(c.l){case 1:return u(c,Nk(d,b),2);case 2:return c["return"](b.u?d.S:d.w)}})})},af:function(b){d.dispatchEvent(new I("onstateidle",{state:b.name}))}});b&&this.zb(b,!0)}Ta(X,J);z("shaka.Player",X); -X.prototype.destroy=function(){var b=this;return t(function d(){var e;return y(d,function(d){switch(d.l){case 1:if(b.g==Wk)return d["return"]();b.g=Wk;e=tk(b.fb,function(){return{node:b.w,payload:qk(),Ra:!1}});return u(d,new Promise(function(b){e.pb=function(){};e.fc=function(){b()};e.Ua=function(){b()};e.onError=function(){b()};e.hc=function(){b()}}),2);case 2:return u(d,b.fb.destroy(),3);case 3:b.i&&(b.i.a(),b.i=null);b.rc=null;b.j=null;b.c=null;if(!b.N){d.A(0);break}return u(d,b.N.destroy(),5); -case 5:b.N=null,v(d)}})})};X.prototype.destroy=X.prototype.destroy;X.version="v2.5.0";var Xk=["2","5"];Le=new function(b){this.a=b;this.c=Me;this.b=Ne}(new Je(Number(Xk[0]),Number(Xk[1])));var Yk=["output-restricted","internal-error"],Zk={};X.registerSupportPlugin=function(b,c){Zk[b]=c}; -X.isBrowserSupported=function(){return window.Promise&&window.Uint8Array&&Array.prototype.forEach&&window.MediaKeys&&window.navigator&&window.navigator.requestMediaKeySystemAccess&&window.MediaKeySystemAccess&&window.MediaKeySystemAccess.prototype.getConfiguration?mc()?!0:nc("application/x-mpegurl"):!1}; -X.probeSupport=function(){return jd().then(function(b){for(var c=U.vf(),d={},e=r('video/mp4; codecs="avc1.42E01E",video/mp4; codecs="avc3.42E01E",video/mp4; codecs="hev1.1.6.L93.90",video/mp4; codecs="hvc1.1.6.L93.90",video/mp4; codecs="hev1.2.4.L153.B0"; eotf="smpte2084",video/mp4; codecs="hvc1.2.4.L153.B0"; eotf="smpte2084",video/mp4; codecs="vp9",video/mp4; codecs="vp09.00.10.08",audio/mp4; codecs="mp4a.40.2",audio/mp4; codecs="ac-3",audio/mp4; codecs="ec-3",audio/mp4; codecs="opus",audio/mp4; codecs="flac",video/webm; codecs="vp8",video/webm; codecs="vp9",video/webm; codecs="vp09.00.10.08",audio/webm; codecs="vorbis",audio/webm; codecs="opus",video/mp2t; codecs="avc1.42E01E",video/mp2t; codecs="avc3.42E01E",video/mp2t; codecs="hvc1.1.6.L93.90",video/mp2t; codecs="mp4a.40.2",video/mp2t; codecs="ac-3",video/mp2t; codecs="ec-3",text/vtt,application/mp4; codecs="wvtt",application/ttml+xml,application/mp4; codecs="stpp"'.split(",")),f= -e.next();!f.done;f=e.next()){f=f.value;d[f]=mc()?Wd(f)?!0:MediaSource.isTypeSupported(f)||zd(f):nc(f);var g=f.split(";")[0];d[g]=d[g]||d[f]}b={manifest:c,media:d,drm:b};for(var h in Zk)b[h]=Zk[h]();return b})};X.prototype.zb=function(b,c){c=void 0===c?!0:c;if(this.g==Wk)return Promise.reject($k());var d=qk();d.u=b;mc()||(c=!1);var e=c?this.ab:this.S,f=tk(this.fb,function(){return{node:e,payload:d,Ra:!1}});f.pb=function(){};return al(f)};X.prototype.attach=X.prototype.zb; -X.prototype.detach=function(){var b=this;if(this.g==Wk)return Promise.reject($k());var c=tk(this.fb,function(){return{node:b.w,payload:qk(),Ra:!1}});c.pb=function(){};return al(c)};X.prototype.detach=X.prototype.detach;X.prototype.ld=function(b){var c=this;b=void 0===b?!0:b;if(this.g==Wk)return Promise.reject($k());mc()||(b=!1);var d=qk(),e=tk(this.fb,function(e){var f=e.u&&b?c.ab:e.u?c.S:c.w;d.u=e.u;return{node:f,payload:d,Ra:!1}});e.pb=function(){};return al(e)};X.prototype.unload=X.prototype.ld; -X.prototype.load=function(b,c,d){if(this.g==Wk)return Promise.reject($k());this.dispatchEvent(new I("loading"));var e=qk();e.uri=b;e.hd=Date.now()/1E3;d&&"string"!=typeof d&&(Ke("Loading with a manifest parser factory","Please register a manifest parser and for the mime-type."),e.va=function(){return new d});d&&"string"==typeof d&&(e.mimeType=d);void 0!==c&&(e.startTime=c);var f=bl(e)?this.cb:this.W,g=tk(this.fb,function(b){if(null==b.u)return null;e.u=b.u;return{node:f,payload:e,Ra:!0}});g.pb=function(){}; -return new Promise(function(b,c){g.hc=function(){return c(new D(2,7,7002))};g.fc=function(){return b()};g.Ua=function(){return c($k())};g.onError=function(b){return c(b)}})};X.prototype.load=X.prototype.load; -function bl(b){if(b.va)return!1;if(!mc())return!0;var c=b.mimeType;b=b.uri||"";c||(c={mp4:"video/mp4",m4v:"video/mp4",m4a:"audio/mp4",webm:"video/webm",ts:"video/mp2t",m3u8:"application/x-mpegurl",mp3:"audio/mpeg",aac:"audio/aac",flac:"audio/flac"}[U.getExtension(b)]);return c?nc(c)?U.isSupported(b,c)?!!navigator.vendor&&navigator.vendor.includes("Apple"):!0:!1:!1}function Mk(b,c,d){null==c.u&&(c.u=d.u,L(b.i,c.u,"error",function(){var c=cl(b);c&&b.Ha(c)}));b.a=c.u;return Promise.resolve()} -function Nk(b,c){return t(function e(){return y(e,function(e){switch(e.l){case 1:b.g!=Wk&&(b.g=Jk);b.dispatchEvent(new I("unloading"));c.currentTime=null;c.va=null;c.mimeType=null;c.startTime=null;c.uri=null;c.u&&(b.i.ma(c.u,"loadeddata"),b.i.ma(c.u,"playing"),b.i.ma(c.u,"pause"),b.i.ma(c.u,"ended"),b.i.ma(c.u,"ratechange"));b.bb&&(b.bb.a(),b.bb=null);b.Qb&&(b.Qb.stop(),b.Qb=null);if(!b.m){e.A(2);break}return u(e,b.m.stop(),3);case 3:b.m=null;case 2:if(!b.j){e.A(4);break}return u(e,b.j.stop(),4); -case 4:if(!b.f){e.A(6);break}return u(e,b.f.destroy(),7);case 7:b.f=null;case 6:b.o&&(b.o.a(),b.o=null);if(!b.F){e.A(8);break}return u(e,b.F.destroy(),9);case 9:b.F=null;case 8:c.u&&c.u.src&&(c.u.removeAttribute("src"),c.u.load());if(!b.h){e.A(10);break}return u(e,b.h.destroy(),11);case 11:b.h=null;case 10:b.C.a.clear(),b.xb=null,b.V=null,b.Tb.clear(),b.b=null,b.v=null,b.eb=!0,dl(b),v(e)}})})} -function Ok(b,c){return t(function e(){var f,g,h,k;return y(e,function(e){switch(e.l){case 1:return f=window.muxjs?new nd:new od,g=b.c.textDisplayFactory,h=new g,k=new $d(c.u,f,h),u(e,k.o,2);case 2:b.F=k,v(e)}})})} -function Pk(b,c,d){return t(function f(){var g,h,k;return y(f,function(f){switch(f.l){case 1:c.va=d.va;c.mimeType=d.mimeType;c.uri=d.uri;g=c.uri;h=b.N;b.xb=g;if(c.va){b.m=c.va();f.A(2);break}k=b;return u(f,U.create(g,h,b.c.manifest.retryParameters,c.mimeType),3);case 3:k.m=f.s;case 2:b.m.configure(b.c.manifest),v(f)}})})} -function Qk(b,c){var d=c.uri,e=b.N;b.Pb=new wi;xi(b.Pb,function(c){el(b,"timelineregionadded",c)});var f={networkingEngine:e,filterNewPeriod:function(c){return b.Bc(c)},filterAllPeriods:function(c){return fl(b,c)},onTimelineRegionAdded:function(c){var d=b.Pb;a:{var e=r(d.b);for(var f=e.next();!f.done;f=e.next())if(f=f.value,f.schemeIdUri==c.schemeIdUri&&f.startTime==c.startTime&&f.endTime==c.endTime){e=f;break a}e=null}null==e&&(d.b.add(c),d.c(c))},onEvent:function(c){return b.dispatchEvent(c)},onError:function(c){return b.Ha(c)}}; -return new H(Promise.resolve().then(function(){return t(function h(){var c;return y(h,function(e){switch(e.l){case 1:return c=b,u(e,b.m.start(d,f),2);case 2:c.b=e.s;b.dispatchEvent(new I("manifestparsed"));if(0==b.b.periods.length)throw new D(2,4,4014);gl(b.b.periods);v(e)}})})}),function(){return b.m.stop()})} -function Rk(b){return t(function d(){return y(d,function(d){switch(d.l){case 1:return b.h=new Gc({nb:b.N,onError:function(d){b.Ha(d)},gc:function(d){hl(b,d)},onExpirationUpdated:function(d,e){il(b,d,e)},onEvent:function(d){b.dispatchEvent(d)}}),b.h.configure(b.c.drm),u(d,Nc(b.h,ci(b.b.periods),b.b.offlineSessionIds),2);case 2:fl(b,b.b.periods),v(d)}})})} -function Sk(b,c,d){return t(function f(){var g,h,k,l,m,n,q,x,w;return y(f,function(f){switch(f.l){case 1:return c.startTime=d.startTime,g=c.u,h=c.uri,b.xb=h,b.v=new Ik,k=function(){return jl(b)},l=function(){var c=b.a.playbackRate;0!=c&&b.K.set(c)},L(b.i,g,"playing",k),L(b.i,g,"pause",k),L(b.i,g,"ended",k),L(b.i,g,"ratechange",l),m=b.c.abrFactory,b.j&&b.rc==m||(b.rc=m,b.j=new m,b.j.configure(b.c.abr)),kl(b,b.b.periods),b.Rb=new Ph(b.c.preferredAudioLanguage,b.c.preferredVariantRole,b.c.preferredAudioChannelCount), -b.Ka=b.c.preferredTextLanguage,ll(b.b.presentationTimeline,b.c.playRangeStart,b.c.playRangeEnd),u(f,b.h.zb(g),2);case 2:return b.j.init(function(c,d,f){d=void 0===d?!1:d;f=void 0===f?0:f;a:{var g=r(b.b.periods);for(var h=g.next();!h.done;h=g.next())if(h=h.value,h.variants.includes(c)){g=h;break a}g=null}ml(b,g,c,!0);b.f&&(Qi(b.f,c,d,f),nl(b))}),b.o=ol(b,c.startTime),b.bb=pl(b),b.K=new gi({cc:function(){return c.u.playbackRate},ed:function(b){c.u.playbackRate=b},Ed:function(b){c.u.currentTime+=b}}), -n=Math.max(b.b.minBufferTime,b.c.streaming.rebufferingGoal),ql(b,n),b.f=rl(b),b.f.configure(b.c.streaming),sl(b),b.g=tl,b.dispatchEvent(new I("streaming")),u(f,b.f.start(),3);case 3:b.c.streaming.startAtSegmentBoundary&&(q=b.o.i(),x=ul(b,q),b.o.o(x)),b.b.periods.forEach(b.Bc.bind(b)),vl(b),nl(b),w=wl(b),w.variants.some(function(b){return b.primary}),xl(b,w.variants),dc(b.i,g,"loadeddata",function(){b.v.c=Date.now()/1E3-d.hd}),v(f)}})})} -function Tk(b,c){return t(function e(){var f,g;return y(e,function(e){switch(e.l){case 1:return f=wd,b.h=new Gc({nb:b.N,onError:function(c){b.Ha(c)},gc:function(c){hl(b,c)},onExpirationUpdated:function(c,e){il(b,c,e)},onEvent:function(c){b.dispatchEvent(c)}}),b.h.configure(b.c.drm),g={id:0,language:"und",primary:!1,audio:null,video:{id:0,originalId:null,createSegmentIndex:Promise.resolve.bind(Promise),findSegmentPosition:function(){return null},getSegmentReference:function(){return null},initSegmentReference:null, -presentationTimeOffset:0,mimeType:"video/mp4",codecs:"",encrypted:!0,keyId:null,language:"und",label:null,type:f.La,primary:!1,trickModeVideo:null,emsgSchemeIdUris:null,roles:[],channelsCount:null,closedCaptions:null},bandwidth:100,drmInfos:[],allowedByApplication:!0,allowedByKeySystem:!0},u(e,Nc(b.h,[g],[]),2);case 2:return u(e,b.h.zb(c.u),0)}})})} -function Uk(b,c,d){function e(){return jl(b)}c.uri=d.uri;c.startTime=d.startTime;b.xb=c.uri;b.v=new Ik;b.o=new pi(c.u);null!=c.startTime&&b.o.o(c.startTime);b.K=new gi({cc:function(){return c.u.playbackRate},ed:function(b){c.u.playbackRate=b},Ed:function(b){c.u.currentTime+=b}});ql(b,b.c.streaming.rebufferingGoal);L(b.i,c.u,"playing",e);L(b.i,c.u,"pause",e);L(b.i,c.u,"ended",e);dc(b.i,c.u,"loadeddata",function(){b.v.c=Date.now()/1E3-d.hd});b.a.audioTracks&&(L(b.i,b.a.audioTracks,"addtrack",function(){return vl(b)}), -L(b.i,b.a.audioTracks,"removetrack",function(){return vl(b)}));if(b.a.textTracks){var f=b.a.textTracks;L(b.i,f,"addtrack",function(){return vl(b)});L(b.i,f,"removetrack",function(){return vl(b)})}c.u.src=c.uri;b.g=yl;b.dispatchEvent(new I("streaming"));var g=new G;b.a.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?g.resolve():b.a.error?g.reject(cl(b)):(dc(b.i,b.a,"loadeddata",function(){g.resolve()}),dc(b.i,b.a,"error",function(){g.reject(cl(b))}));return new H(g,function(){g.reject(new D(2,7,7001)); -return Promise.resolve()})}function gl(b){function c(b){return b.video&&b.audio||b.video&&b.video.codecs.includes(",")}b.some(function(b){return b.variants.some(c)})&&b.forEach(function(b){b.variants=b.variants.filter(c)})} -function sl(b){function c(b){var c="";b.video&&(c=lc(b.video.codecs)[0]);var d="";b.audio&&(d=lc(b.audio.codecs)[0]);return c+"-"+d}var d=b.b.periods.reduce(function(b,c){return b.concat(c.variants)},[]);d=De(d,b.c.preferredAudioChannelCount);var e=new Hb;d.forEach(function(b){var d=c(b);e.push(d,b)});var f=null,g=Infinity;e.forEach(function(b,c){var d=0,e=0;c.forEach(function(b){d+=b.bandwidth||0;++e});var h=d/e;h=b.a.duration-.1;break;case tl:a:{var e;(e=b.a.ended)||(e=b.F,e=e.g?"ended"==e.g.readyState:!0);if(e)c=!0;else{if(b.b.presentationTimeline.U()&&(e=b.b.presentationTimeline.jb(),pd(b.a.buffered)>=e)){c=!0;break a}c=!1}}break;default:c=!1}var f=rd(b.a.buffered,b.a.currentTime);e=b.V;var g=c,h=e.b.get(e.a);c=e.a;f=g||f>=h?Th:Uh;e.a=f;c!=f&&dl(b)})).Ia(.25)} -function rl(b){return new Di(b.b,{Qa:function(){return b.o.i()},L:b.F,nb:b.N,Hd:b.Ze.bind(b),Gd:b.pe.bind(b),onError:b.Ha.bind(b),onEvent:function(c){return b.dispatchEvent(c)},cf:b.df.bind(b),Uc:b.kf.bind(b)})} -X.prototype.configure=function(b,c){if(2==arguments.length&&"string"==typeof b){for(var d=b,e={},f=e,g=0,h=0;;){g=d.indexOf(".",g);if(0>g)break;if(0==g||"\\"!=d[g-1])h=d.substring(h,g).replace(/\\\./g,"."),f[h]={},f=f[h],h=g+1;g+=1}f[d.substring(h).replace(/\\\./g,".")]=c;b=e}d=Bk(this.c,b,Kk(this));zl(this);return d};X.prototype.configure=X.prototype.configure; -function zl(b){b.m&&b.m.configure(b.c.manifest);b.h&&b.h.configure(b.c.drm);if(b.f){b.f.configure(b.c.streaming);try{b.b.periods.forEach(b.Bc.bind(b))}catch(f){b.Ha(f)}var c=Hi(b.f),d=Ji(b.f),e=wl(b);c=Ge(c,d,e.variants);b.j&&c&&c.allowedByApplication&&c.allowedByKeySystem?xl(b,e.variants):Al(b,e)}b.j&&(b.j.configure(b.c.abr),b.c.abr.enabled&&!b.eb?b.j.enable():b.j.disable(),Bl(b))}X.prototype.getConfiguration=function(){var b=Kk(this);Bk(b,this.c,Kk(this));return b}; -X.prototype.getConfiguration=X.prototype.getConfiguration;X.prototype.Df=function(){for(var b in this.c)delete this.c[b];Bk(this.c,Kk(this),Kk(this));zl(this)};X.prototype.resetConfiguration=X.prototype.Df;X.prototype.Ee=function(){return this.g};X.prototype.getLoadMode=X.prototype.Ee;X.prototype.Ie=function(){return this.a};X.prototype.getMediaElement=X.prototype.Ie;X.prototype.Bb=function(){return this.N};X.prototype.getNetworkingEngine=X.prototype.Bb;X.prototype.$b=function(){return this.xb}; -X.prototype.getAssetUri=X.prototype.$b;X.prototype.He=function(){Ke("getManifestUri",'Please use "getAssetUri" instead.');return this.$b()};X.prototype.getManifestUri=X.prototype.He;X.prototype.U=function(){return this.b?this.b.presentationTimeline.U():this.a&&this.a.src?Infinity==this.a.duration:!1};X.prototype.isLive=X.prototype.U;X.prototype.Ta=function(){return this.b?this.b.presentationTimeline.Ta():!1};X.prototype.isInProgress=X.prototype.Ta; -X.prototype.We=function(){if(this.b){if(!this.b.periods.length)return!1;var b=this.b.periods[0].variants;return b.length?!b[0].video:!1}return this.a&&this.a.src?this.a.videoTracks?0==this.a.videoTracks.length:0==this.a.videoHeight:!1};X.prototype.isAudioOnly=X.prototype.We;X.prototype.Ff=function(){if(this.b){var b=this.b.presentationTimeline;return{start:b.ib(),end:b.wa()}}return this.a&&this.a.src&&(b=this.a.seekable,b.length)?{start:b.start(0),end:b.end(b.length-1)}:{start:0,end:0}}; -X.prototype.seekRange=X.prototype.Ff;X.prototype.keySystem=function(){return this.h?this.h.keySystem():""};X.prototype.keySystem=X.prototype.keySystem;X.prototype.drmInfo=function(){return this.h?this.h.a:null};X.prototype.drmInfo=X.prototype.drmInfo;X.prototype.bc=function(){return this.h?this.h.bc():Infinity};X.prototype.getExpiration=X.prototype.bc;X.prototype.Ic=function(){return this.V?this.V.a==Uh:!1};X.prototype.isBuffering=X.prototype.Ic; -X.prototype.Le=function(){if(this.K){var b=this.K;b=b.g?0:b.f}else b=0;return b};X.prototype.getPlaybackRate=X.prototype.Le;X.prototype.ag=function(b){0==b?$a("A trick play rate of 0 is unsupported!"):(this.g==yl&&this.K.set(b),this.g==tl&&(this.K.set(b),Oi(this.f,1=h.b.periods.length?h.b.presentationTimeline.Y():h.b.periods[x].startTime;A=w-m.startTime;if(Infinity==A)throw new D(1,4,4033);C=new Q(1,0,A,function(){return[b]},0,null);E={id:h.Ad++,originalId:null,createSegmentIndex:Promise.resolve.bind(Promise), -findSegmentPosition:function(){return 1},getSegmentReference:function(b){return 1==b?C:null},initSegmentReference:null,presentationTimeOffset:0,mimeType:e,codecs:f||"",kind:d,encrypted:!1,keyId:null,language:c,label:g||null,type:n.na,primary:!1,trickModeVideo:null,emsgSchemeIdUris:null,roles:[],channelsCount:null,closedCaptions:null};h.Tb.add(E);m.textStreams.push(E);return u(l,Ki(h.f,E),2);case 2:return(F=Ii(h.f,"text"))&&Hh(h.C,m,F),h.Tb["delete"](E),Al(h,m),vl(h),l["return"](xe(E))}})})}; -X.prototype.addTextTrack=X.prototype.addTextTrack;X.prototype.dd=function(b,c){this.Ub.width=b;this.Ub.height=c};X.prototype.setMaxHardwareResolution=X.prototype.dd;X.prototype.ad=function(){if(this.g==tl){var b=this.f;if(b.f)b=!1;else if(b.m)b=!1;else{for(var c=r(b.b.values()),d=c.next();!d.done;d=c.next())d=d.value,d.Eb&&(d.Eb=!1,Ni(b,d,.1));b=!0}}else b=!1;return b};X.prototype.retryStreaming=X.prototype.ad;X.prototype.Fe=function(){return this.b};X.prototype.getManifest=X.prototype.Fe; -X.prototype.Ge=function(){return this.m?this.m.constructor:null};X.prototype.getManifestParserFactory=X.prototype.Ge;function ml(b,c,d,e){Ih(b.C,c).variant=d;b=b.v.b;b.c!=d&&(b.c=d,b.a.push({timestamp:Date.now()/1E3,id:d.id,type:"variant",fromAdaptation:e,bandwidth:d.bandwidth}))}function Kk(b){var c=zk();c.streaming.failureCallback=function(c){var d=[1001,1002,1003];b.U()&&d.includes(c.code)&&(c.severity=1,b.ad())};c.textDisplayFactory=function(){return new vk(b.a)};return c} -function kl(b,c){for(var d=0;dd&&(d+=Math.pow(2,32)),d=d.toString(16));return new D(2,3,3016,c,d,b.a.error.message)} -function hl(b,c){if(b.f){var d=wl(b),e=!1,f=Object.keys(c),g=1==f.length&&"00"==f[0];f.length&&d.variants.forEach(function(b){He(b).forEach(function(d){var f=b.allowedByKeySystem;d.keyId&&(d=c[g?"00":d.keyId],b.allowedByKeySystem=!!d&&!Yk.includes(d));f!=b.allowedByKeySystem&&(e=!0)})});f=Hi(b.f);var h=Ji(b.f);(f=Ge(f,h,d.variants))&&!f.allowedByKeySystem&&Al(b,d);e&&(vl(b),xl(b,d.variants))}} -function il(b,c,d){if(b.m&&b.m.onExpirationUpdated)b.m.onExpirationUpdated(c,d);b.dispatchEvent(new I("expirationupdated"))}function ll(b,c,d){0=g&&(g=b.bandwidth);e.a[c.id]=f;e.a[d.id]=g}});b.textStreams.forEach(function(b){e.a[b.id]=52})});var h=ym(f),k=new Map;h=r(h);for(var l=h.next();!l.done;l=h.next()){l=l.value;var m=Am(b,c,d,e,f,l);k.set(l.id,m)}g.variants.forEach(function(b){b.audio&& -k.get(b.audio.id).variantIds.push(b.id);b.video&&k.get(b.video.id).variantIds.push(b.id)});return{startTime:g.startTime,streams:Array.from(k.values())}} -function Am(b,c,d,e,f,g){var h={id:g.id,originalId:g.originalId,primary:g.primary,presentationTimeOffset:g.presentationTimeOffset||0,contentType:g.type,mimeType:g.mimeType,codecs:g.codecs,frameRate:g.frameRate,kind:g.kind,language:g.language,label:g.label,width:g.width||null,height:g.height||null,initSegmentKey:null,encrypted:g.encrypted,keyId:g.keyId,segments:[],variantIds:[]};f=f.presentationTimeline.Db();var k=g.id;Bm(g,f,function(f){var l=og(f.c(),f.b,f.a,b.a.streaming.retryParameters);sj(c,k, -l,Tl(e,g.id,f),function(c){return d.addSegments([{data:c}]).then(function(c){b.c.push(c[0]);h.segments.push({startTime:f.startTime,endTime:f.endTime,dataKey:c[0]})})})});if(f=g.initSegmentReference)f=og(f.c(),f.b,f.a,b.a.streaming.retryParameters),sj(c,k,f,.5*Ul(e,g.id),function(c){return d.addSegments([{data:c}]).then(function(c){b.c.push(c[0]);h.initSegmentKey=c[0]})});return h} -function Bm(b,c,d){c=b.findSegmentPosition(c);for(var e=null==c?null:b.getSegmentReference(c);e;)d(e),e=b.getSegmentReference(++c)}function km(b){if(b.h.a)throw new D(2,9,7001);}function im(){if(!fm())throw new D(2,9,9E3);}function gm(b,c){return t(function e(){return y(e,function(e){switch(e.l){case 1:return b.g.push(c),za(e,2),u(e,c,4);case 4:return e["return"](e.s);case 2:Ea(e),Kb(b.g,c),Fa(e,0)}})})} -function vm(b){var c=[];b.periods.forEach(function(b){b.streams.forEach(function(b){null!=b.initSegmentKey&&c.push(b.initSegmentKey);b.segments.forEach(function(b){c.push(b.dataKey)})})});return c}Z.deleteAll=function(){return t(function c(){var d;return y(c,function(c){switch(c.l){case 1:return d=new Cj,za(c,2),u(c,d.erase(),2);case 2:return Ea(c),u(c,d.destroy(),5);case 5:Fa(c,0)}})})}; -function um(b,c,d,e){return t(function g(){var h,k,l;return y(g,function(g){switch(g.l){case 1:if(!e.drmInfo)return g["return"]();h=Ij(d);k=e.sessionIds.map(function(b){return{sessionId:b,keySystem:e.drmInfo.keySystem,licenseUri:e.drmInfo.licenseServerUri,serverCertificate:e.drmInfo.serverCertificate,audioCapabilities:tm(e,!1),videoCapabilities:tm(e,!0)}});return u(g,mk(c,b,k),2);case 2:return l=g.s,u(g,h.remove(l),3);case 3:return u(g,h.add(k.filter(function(b){return-1==l.indexOf(b.sessionId)})), -0)}})})}function ym(b){var c=new Set;b=r(b.periods);for(var d=b.next();!d.done;d=b.next()){d=d.value;for(var e=r(d.textStreams),f=e.next();!f.done;f=e.next())c.add(f.value);d=r(d.variants);for(e=d.next();!e.done;e=d.next())e=e.value,e.audio&&c.add(e.audio),e.video&&c.add(e.video)}return c}function om(b){if(0==b.periods.length)throw new D(2,4,4014);b=r(b.periods);for(var c=b.next();!c.done;c=b.next())Cm(c.value)} -function Cm(b){b.variants.map(function(b){return b.video});var c=new Set(b.variants.map(function(b){return b.audio}));b=b.textStreams;for(var d=r(c),e=d.next();!e.done;e=d.next()){e=r(c);for(var f=e.next();!f.done;f=e.next());}c=r(b);for(d=c.next();!d.done;d=c.next())for(d=r(b),e=d.next();!e.done;e=d.next());}Zk.offline=fm;z("shaka.polyfill.installAll",function(){for(var b=0;b=c.a.length)return b;for(var d=[],e=0;ed.length)return null;var f=null,g=b;for(b=null;g&&!(b=g.getAttribute(c))&&(g=g.parentNode,g instanceof Element););if(c=b)for(b=0;b= this._minDvrSize && this.getPlaybackType() === 'live'; - } - }, { - key: '_duration', - get: function get() { - if (!this.shakaPlayerInstance) return 0; - - return this.seekRange.end - this.seekRange.start; - } - }, { - key: '_startTime', - get: function get() { - return this.seekRange.start; - } - }, { - key: 'presentationTimeline', - get: function get() { - return this.shakaPlayerInstance.getManifest().presentationTimeline; - } - }], [{ - key: 'canPlay', - value: function canPlay(resource) { - var mimeType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; - - _shakaPlayer2.default.polyfill.installAll(); - var browserSupported = _shakaPlayer2.default.Player.isBrowserSupported(); - var resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || []; - return browserSupported && (resourceParts[1] === 'mpd' || mimeType.indexOf('application/dash+xml') > -1); - } - }, { - key: 'Events', - get: function get() { - return { - SHAKA_READY: 'shaka:ready' - }; - } - }]); - - function DashShakaPlayback() { - var _ref; - - _classCallCheck(this, DashShakaPlayback); - - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - var _this = _possibleConstructorReturn(this, (_ref = DashShakaPlayback.__proto__ || Object.getPrototypeOf(DashShakaPlayback)).call.apply(_ref, [this].concat(args))); - - _this._levels = []; - _this._pendingAdaptationEvent = false; - _this._isShakaReadyState = false; - - _this._minDvrSize = typeof _this.options.shakaMinimumDvrSize === 'undefined' ? 60 : _this.options.shakaMinimumDvrSize; - return _this; - } - - _createClass(DashShakaPlayback, [{ - key: 'getProgramDateTime', - value: function getProgramDateTime() { - return new Date((this.presentationTimeline.getPresentationStartTime() + this.seekRange.start) * 1000); - } - }, { - key: '_updateDvr', - value: function _updateDvr(status) { - this.trigger(_clappr.Events.PLAYBACK_DVR, status); - this.trigger(_clappr.Events.PLAYBACK_STATS_ADD, { 'dvr': status }); - } - }, { - key: 'seek', - value: function seek(time) { - if (time < 0) { - _clappr.Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.'); - time = this._duration; - } - // assume live if time within 3 seconds of end of stream - this.dvrEnabled && this._updateDvr(time < this._duration - 3); - time += this._startTime; - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'seek', this).call(this, time); - } - }, { - key: 'pause', - value: function pause() { - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'pause', this).call(this); - - if (this.dvrEnabled) this._updateDvr(true); - } - }, { - key: 'play', - value: function play() { - if (!this._player) { - this._setup(); - } - - if (!this.isReady) { - this.once(DashShakaPlayback.Events.SHAKA_READY, this.play); - return; - } - - this._stopped = false; - this._src = this.el.src; - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'play', this).call(this); - this._startTimeUpdateTimer(); - } - }, { - key: '_startTimeUpdateTimer', - value: function _startTimeUpdateTimer() { - var _this3 = this; - - this._stopTimeUpdateTimer(); - this._timeUpdateTimer = setInterval(function () { - _this3._onTimeUpdate(); - }, 100); - } - }, { - key: '_stopTimeUpdateTimer', - value: function _stopTimeUpdateTimer() { - this._timeUpdateTimer && clearInterval(this._timeUpdateTimer); - } - - // skipping HTML5Video `_setupSrc` (on tag video) - - }, { - key: '_setupSrc', - value: function _setupSrc() {} - - // skipping ready event on video tag in favor of ready on shaka - - }, { - key: '_ready', - value: function _ready() { - // override with no-op - } - }, { - key: '_onShakaReady', - value: function _onShakaReady() { - this._isShakaReadyState = true; - this.trigger(DashShakaPlayback.Events.SHAKA_READY); - this.trigger(_clappr.Events.PLAYBACK_READY, this.name); - } - }, { - key: 'error', - - - // skipping error handling on video tag in favor of error on shaka - value: function error(event) { - _clappr.Log.error('an error was raised by the video tag', event, this.el.error); - } - }, { - key: 'isHighDefinitionInUse', - value: function isHighDefinitionInUse() { - return !!this.highDefinition; - } - }, { - key: 'stop', - value: function stop() { - var _this4 = this; - - this._stopTimeUpdateTimer(); - clearInterval(this.sendStatsId); - this._stopped = true; - - if (this._player) { - this._sendStats(); - - this._player.unload().then(function () { - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'stop', _this4).call(_this4); - _this4._player = null; - _this4._isShakaReadyState = false; - }).catch(function () { - _clappr.Log.error('shaka could not be unloaded'); - }); - } else { - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'stop', this).call(this); - } - } - }, { - key: 'getPlaybackType', - value: function getPlaybackType() { - return (this.isReady && this._player.isLive() ? 'live' : 'vod') || ''; - } - }, { - key: 'selectTrack', - value: function selectTrack(track) { - if (track.type === 'text') { - this._player.selectTextTrack(track); - } else if (track.type === 'variant') { - this._player.selectVariantTrack(track); - if (track.mimeType.startsWith('video/')) { - // we trigger the adaptation event here - // because Shaka doesn't trigger its event on "manual" selection. - this._onAdaptation(); - } - } else { - throw new Error('Unhandled track type:', track.type); - } - } - - /** - * @override - */ - - }, { - key: '_enableShakaTextTrack', - value: function _enableShakaTextTrack(isEnable) { - // Shaka player use only one TextTrack object with video element to handle all text tracks - // It must be enabled or disabled in addition to call selectTextTrack() - if (!this.el.textTracks) { - return; - } - - this._shakaTTVisible = isEnable; - - Array.from(this.el.textTracks).filter(function (track) { - return track.kind === 'subtitles'; - }).forEach(function (track) { - return track.mode = isEnable === true ? 'showing' : 'hidden'; - }); - } - }, { - key: '_checkForClosedCaptions', - value: function _checkForClosedCaptions() { - if (this._ccIsSetup) { - return; - } - - if (this.hasClosedCaptionsTracks) { - this.trigger(_clappr.Events.PLAYBACK_SUBTITLE_AVAILABLE); - var trackId = this.closedCaptionsTrackId; - this.closedCaptionsTrackId = trackId; - } - this._ccIsSetup = true; - } - }, { - key: 'destroy', - value: function destroy() { - var _this5 = this; - - this._stopTimeUpdateTimer(); - clearInterval(this.sendStatsId); - - if (this._player) { - this._player.destroy().then(function () { - return _this5._destroy(); - }).catch(function () { - _this5._destroy(); - _clappr.Log.error('shaka could not be destroyed'); - }); - } else { - this._destroy(); - } - - _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'destroy', this).call(this); - } - }, { - key: '_setup', - value: function _setup() { - var _this6 = this; - - this._isShakaReadyState = false; - this._ccIsSetup = false; - this._player = this._createPlayer(); - this._options.shakaConfiguration && this._player.configure(this._options.shakaConfiguration); - this._options.shakaOnBeforeLoad && this._options.shakaOnBeforeLoad(this._player); - - var playerLoaded = this._player.load(this._options.src); - playerLoaded.then(function () { - return _this6._loaded(); - }).catch(function (e) { - return _this6._setupError(e); - }); - } - }, { - key: '_createPlayer', - value: function _createPlayer() { - var player = new _shakaPlayer2.default.Player(this.el); - player.addEventListener('error', this._onError.bind(this)); - player.addEventListener('adaptation', this._onAdaptation.bind(this)); - player.addEventListener('buffering', this._onBuffering.bind(this)); - return player; - } - }, { - key: '_onTimeUpdate', - value: function _onTimeUpdate() { - if (!this.shakaPlayerInstance) return; - - var update = { - current: this.getCurrentTime(), - total: this.getDuration(), - firstFragDateTime: this.getProgramDateTime() - }; - var isSame = this._lastTimeUpdate && update.current === this._lastTimeUpdate.current && update.total === this._lastTimeUpdate.total; - if (isSame) return; - - this._lastTimeUpdate = update; - this.trigger(_clappr.Events.PLAYBACK_TIMEUPDATE, update, this.name); - } - }, { - key: '_onBuffering', - value: function _onBuffering(e) { - if (this._stopped) return; - var event = e.buffering ? _clappr.Events.PLAYBACK_BUFFERING : _clappr.Events.PLAYBACK_BUFFERFULL; - this.trigger(event); - } - }, { - key: '_loaded', - value: function _loaded() { - this._onShakaReady(); - this._startToSendStats(); - this._fillLevels(); - this._checkForClosedCaptions(); - } - }, { - key: '_fillLevels', - value: function _fillLevels() { - if (this._levels.length === 0) { - this._levels = this.videoTracks.map(function (videoTrack) { - return { id: videoTrack.id, label: videoTrack.height + 'p' }; - }).reverse(); - this.trigger(_clappr.Events.PLAYBACK_LEVELS_AVAILABLE, this.levels); - } - } - }, { - key: '_startToSendStats', - value: function _startToSendStats() { - var _this7 = this; - - var intervalMs = this._options.shakaSendStatsInterval || SEND_STATS_INTERVAL_MS; - this.sendStatsId = setInterval(function () { - return _this7._sendStats(); - }, intervalMs); - } - }, { - key: '_sendStats', - value: function _sendStats() { - this.trigger(_clappr.Events.PLAYBACK_STATS_ADD, this._player.getStats()); - } - }, { - key: '_setupError', - value: function _setupError(err) { - this._onError(err); - } - }, { - key: '_onError', - value: function _onError(err) { - var error = { - shakaError: err, - videoError: this.el.error - }; - - var _ref2 = error.shakaError.detail || error.shakaError, - category = _ref2.category, - code = _ref2.code, - severity = _ref2.severity; - - if (error.videoError || !code && !category) return _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), '_onError', this).call(this); - - var isCritical = severity === _shakaPlayer2.default.util.Error.Severity.CRITICAL; - var errorData = { - code: category + '_' + code, - description: 'Category: ' + category + ', code: ' + code + ', severity: ' + severity, - level: isCritical ? _clappr.PlayerError.Levels.FATAL : _clappr.PlayerError.Levels.WARN, - raw: err - }; - var formattedError = this.createError(errorData); - _clappr.Log.error('Shaka error event:', formattedError); - this.trigger(_clappr.Events.PLAYBACK_ERROR, formattedError); - } - }, { - key: '_onAdaptation', - value: function _onAdaptation() { - var activeVideo = this.videoTracks.filter(function (t) { - return t.active === true; - })[0]; - - this._fillLevels(); - - // update stats that may have changed before we trigger event - // so that user can rely on stats data when handling event - this._sendStats(); - - if (this._pendingAdaptationEvent) { - this.trigger(_clappr.Events.PLAYBACK_LEVEL_SWITCH_END); - this._pendingAdaptationEvent = false; - } - - _clappr.Log.debug('an adaptation has happened:', activeVideo); - this.highDefinition = activeVideo.height >= 720; - this.trigger(_clappr.Events.PLAYBACK_HIGHDEFINITIONUPDATE, this.highDefinition); - this.trigger(_clappr.Events.PLAYBACK_BITRATE, { - bandwidth: activeVideo.bandwidth, - width: activeVideo.width, - height: activeVideo.height, - level: activeVideo.id, - bitrate: activeVideo.videoBandwidth - }); - } - }, { - key: '_updateSettings', - value: function _updateSettings() { - if (this.getPlaybackType() === 'vod') this.settings.left = ['playpause', 'position', 'duration'];else if (this.dvrEnabled) this.settings.left = ['playpause'];else this.settings.left = ['playstop']; - - this.settings.seekEnabled = this.isSeekEnabled(); - this.trigger(_clappr.Events.PLAYBACK_SETTINGSUPDATE); - } - }, { - key: '_destroy', - value: function _destroy() { - this._isShakaReadyState = false; - _clappr.Log.debug('shaka was destroyed'); - } - }, { - key: 'isReady', - get: function get() { - return this._isShakaReadyState; - } - }, { - key: 'textTracks', - get: function get() { - return this.isReady && this._player.getTextTracks(); - } - }, { - key: 'audioTracks', - get: function get() { - return this.isReady && this._player.getVariantTracks().filter(function (t) { - return t.mimeType.startsWith('audio/'); - }); - } - }, { - key: 'videoTracks', - get: function get() { - return this.isReady && this._player.getVariantTracks().filter(function (t) { - return t.mimeType.startsWith('video/'); - }); - } - }, { - key: 'closedCaptionsTracks', - get: function get() { - var id = 0; - var trackId = function trackId() { - return id++; - }; - var tracks = this.textTracks || []; - - return tracks.filter(function (track) { - return track.kind === 'subtitle'; - }).map(function (track) { - return { id: trackId(), name: track.label || track.language, track: track }; - }); - } - - /** - * @override - */ - - }, { - key: 'closedCaptionsTrackId', - get: function get() { - return _get(DashShakaPlayback.prototype.__proto__ || Object.getPrototypeOf(DashShakaPlayback.prototype), 'closedCaptionsTrackId', this); - } - - /** - * @override - */ - , - set: function set(trackId) { - if (!this._player) { - return; - } - - var tracks = this.closedCaptionsTracks; - var showingTrack = void 0; - - // Note: -1 is for hide all tracks - if (trackId !== -1) { - showingTrack = tracks.find(function (track) { - return track.id === trackId; - }); - if (!showingTrack) { - _clappr.Log.warn('Track id "' + trackId + '" not found'); - return; - } - if (this._shakaTTVisible && showingTrack.track.active === true) { - _clappr.Log.info('Track id "' + trackId + '" already showing'); - return; - } - } - - if (showingTrack) { - this._player.selectTextTrack(showingTrack.track); - this._player.setTextTrackVisibility(true); - this._enableShakaTextTrack(true); - } else { - this._player.setTextTrackVisibility(false); - this._enableShakaTextTrack(false); - } - - this._ccTrackId = trackId; - this.trigger(_clappr.Events.PLAYBACK_SUBTITLE_CHANGED, { - id: trackId - }); - } - }]); - - return DashShakaPlayback; -}(_clappr.HTML5Video); - -exports.default = DashShakaPlayback; -module.exports = exports['default']; - -/***/ }), - -/***/ "clappr": -/*!******************************************************************************************!*\ - !*** external {"amd":"clappr","commonjs":"clappr","commonjs2":"clappr","root":"Clappr"} ***! - \******************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -module.exports = __WEBPACK_EXTERNAL_MODULE_clappr__; - -/***/ }) - -/******/ }); -}); \ No newline at end of file diff --git a/app/assets/javascripts/oembed-player.js b/app/assets/javascripts/oembed-player.js deleted file mode 100644 index 94640b352..000000000 --- a/app/assets/javascripts/oembed-player.js +++ /dev/null @@ -1,15 +0,0 @@ -//= require replacehash -//= require jquery -//= require mirrorbrain-fix -//= require subtitle-loader -//= require player-deeplinks -//= require mediaelement-and-player -//= require mejs-player -//= require player-gestures - -//= require source-chooser/source-chooser -//= require speed/speed -//= require skip-back/skip-back -//= require jump-forward/jump-forward - -//= require timelens diff --git a/app/assets/stylesheets/active_admin.css.scss b/app/assets/stylesheets/active_admin.scss similarity index 54% rename from app/assets/stylesheets/active_admin.css.scss rename to app/assets/stylesheets/active_admin.scss index 744597f8e..e30acf435 100644 --- a/app/assets/stylesheets/active_admin.css.scss +++ b/app/assets/stylesheets/active_admin.scss @@ -1,8 +1,9 @@ +@use "sass:color"; + // SASS variable overrides must be declared before loading up Active Admin's styles. // // To view the variables that Active Admin provides, take a look at -// `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the -// Active Admin source. +// `node_modules/@activeadmin/activeadmin/src/scss/_base.scss` and its imports. // // For example, to change the sidebar width: // $sidebar-width: 242px; @@ -12,20 +13,20 @@ $secondary-color: #f0f0f0 !default; $text-color: #323537 !default; $link-color: #38678b !default; $section-header-text-color: $primary-color !default; -$current-menu-item-background: lighten($primary-color, 12%) !default; -$hover-menu-item-background: lighten($primary-color, 12%) !default; -$table-stripe-color: lighten($primary-color, 57%) !default; +$current-menu-item-background: color.adjust($primary-color, $lightness: 12%) !default; +$hover-menu-item-background: color.adjust($primary-color, $lightness: 12%) !default; +$table-stripe-color: color.adjust($primary-color, $lightness: 57%) !default; $table-selected-color: #d9e4ec !default; $error-color: #932419 !default; // Active Admin's got SASS! -@import "active_admin/mixins"; -@import "active_admin/base"; +@import "@activeadmin/activeadmin/src/scss/mixins/all"; +@import "@activeadmin/activeadmin/src/scss/base"; // Overriding any non-variable SASS must be done after the fact. // For example, to change the default status-tag color: // // .status_tag { background: #6090DB; } -.scrollable { overflow:auto; width:800px; height:50px; } -.scrollable_error { overflow:auto; width:300px; height:50px; } +.scrollable { overflow:auto; width:800px; height:50px; } +.scrollable_error { overflow:auto; width:300px; height:50px; } diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css deleted file mode 100644 index b54008035..000000000 --- a/app/assets/stylesheets/application.css +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the top of the - * compiled file, but it's generally better to create a new file per style scope. - * - *= require_self - *= require frontend/styles - *= require icomoon-font - *= require jquery.bxslider - *= require mediaelementplayer - - *= require timelens - *= require timelens-custom - - *= require source-chooser/source-chooser - *= require speed/speed - *= require postroll/postroll - *= require playlist/playlist - * when adding plugins here, also add them to embed.css.scss - -*= require player-fixes -*= require player-gestures - -*/ diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 000000000..a5bce5df0 --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,20 @@ +// This is the cssbundling-rails entry point, replacing the old Sprockets +// application.css manifest. Mirrors its require order via @import. +// +// When adding plugins here, also add them to embed.scss. + +@import "frontend/styles"; +@import "icomoon-font"; +@import "jquery.bxslider"; +@import "mediaelementplayer"; + +@import "timelens"; +@import "timelens-custom"; + +@import "source-chooser/source-chooser"; +@import "speed/speed"; +@import "postroll/postroll"; +@import "playlist/playlist"; + +@import "player-fixes"; +@import "player-gestures"; diff --git a/app/assets/stylesheets/embed.css.scss b/app/assets/stylesheets/embed.css.scss deleted file mode 100644 index 343d1e336..000000000 --- a/app/assets/stylesheets/embed.css.scss +++ /dev/null @@ -1,31 +0,0 @@ -/* - *= require_self - *= require mediaelementplayer - - *= require source-chooser/source-chooser - *= require speed/speed - - *= require timelens - - *= require player-fixes - *= require player-gestures - */ - -body { - margin: 0; - padding: 0; -} - -/** - * Make the player fill the iframe properly to prevent weirdness with weird aspect - * ratios. - */ -.player.video { - width: 100%; - height: 100%; - - .mejs__container { - width: 100% !important; - height: 100% !important; - } -} diff --git a/app/assets/stylesheets/embed.scss b/app/assets/stylesheets/embed.scss new file mode 100644 index 000000000..9d17d66c4 --- /dev/null +++ b/app/assets/stylesheets/embed.scss @@ -0,0 +1,33 @@ +// cssbundling-rails entry point for the oembed/iframe embed page. +// Reduced subset mirroring embed.css.scss's old Sprockets manifest. +// +// When adding plugins here, also add them to application.scss. + +@import "mediaelementplayer"; + +@import "source-chooser/source-chooser"; +@import "speed/speed"; + +@import "timelens"; + +@import "player-fixes"; +@import "player-gestures"; + +body { + margin: 0; + padding: 0; +} + +/** + * Make the player fill the iframe properly to prevent weirdness with weird aspect + * ratios. + */ +.player.video { + width: 100%; + height: 100%; + + .mejs__container { + width: 100% !important; + height: 100% !important; + } +} diff --git a/app/assets/stylesheets/frontend/base/_feeds_dropdown.scss b/app/assets/stylesheets/frontend/base/_feeds_dropdown.scss index 758a9b37f..b60864a35 100644 --- a/app/assets/stylesheets/frontend/base/_feeds_dropdown.scss +++ b/app/assets/stylesheets/frontend/base/_feeds_dropdown.scss @@ -1,3 +1,5 @@ +@use "sass:color"; + #feedMenuMobile { display: none; // displayed via js } @@ -47,7 +49,7 @@ white-space: nowrap; // let the dropdown grow with it's width &:hover { - background-color: darken($body-bg, 20%); + background-color: color.adjust($body-bg, $lightness: -20%); } &.placeholder:hover { @@ -128,7 +130,7 @@ } a:hover { - background-color: darken($body-bg, 20%); + background-color: color.adjust($body-bg, $lightness: -20%); } } diff --git a/app/assets/stylesheets/frontend/base/_theme.scss b/app/assets/stylesheets/frontend/base/_theme.scss index 4e1398b58..73e4ecc8d 100644 --- a/app/assets/stylesheets/frontend/base/_theme.scss +++ b/app/assets/stylesheets/frontend/base/_theme.scss @@ -20,7 +20,7 @@ --freifunk-500: #df3a6c; --freifunk-400: #e85f88; - --font-sans-serif: "Estrangelo Edessa", "Helvetica Neue", Helvetica, Arial, sans-serif; + --font-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; --font-body: var(--font-sans-serif); --font-heading: var(--font-sans-serif); --font-digits: var(--font-sans-serif); diff --git a/app/assets/stylesheets/frontend/lib/_fonts.scss b/app/assets/stylesheets/frontend/lib/_fonts.scss index c001df3a6..b4be3ed2a 100644 --- a/app/assets/stylesheets/frontend/lib/_fonts.scss +++ b/app/assets/stylesheets/frontend/lib/_fonts.scss @@ -1,38 +1,17 @@ -/* font Estrangelo Edessa */ -@font-face { - font-family: 'Estrangelo Edessa'; - src: font-url('estre.eot'); - font-weight: normal; - font-style: normal; -} -@font-face { - font-family: 'Estrangelo Edessa'; - src: font-url('estre.otf') format('opentype'); - font-weight: normal; - font-style: normal; -} -@font-face { - font-family: 'Estrangelo Edessa'; - src: font-url('estre.ttf') format('truetype'), - font-url('estre.woff') format('woff'), - font-url('estre.svg') format('svg'); - font-weight: normal; - font-style: normal; -} /* fonts for 37C3 headings */ @font-face { font-family: 'Gabriella'; - src: font-url('GabriellaHeavy.otf') format('opentype'); + src: url('GabriellaHeavy.otf') format('opentype'); font-weight: heavy; font-style: normal; } @font-face { font-family: 'Mona Sans'; - src: font-url('Mona-Sans.woff2') format('woff2'); + src: url('Mona-Sans.woff2') format('woff2'); font-weight: normal; } @font-face { font-family: 'VCR OCD Faux'; - src: font-url('VCROCDFaux.woff2') format('woff2'), font-url('VCROCDFaux.woff') format('woff'), font-url('VCROCDFaux.ttf') format('truetype'); + src: url('VCROCDFaux.woff2') format('woff2'), url('VCROCDFaux.woff') format('woff'), url('VCROCDFaux.ttf') format('truetype'); font-weight: normal; } diff --git a/app/assets/stylesheets/frontend/lib/_variables.scss b/app/assets/stylesheets/frontend/lib/_variables.scss index 6c265139c..be4c16e21 100644 --- a/app/assets/stylesheets/frontend/lib/_variables.scss +++ b/app/assets/stylesheets/frontend/lib/_variables.scss @@ -1,4 +1,7 @@ -@import "bootstrap/variables"; +@use "sass:math"; +@use "sass:color"; + +@import "bootstrap-sass/assets/stylesheets/bootstrap/variables"; // Override Bootstrap variables here (defaults from bootstrap-sass v3.1.1.1): @@ -43,14 +46,14 @@ $text-color: #000; //* Global textual link color. $link-color: $brand-primary; -//* Link hover color set via `darken()` function. -$link-hover-color: darken($link-color, 15%); +//* Link hover color, darkened from $link-color. +$link-hover-color: color.adjust($link-color, $lightness: -15%); //== Typography // //## Font, line-height, and color for body text, headings, and more. -$font-family-sans-serif: "Estrangelo Edessa", "Helvetica Neue", Helvetica, Arial, sans-serif; +$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; // $font-family-serif: Georgia, "Times New Roman", Times, serif //* Default monospace fonts for ``, ``, and `
`.
 // $font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace
@@ -58,11 +61,11 @@ $font-family-base:        $font-family-sans-serif;
 //$font-family: "Droid Sans",Arial,"Trebuchet MS",sans-serif;
 
 $font-size-base:          14px;
-$font-size-large:         ceil(($font-size-base * 1.7)); // ~24px
+$font-size-large:         math.ceil(($font-size-base * 1.7)); // ~24px
 // $font-size-small:         ceil(($font-size-base * 0.85)) // ~12px
 
-$font-size-h1:            floor(($font-size-base * 3)); // ~42px
-$font-size-h2:            floor(($font-size-base * 2.15)); // ~30px
+$font-size-h1:            math.floor(($font-size-base * 3)); // ~42px
+$font-size-h2:            math.floor(($font-size-base * 2.15)); // ~30px
 // $font-size-h3:            ceil(($font-size-base * 1.7)) // ~24px
 // $font-size-h4:            ceil(($font-size-base * 1.25)) // ~18px
 // $font-size-h5:            $font-size-base
@@ -71,7 +74,7 @@ $font-size-h2:            floor(($font-size-base * 2.15)); // ~30px
 //* Unit-less `line-height` for use in components like buttons.
 $line-height-base:        1.2;
 //* Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
-$line-height-computed:    floor(($font-size-base * $line-height-base));
+$line-height-computed:    math.floor(($font-size-base * $line-height-base));
 
 //* By default, this inherits from the ``.
 // $headings-font-family:    inherit
@@ -347,7 +350,7 @@ $navbar-height:                    48px;
 $navbar-margin-bottom:             0;
 $navbar-border-radius:             $border-radius-base;
 // $navbar-padding-horizontal:        floor(($grid-gutter-width / 2))
-$navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2);
+$navbar-padding-vertical:          math.div($navbar-height - $line-height-computed, 2);
 // $navbar-collapse-max-height:       340px
 
 // $navbar-default-color:             #777
diff --git a/app/assets/stylesheets/frontend/pages/_show.scss b/app/assets/stylesheets/frontend/pages/_show.scss
index ce9e73360..4f02f1281 100644
--- a/app/assets/stylesheets/frontend/pages/_show.scss
+++ b/app/assets/stylesheets/frontend/pages/_show.scss
@@ -1,3 +1,5 @@
+@use "sass:color";
+
 body.page-show {
   h3 {
     border-bottom: 1px solid var(--border-divider);
@@ -191,7 +193,7 @@ body.page-show {
       color: white;
 
       &:hover {
-        background: lighten($gray-light, 10%);
+        background: color.adjust($gray-light, $lightness: 10%);
       }
     }
 
diff --git a/app/assets/stylesheets/frontend/shared/_videoplayer.scss b/app/assets/stylesheets/frontend/shared/_videoplayer.scss
index fc8a16d0b..87d483b31 100644
--- a/app/assets/stylesheets/frontend/shared/_videoplayer.scss
+++ b/app/assets/stylesheets/frontend/shared/_videoplayer.scss
@@ -1,12 +1,14 @@
+@use "sass:math";
+
 @mixin video-player-size($w) {
-  $h: $w / 16 * 9;
-  $h_43: $w / 4 * 3;
+  $h: math.div($w, 16) * 9;
+  $h_43: math.div($w, 4) * 3;
 
   @media all and (min-width: $w+40px) { // +40 -> add 20px space on the left and right side video player
     .video.player {
       width: $w !important;
       .mejs__container.video, >video, div[data-player], .video-native {
-        margin-left: -(($w - ($max-width - 30))/2)  !important;
+        margin-left: -(math.div($w - ($max-width - 30), 2))  !important;
 
         &.mejs__container-fullscreen {
           margin-left: 0 !important;
diff --git a/app/assets/stylesheets/frontend/styles.scss b/app/assets/stylesheets/frontend/styles.scss
index 1a545ad42..ee3445774 100644
--- a/app/assets/stylesheets/frontend/styles.scss
+++ b/app/assets/stylesheets/frontend/styles.scss
@@ -1,8 +1,12 @@
 // library setup
 @import "lib/fonts";
 @import "lib/variables";
-@import "bootstrap-sprockets";
-@import "bootstrap";
+// NB: do not import "bootstrap-sprockets" here. It sets
+// $bootstrap-sass-asset-helper: true and aliases twbs-font-path() to Sprockets'
+// font-path() helper, which dart-sass does not know and emits verbatim, e.g.
+// url(font-path("../fonts/...")) - invalid CSS. Propshaft rewrites plain
+// relative url()s instead.
+@import "bootstrap-sass/assets/stylesheets/bootstrap";
 
 // general page layout
 @import "base/typography";
diff --git a/app/assets/stylesheets/jquery.bxslider.css.scss b/app/assets/stylesheets/jquery.bxslider.scss
similarity index 90%
rename from app/assets/stylesheets/jquery.bxslider.css.scss
rename to app/assets/stylesheets/jquery.bxslider.scss
index b8cebd581..d30b4db47 100644
--- a/app/assets/stylesheets/jquery.bxslider.css.scss
+++ b/app/assets/stylesheets/jquery.bxslider.scss
@@ -51,7 +51,7 @@ ul.bxslider {
 /* LOADER */
 .bx-wrapper .bx-loading {
   min-height: 50px;
-  background: asset-url("bxslider/bx_loader.gif") center center no-repeat #ffffff;
+  background: url("bxslider/bx_loader.gif") center center no-repeat #ffffff;
   height: 100%;
   width: 100%;
   position: absolute;
@@ -93,7 +93,7 @@ ul.bxslider {
 /* DIRECTION CONTROLS (NEXT / PREV) */
 .bx-wrapper .bx-prev {
   left: 10px;
-  background: asset-url("bxslider/controls.png") no-repeat 0 -32px;
+  background: url("bxslider/controls.png") no-repeat 0 -32px;
 }
 .bx-wrapper .bx-prev:hover,
 .bx-wrapper .bx-prev:focus {
@@ -101,7 +101,7 @@ ul.bxslider {
 }
 .bx-wrapper .bx-next {
   right: 10px;
-  background: asset-url("bxslider/controls.png") no-repeat -43px -32px;
+  background: url("bxslider/controls.png") no-repeat -43px -32px;
 }
 .bx-wrapper .bx-next:hover,
 .bx-wrapper .bx-next:focus {
@@ -130,7 +130,7 @@ ul.bxslider {
   width: 10px;
   height: 11px;
   outline: 0;
-  background: asset-url("bxslider/controls.png") -86px -11px no-repeat;
+  background: url("bxslider/controls.png") -86px -11px no-repeat;
   margin: 0 3px;
 }
 .bx-wrapper .bx-controls-auto .bx-start:hover,
@@ -144,7 +144,7 @@ ul.bxslider {
   width: 9px;
   height: 11px;
   outline: 0;
-  background: asset-url("bxslider/controls.png") -86px -44px no-repeat;
+  background: url("bxslider/controls.png") -86px -44px no-repeat;
   margin: 0 3px;
 }
 .bx-wrapper .bx-controls-auto .bx-stop:hover,
diff --git a/app/assets/stylesheets/player-fixes.scss b/app/assets/stylesheets/player-fixes.scss
index 3313de9a7..780494f67 100644
--- a/app/assets/stylesheets/player-fixes.scss
+++ b/app/assets/stylesheets/player-fixes.scss
@@ -15,7 +15,7 @@ html {
 // voctocat artwork — overrides the inline SVG icons that 7.x uses for the
 // big play overlay and the loading spinner
 .mejs__overlay-button {
-  background: image-url('bigplay.svg') no-repeat !important;
+  background: url('bigplay.svg') no-repeat !important;
   height: 100px !important;
   width: 100px !important;
 }
@@ -34,7 +34,7 @@ html {
 }
 .mejs__overlay-loading-bg-img {
   animation: none !important;
-  background: transparent image-url('loading.gif') 50% 50% no-repeat !important;
+  background: transparent url('loading.gif') 50% 50% no-repeat !important;
   background-size: 100% !important;
   height: 80px !important;
   width: 80px !important;
diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql
index 7d0aeefc6..cdfe170f5 100644
--- a/app/graphql/schema.graphql
+++ b/app/graphql/schema.graphql
@@ -1,11 +1,11 @@
 """
-This entity groups multiple lectures together, e.g. a Conference or Lecture Series
+This entity groups multiple items together, e.g. a Conference or Lecture Series
 """
 type Conference {
   """
   The acronym of this conference, used as its identifier
   """
-  acronym: String!
+  acronym: Slug!
 
   """
   The aspect ratio of the conference's recordings
@@ -18,16 +18,21 @@ type Conference {
   description: String
 
   """
-  Identifies the date and time when a event was last released
+  when was an item last added in this grouping
   """
   eventLastReleasedAt: DateTime
   id: ID!
 
+  """
+  All images assigned to this conference, includes banner, logos in light & dark variants, etc.
+  """
+  images: [Image!]!
+
   """
   A URL pointing to the root of all image files of this conference
   """
-  imagesUrl: URL!
-  lectures(
+  imagesUrl: URL! @deprecated(reason: "Use the `images` field instead")
+  items(
     """
     Returns the elements in the list that come after the specified cursor.
     """
@@ -48,16 +53,47 @@ type Conference {
     """
     last: Int
   ): LectureConnection
+  lectures(
+    """
+    Returns the elements in the list that come after the specified cursor.
+    """
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """
+    Returns the first _n_ elements from the list.
+    """
+    first: Int
+
+    """
+    Returns the last _n_ elements from the list.
+    """
+    last: Int
+  ): LectureConnection @deprecated(reason: "Use the `items` field instead, which is a more generic name for the same query")
 
   """
   A URL pointing to the conference's own website
   """
   link: URL
 
+  """
+  The conference's logo
+  """
+  logo(prefer: ImageMimeTypeEnum): Image
+
   """
   A URL pointing to the conference's logo
   """
-  logoUrl: URL
+  logoUrl: URL @deprecated(reason: "Use the `logo` field instead")
+
+  """
+  The raw metadata for this conference, as stored in the database
+  """
+  metadataRaw: JSON @deprecated(reason: "experimental")
 
   """
   A URL pointing to the root of all recording files of this conference
@@ -72,7 +108,12 @@ type Conference {
   """
   The URL slug of this conference
   """
-  slug: String!
+  slug: Slug!
+
+  """
+  The raw streaming information for this conference, as provided by streaming v2 API
+  """
+  streamingRaw: JSON @deprecated(reason: "experimental")
 
   """
   The title of this conference
@@ -94,11 +135,6 @@ type Conference {
 The connection type for Conference.
 """
 type ConferenceConnection {
-  """
-  A list of edges.
-  """
-  edges: [ConferenceEdge]
-
   """
   A list of nodes.
   """
@@ -110,21 +146,6 @@ type ConferenceConnection {
   pageInfo: PageInfo!
 }
 
-"""
-An edge in a connection.
-"""
-type ConferenceEdge {
-  """
-  A cursor for use in pagination.
-  """
-  cursor: String!
-
-  """
-  The item at the end of the edge.
-  """
-  node: Conference
-}
-
 input ConferenceFilter {
   OR: [ConferenceFilter!]
   currentlyStreaming: Boolean
@@ -141,6 +162,116 @@ An ISO 8601-encoded datetime
 """
 scalar DateTime
 
+"""
+An image e.g. logo or banner, with basic metadata
+"""
+type Image {
+  """
+  The MIME type of the image, e.g. `image/png` or `image/svg+xml`
+  """
+  mimeType: String!
+
+  """
+  The purpose or type of the image
+  """
+  type: ImageTypeEnum
+
+  """
+  The URL of the image
+  """
+  url: URL!
+
+  """
+  The variant of the image, e.g. `light` or `dark` for logos
+  """
+  variant: ImageVariantEnum
+}
+
+"""
+MIME type for images
+"""
+enum ImageMimeTypeEnum {
+  """
+  JPEG format
+  """
+  JPEG
+
+  """
+  PNG format
+  """
+  PNG
+
+  """
+  SVG format
+  """
+  SVG
+
+  """
+  WebP format
+  """
+  WEBP
+}
+
+"""
+Type or purpose of the image
+"""
+enum ImageTypeEnum {
+  """
+  Banner image
+  """
+  BANNER
+
+  """
+  Logo image
+  """
+  LOGO
+
+  """
+  Other image type
+  """
+  OTHER
+
+  """
+  Poster image
+  """
+  POSTER
+
+  """
+  Thumbnail image
+  """
+  THUMBNAIL
+}
+
+"""
+Variant of the image
+"""
+enum ImageVariantEnum {
+  """
+  Dark mode image
+  """
+  DARK
+
+  """
+  Light mode image
+  """
+  LIGHT
+}
+
+"""
+Lookup key for finding a single item. Provide exactly one of the fields below.
+"""
+input ItemLookupInput {
+  """
+  The item's guid
+  """
+  guid: ID
+
+  """
+  The item's URL slug
+  """
+  slug: ID
+}
+
 """
 A untyped JSON document
 """
@@ -151,7 +282,10 @@ This entity is an Event with multiple Files e.g. Video and Audio recordings,
 subtitles (SRT) as well as PDFs e.g. the lecture slides
 """
 type Lecture {
-  audios: [Resource!]
+  """
+  all audio recordings assigned to this item
+  """
+  audios: [Resource!]!
 
   """
   The conference this event belongs to
@@ -199,7 +333,7 @@ type Lecture {
     last: Int
   ): ResourceConnection!
   guid: ID!
-  images: LectureImage
+  images: LectureImage!
 
   """
   URL pointing to the conference event website
@@ -228,30 +362,39 @@ type Lecture {
   """
   releaseDate: DateTime
   relive: JSON
-  slides: [Resource!]
+
+  """
+  PDF slides, if provided
+  """
+  slides: [Resource!]!
 
   """
   The URL slug of this event
   """
-  slug: URL!
+  slug: Slug!
 
   """
   The event's subtitle that may be displayed below the title
   """
   subtitle: String
-  subtitles: [Resource!]
+  subtitles: [Resource!]!
 
   """
   Tags/keywords describing the event
   """
   tags: [String!]
-  timelens: LectureTimelens
+  timelens: LectureTimelens!
 
   """
   The title of this event
   """
   title: String!
 
+  """
+  The languages this event has been translated into, encoded as ISO 639-2
+  """
+  translations: [String!]
+
   """
   Identifies the date and time when the object was last updated
   """
@@ -261,7 +404,20 @@ type Lecture {
   A URL pointing to this lecture's page in vocotweb frontend
   """
   url: URL!
+
+  """
+  main MP4 video, possibly with multiple audio and video tracks
+  """
+  video(prefer: [VideoPreferenceEnum!], quality: VideoQualityEnum): Resource!
+
+  """
+  simlified access to single language video in original language
+  """
   videoPreferred: Resource!
+
+  """
+  all video recordings assigned to this item
+  """
   videos: [Resource!]!
 
   """
@@ -274,11 +430,6 @@ type Lecture {
 The connection type for Lecture.
 """
 type LectureConnection {
-  """
-  A list of edges.
-  """
-  edges: [LectureEdge]
-
   """
   A list of nodes.
   """
@@ -290,21 +441,6 @@ type LectureConnection {
   pageInfo: PageInfo!
 }
 
-"""
-An edge in a connection.
-"""
-type LectureEdge {
-  """
-  A cursor for use in pagination.
-  """
-  cursor: String!
-
-  """
-  The item at the end of the edge.
-  """
-  node: Lecture
-}
-
 input LectureFilter {
   OR: [LectureFilter!]
   tagsContains: [String!]
@@ -370,7 +506,7 @@ type Query {
   _service: _Service!
 
   """
-  All conferences as Connection
+  All conferences with pagination support
   """
   allConferences(
     """
@@ -395,30 +531,51 @@ type Query {
   ): ConferenceConnection
 
   """
-  Find a conference by acronym
+  Find a conference by acronym (e.g., `39c3`, `eh2010`)
   """
-  conference(id: ID!): Conference
+  conference(
+    """
+    The conference acronym
+    """
+    id: ID!
+  ): Conference
   conferences(filter: ConferenceFilter, first: Int, offset: Int, orderBy: ConferenceOrderBy = createdAt_DESC): [Conference!]!
 
   """
-  All conferences as List, recent first
+  Recently active conferences with recorded events, ordered by most recent first
+  """
+  conferencesRecent(
+    """
+    Number of conferences to return (max 30)
+    """
+    first: Int! = 5
+
+    """
+    Offset for pagination
+    """
+    offset: Int = 0
+  ): [Conference!]
+
+  """
+  Find a single item by guid or slug
   """
-  conferencesRecent(first: Int! = 5, offset: Int = 0): [Conference!]
+  item(by: ItemLookupInput!): Lecture
+  items(filter: LectureFilter, first: Int, offset: Int, orderBy: LectureOrderBy = date_DESC): [Lecture!]!
 
   """
   Find a lecture by guid
   """
-  lecture(guid: ID!): Lecture
+  lecture(guid: ID!): Lecture @deprecated(reason: "Use `item(by: {guid: \"\"})` instead, which is a more generic name for the same query")
 
   """
   Find a lecture by slug
   """
-  lectureBySlug(slug: ID!): Lecture
+  lectureBySlug(slug: ID!): Lecture @deprecated(reason: "Use `item(by: {slug: \"slug>\"})` instead, which is a more generic name for the same query")
   lectureSearch(page: Int, query: String!): [Lecture!]!
-  lectures(filter: LectureFilter, first: Int, offset: Int, orderBy: LectureOrderBy = date_DESC): [Lecture!]!
+  lectures(filter: LectureFilter, first: Int, offset: Int, orderBy: LectureOrderBy = date_DESC): [Lecture!]! @deprecated(reason: "Use items instead, which is a more generic name for the same query")
 
   """
-  A list of related lectures, ordered by decreasing relevance.
+  A list of related items
   """
   lecturesRelatedTo(
     """
@@ -442,6 +599,36 @@ type Query {
     """
     last: Int
   ): LectureConnection
+
+  """
+  Promoted lectures featured on the start page
+  """
+  promoted(
+    """
+    Returns the elements in the list that come after the specified cursor.
+    """
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """
+    Returns the first _n_ elements from the list.
+    """
+    first: Int
+
+    """
+    Returns the last _n_ elements from the list.
+    """
+    last: Int
+
+    """
+    Offset for pagination
+    """
+    offset: Int = 0
+  ): LectureConnection
 }
 
 """
@@ -471,7 +658,7 @@ type Resource {
   language: String
 
   """
-  The recordings's mime type, e.g. video/mp4
+  The recordings's mime type, e.g. `video/mp4`
   """
   mimeType: String!
 
@@ -493,7 +680,7 @@ type Resource {
   """
   A URL pointing to the CDN location of this recording
   """
-  url: String!
+  url: URL!
 
   """
   The width of this recording in px, if it is a video
@@ -505,11 +692,6 @@ type Resource {
 The connection type for Resource.
 """
 type ResourceConnection {
-  """
-  A list of edges.
-  """
-  edges: [ResourceEdge]
-
   """
   A list of nodes.
   """
@@ -521,21 +703,6 @@ type ResourceConnection {
   pageInfo: PageInfo!
 }
 
-"""
-An edge in a connection.
-"""
-type ResourceEdge {
-  """
-  A cursor for use in pagination.
-  """
-  cursor: String!
-
-  """
-  The item at the end of the edge.
-  """
-  node: Resource
-}
-
 """
 The unit a recording's size is expressed in
 """
@@ -556,11 +723,61 @@ enum SizeUnit {
   MB
 }
 
+"""
+A URL slug (no forward slashes)
+"""
+scalar Slug
+
 """
 A valid URL, transported as a string
 """
 scalar URL
 
+"""
+Preferred video format
+"""
+enum VideoPreferenceEnum {
+  """
+  AV1 video codec (WebM container)
+  """
+  AV1
+
+  """
+  DASH streaming format
+  """
+  DASH
+
+  """
+  MPEG-4 video format
+  """
+  MP4
+
+  """
+  WebM video format
+  """
+  WEBM
+}
+
+"""
+Preferred video quality tier
+"""
+enum VideoQualityEnum {
+  """
+  High quality
+  """
+  HQ
+
+  """
+  Low quality
+  """
+  LQ
+
+  """
+  Original master quality, largest file size
+  """
+  ORIGINAL
+}
+
 """
 The sdl representing the federated service capabilities. Includes federation
 directives, removes federation types, and includes rest of full schema after
diff --git a/app/graphql/types/base_connection.rb b/app/graphql/types/base_connection.rb
new file mode 100644
index 000000000..31b11e8b8
--- /dev/null
+++ b/app/graphql/types/base_connection.rb
@@ -0,0 +1,10 @@
+module Types
+  class BaseConnection < GraphQL::Types::Relay::BaseConnection
+    # Skip the `edges` field entirely - this API only exposes `nodes` and `pageInfo`.
+    def self.edge_type(edge_type_class, node_type: edge_type_class.node_type, node_nullable: self.node_nullable, **_field_options)
+      @node_type = node_type
+      define_nodes_field(node_nullable)
+      description("The connection type for #{node_type.graphql_name}.")
+    end
+  end
+end
diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb
index c60986100..ec0566260 100644
--- a/app/graphql/types/base_object.rb
+++ b/app/graphql/types/base_object.rb
@@ -3,5 +3,6 @@ class BaseObject < GraphQL::Schema::Object
     include ApolloFederation::Object
 
     field_class BaseField
+    connection_type_class(BaseConnection)
   end
 end
diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb
index 673b7c815..9ca578823 100644
--- a/app/graphql/types/conference_type.rb
+++ b/app/graphql/types/conference_type.rb
@@ -1,23 +1,31 @@
 module Types
   class ConferenceType < Types::BaseObject
-    description "This entity groups multiple lectures together, e.g. a Conference or Lecture Series"
+    description "This entity groups multiple items together, e.g. a Conference or Lecture Series"
     field :id, ID, null: false
-    field :acronym, String, "The acronym of this conference, used as its identifier", null: false
+    field :acronym, SlugType, "The acronym of this conference, used as its identifier", null: false
     field :title, String, "The title of this conference", null: false
-    field :slug, String, "The URL slug of this conference", null: false
-    field :lectures, LectureType.connection_type, null: true
+    field :slug, SlugType, "The URL slug of this conference", null: false
+    field :lectures, LectureType.connection_type, null: true, deprecation_reason: "Use the `items` field instead, which is a more generic name for the same query"
+    field :items, LectureType.connection_type, null: true
 
     field :url, UrlType, "A URL pointing to the conference page in vocotweb frontend", null: false
     field :link, UrlType, "A URL pointing to the conference's own website", null: true
     field :description, String, "The conference's description", null: true
-    #field :logo, Types::ImageType, null: true
-    field :logo_url, UrlType, "A URL pointing to the conference's logo", null: true
-    field :images_url, UrlType, "A URL pointing to the root of all image files of this conference", null: false
+    field :logo_url, UrlType, "A URL pointing to the conference's logo", null: true, deprecation_reason: "Use the `logo` field instead"
+    field :logo, Types::ImageType, "The conference's logo", null: true do
+      argument :prefer, Types::ImageMimeTypeEnum, required: false
+    end
+    field :images, [Types::ImageType], "All images assigned to this conference, includes banner, logos in light & dark variants, etc.", null: false
+
+    field :images_url, UrlType, "A URL pointing to the root of all image files of this conference", null: false, deprecation_reason: "Use the `images` field instead"
     field :aspect_ratio, String, "The aspect ratio of the conference's recordings", null: false # TODO: Enum
     field :recordings_url, UrlType, "A URL pointing to the root of all recording files of this conference", null: false
     field :schedule_url, UrlType, "A URL pointing to the conference's frab xml schedule", null: true
     field :updated_at, DateTimeType, "Identifies the date and time when the object was last updated", null: false
-    field :event_last_released_at, DateTimeType, "Identifies the date and time when a event was last released", null: true
+    field :event_last_released_at, DateTimeType, "when was an item last added in this grouping", null: true
+
+    field :metadata_raw, JsonType, "The raw metadata for this conference, as stored in the database", null: true, deprecation_reason: "experimental"
+    field :streaming_raw, JsonType, "The raw streaming information for this conference, as provided by streaming v2 API", null: true, deprecation_reason: "experimental"
 
     def id
       object.acronym
@@ -31,6 +39,20 @@ def url
       Rails.application.routes.url_helpers.conference_url(acronym: object.acronym)
     end
 
+    def logo(prefer: nil)
+      logo = object.logo_img
+      return logo if prefer.blank? || logo.blank? || logo[:mime_type] == prefer
+
+      object.images.each do |image|
+        if image['type'] == 'logo' && image['mime_type'] == prefer
+          return image
+        end
+      end
+
+      # fallback to the default logo if no preferred variant is found
+      logo
+    end
+
     def images_url
       object.get_images_url
     end
@@ -38,5 +60,13 @@ def images_url
     def recordings_url
       object.get_recordings_url
     end
+
+    def metadata_raw
+      object.metadata
+    end
+
+    def streaming_raw
+      object.streaming
+    end
   end
 end
diff --git a/app/graphql/types/image_mime_type_enum.rb b/app/graphql/types/image_mime_type_enum.rb
new file mode 100644
index 000000000..9e3d1d731
--- /dev/null
+++ b/app/graphql/types/image_mime_type_enum.rb
@@ -0,0 +1,9 @@
+module Types
+  class ImageMimeTypeEnum < Types::BaseEnum
+    description "MIME type for images"
+    value "SVG", "SVG format", value: "image/svg+xml"
+    value "PNG", "PNG format", value: "image/png"
+    value "JPEG", "JPEG format", value: "image/jpeg"
+    value "WEBP", "WebP format", value: "image/webp"
+  end
+end
diff --git a/app/graphql/types/image_type.rb b/app/graphql/types/image_type.rb
new file mode 100644
index 000000000..3bc53e1da
--- /dev/null
+++ b/app/graphql/types/image_type.rb
@@ -0,0 +1,10 @@
+module Types
+  class ImageType < Types::BaseObject
+    description "An image e.g. logo or banner, with basic metadata"
+
+    field :url, UrlType, "The URL of the image", null: false
+    field :mime_type, String, "The MIME type of the image, e.g. `image/png` or `image/svg+xml`", null: false
+    field :type, Types::ImageTypeEnum, "The purpose or type of the image", null: true
+    field :variant, Types::ImageVariantEnum, "The variant of the image, e.g. `light` or `dark` for logos", null: true
+  end
+end
diff --git a/app/graphql/types/image_type_enum.rb b/app/graphql/types/image_type_enum.rb
new file mode 100644
index 000000000..4ab5e04b9
--- /dev/null
+++ b/app/graphql/types/image_type_enum.rb
@@ -0,0 +1,16 @@
+module Types
+  class ImageTypeEnum < Types::BaseEnum
+    description "Type or purpose of the image"
+    value "LOGO", "Logo image", value: "logo"
+    value "BANNER", "Banner image", value: "banner"
+    value "OTHER", "Other image type", value: "other"
+    value "THUMBNAIL", "Thumbnail image", value: "thumbnail"
+    value "POSTER", "Poster image", value: "poster"
+  end
+
+  class ImageVariantEnum < Types::BaseEnum
+    description "Variant of the image"
+    value "LIGHT", "Light mode image", value: "light"
+    value "DARK", "Dark mode image", value: "dark"
+  end
+end
diff --git a/app/graphql/types/item_lookup_input.rb b/app/graphql/types/item_lookup_input.rb
new file mode 100644
index 000000000..fa278e21f
--- /dev/null
+++ b/app/graphql/types/item_lookup_input.rb
@@ -0,0 +1,7 @@
+module Types
+  class ItemLookupInput < Types::BaseInputObject
+    description 'Lookup key for finding a single item. Provide exactly one of the fields below.'
+    argument :guid, ID, required: false, description: "The item's guid"
+    argument :slug, ID, required: false, description: "The item's URL slug"
+  end
+end
diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb
index fffffdc5b..8c8f4b454 100644
--- a/app/graphql/types/lecture_type.rb
+++ b/app/graphql/types/lecture_type.rb
@@ -13,10 +13,11 @@ class LectureType < Types::BaseObject
     field :subtitle, String, "The event's subtitle that may be displayed below the title", null: true
     field :description, String, "The event's description", null: true
 
-    field :slug, UrlType, "The URL slug of this event", null: false
+    field :slug, SlugType, "The URL slug of this event", null: false
     field :url, UrlType, "A URL pointing to this lecture's page in vocotweb frontend", null: false
 
     field :original_language, String, "The event's original language, encoded as ISO 639-2", null: true
+    field :translations, [String], "The languages this event has been translated into, encoded as ISO 639-2", null: true
     field :duration, Integer, "The lecture recording duration in seconds", null: true
 
     field :persons, [String], "Names of persons that held the event", null: true
@@ -32,11 +33,15 @@ class LectureType < Types::BaseObject
     field :link, UrlType, "URL pointing to the conference event website", null: true
     field :doi_url, UrlType, "Digital Object Identifier (DOI) e.g. https://doi.org/10.5446/19566", null: true
 
-    field :video_preferred, ResourceType, null: false
-    field :videos, [ResourceType], null: false
-    field :audios, [ResourceType], null: true
-    field :subtitles, [ResourceType], null: true
-    field :slides, [ResourceType], null: true
+    field :video_preferred, ResourceType, "simlified access to single language video in original language", null: false
+    field :video, ResourceType, "main MP4 video, possibly with multiple audio and video tracks", null: false do
+      argument :prefer, [Types::VideoPreferenceEnum], required: false
+      argument :quality, Types::VideoQualityEnum, required: false
+    end
+    field :videos, [ResourceType], "all video recordings assigned to this item", null: false
+    field :audios, [ResourceType], "all audio recordings assigned to this item", null: false
+    field :subtitles, [ResourceType], null: false
+    field :slides, [ResourceType], "PDF slides, if provided", null: false
     field :files, ResourceType.connection_type, null: false
 
     # field :thumbnail, Types::ImageType, null: true
@@ -44,7 +49,7 @@ class LectureImageType < Types::BaseObject
       field :poster_url, UrlType, 'URL pointing to a preview/poster image of the event', null: true
       field :thumb_url, UrlType, 'URL pointing to a smaller version of the poster image', null: true
     end
-    field :images, LectureImageType, null: true
+    field :images, LectureImageType, null: false
     def images
       object
     end
@@ -53,7 +58,7 @@ class LectureTimelensType < Types::BaseObject
       field :timeline_url, UrlType, 'URL pointing timelens timline image of the event', null: true
       field :thumbnails_url, UrlType, 'URL pointing to scrubbing thumbnails for timelens/timeline', null: true
     end
-    field :timelens, LectureTimelensType, null: true
+    field :timelens, LectureTimelensType, null: false
     def timelens
       object
     end
@@ -69,8 +74,8 @@ def relive
     end
 
     '''
-    # A list of related events, ordered by decreasing relevance.
-    relatedLectures(
+    # A list of related items, ordered by decreasing relevance.
+    related(
       # Skip the first _n_ related events.
       offset: Integer
 
@@ -96,6 +101,15 @@ def video_preferred
       object.preferred_recording
     end
 
+    def video(prefer: nil, quality: nil)
+      if prefer.present?
+        result = object.recordings.video.find_by(mime_type: prefer)
+        return result if result.present?
+      end
+      #object.recording_for_master_feed
+      object.video_master
+    end
+
     def audios
       object.recordings.audio
     end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index e2fdb32eb..7a727da5b 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -3,9 +3,17 @@ class QueryType < Types::BaseObject
     # add_field(GraphQL::Types::Relay::NodeField)
     # add_field(GraphQL::Types::Relay::NodesField)
 
+    field :promoted, LectureType.connection_type, null: true, max_page_size: 30 do
+      description 'Promoted lectures featured on the start page'
+      argument :offset, Integer, default_value: 0, required: false, description: 'Offset for pagination'
+    end
+    def promoted(offset:, first: nil)
+      Event.promoted(first || 10).offset(offset)
+    end
+
     field :conference, ConferenceType, null: true do
-      description 'Find a conference by acronym'
-      argument :id, ID, required: true
+      description 'Find a conference by acronym (e.g., `39c3`, `eh2010`)'
+      argument :id, ID, required: true, description: 'The conference acronym'
     end
     def conference(id:)
       Conference.find_by(acronym: id)
@@ -14,19 +22,33 @@ def conference(id:)
     field :conferences, resolver: Resolvers::Conference
 
     field :conferences_recent, [ConferenceType], null: true do
-      description 'All conferences as List, recent first'
-      argument :first, Integer, default_value: 5, prepare: ->(limit, ctx) { [limit, 30].min }, required: true
-      argument :offset, Integer, default_value: 0, required: false
+      description 'Recently active conferences with recorded events, ordered by most recent first'
+      argument :first, Integer, default_value: 5, prepare: ->(limit, ctx) { [limit, 30].min }, required: true, description: 'Number of conferences to return (max 30)'
+      argument :offset, Integer, default_value: 0, required: false, description: 'Offset for pagination'
     end
     def conferences_recent(offset:, first:)
       Conference.with_recent_events(first).offset(offset)
     end
 
     field :all_conferences, ConferenceType.connection_type, null: true do
-      description 'All conferences as Connection'
+      description 'All conferences with pagination support'
+    end
+
+    field :item, LectureType, null: true do
+      description 'Find a single item by guid or slug'
+      argument :by, Types::ItemLookupInput, required: true
+    end
+    def item(by:)
+      if by.guid.present?
+        Event.find_by(guid: by.guid)
+      elsif by.slug.present?
+        Event.find_by(slug: by.slug)
+      else
+        raise GraphQL::ExecutionError, 'ItemLookupInput requires either guid or slug'
+      end
     end
 
-    field :lecture, LectureType, null: true do
+    field :lecture, LectureType, null: true, deprecation_reason: 'Use `item(by: {guid: ""})` instead, which is a more generic name for the same query' do
       description 'Find a lecture by guid'
       argument :guid, ID, required: true
     end
@@ -34,7 +56,7 @@ def lecture(guid:)
       Event.find_by(guid: guid)
     end
 
-    field :lecture_by_slug, LectureType, null: true do
+    field :lecture_by_slug, LectureType, null: true, deprecation_reason: 'Use `item(by: {slug: "slug>"})` instead, which is a more generic name for the same query' do
       description 'Find a lecture by slug'
       argument :slug, ID, required: true
     end
@@ -44,10 +66,11 @@ def lecture_by_slug(slug:)
 
     field :lecture_search, resolver: Resolvers::SearchLectures
 
-    field :lectures, resolver: Resolvers::Lectures
+    field :lectures, resolver: Resolvers::Lectures, deprecation_reason: "Use items instead, which is a more generic name for the same query"
+    field :items, resolver: Resolvers::Lectures
 
     field :lectures_related_to, LectureType.connection_type, null: true do
-      description 'A list of related lectures, ordered by decreasing relevance.'
+      description 'A list of related items'
       argument :guid, ID, required: true
     end
     def lectures_related_to(guid:)
diff --git a/app/graphql/types/resource_type.rb b/app/graphql/types/resource_type.rb
index d4a32bb34..b5de95493 100644
--- a/app/graphql/types/resource_type.rb
+++ b/app/graphql/types/resource_type.rb
@@ -4,10 +4,10 @@ class ResourceType < Types::BaseObject
 
     field :label, String, null: false
     field :filename, String, null: false
-    field :url, String, "A URL pointing to the CDN location of this recording", null: false
+    field :url, UrlType, "A URL pointing to the CDN location of this recording", null: false
     field :language, String, "The recordings's language, encoded as ISO 639-2", null: true
     field :duration, Integer, "The recordings's duration in seconds", null: false
-    field :mime_type, String, "The recordings's mime type, e.g. video/mp4", null: false
+    field :mime_type, String, "The recordings's mime type, e.g. `video/mp4`", null: false
     field :width, Integer, "The width of this recording in px, if it is a video", null: false
     field :height, Integer, "The height of this recording in px, if it is a video", null: false
     field :high_quality, Boolean, "Whether this recording is a video with at least 720p resolution", null: false
diff --git a/app/graphql/types/slug_type.rb b/app/graphql/types/slug_type.rb
new file mode 100644
index 000000000..3bcdf6d2b
--- /dev/null
+++ b/app/graphql/types/slug_type.rb
@@ -0,0 +1,18 @@
+module Types
+  class SlugType < GraphQL::Types::String
+    description "A URL slug (no forward slashes)"
+    graphql_name 'Slug'
+
+    def self.coerce_input(input_value, _context)
+      unless input_value.match?(/\A[^\/]+\z/)
+        raise GraphQL::CoercionError, "#{input_value.inspect} is not a valid slug"
+      end
+
+      input_value
+    end
+
+    def self.coerce_result(ruby_value, _context)
+      ruby_value.to_s
+    end
+  end
+end
diff --git a/app/graphql/types/video_preference_enum.rb b/app/graphql/types/video_preference_enum.rb
new file mode 100644
index 000000000..925594d16
--- /dev/null
+++ b/app/graphql/types/video_preference_enum.rb
@@ -0,0 +1,9 @@
+module Types
+  class VideoPreferenceEnum < Types::BaseEnum
+    description "Preferred video format"
+    value "MP4", "MPEG-4 video format", value: "video/mp4"
+    value "AV1", "AV1 video codec (WebM container)", value: "video/webm;codecs=av01"
+    value "DASH", "DASH streaming format", value: "application/dash+xml"
+    value "WEBM", "WebM video format", value: "video/webm"
+  end
+end
diff --git a/app/graphql/types/video_quality_enum.rb b/app/graphql/types/video_quality_enum.rb
new file mode 100644
index 000000000..4c80a2979
--- /dev/null
+++ b/app/graphql/types/video_quality_enum.rb
@@ -0,0 +1,8 @@
+module Types
+  class VideoQualityEnum < Types::BaseEnum
+    description "Preferred video quality tier"
+    value "HQ", "High quality", value: FeedQuality::HQ
+    value "LQ", "Low quality", value: FeedQuality::LQ
+    value "ORIGINAL", "Original master quality, largest file size", value: FeedQuality::MASTER
+  end
+end
diff --git a/app/assets/javascripts/activate-timelens.js b/app/javascript/activate-timelens.js
similarity index 100%
rename from app/assets/javascripts/activate-timelens.js
rename to app/javascript/activate-timelens.js
diff --git a/app/javascript/active_admin.js b/app/javascript/active_admin.js
new file mode 100644
index 000000000..c16caf688
--- /dev/null
+++ b/app/javascript/active_admin.js
@@ -0,0 +1 @@
+import "@activeadmin/activeadmin";
diff --git a/app/javascript/application.js b/app/javascript/application.js
new file mode 100644
index 000000000..c3a1a801e
--- /dev/null
+++ b/app/javascript/application.js
@@ -0,0 +1,46 @@
+// esbuild entry point for the main frontend bundle. Mirrors the previous Sprockets
+// application.js manifest, in the same require order, file for file.
+//
+// When adding plugins here, also add them to oembed.js.
+
+import './vendor/jquery-global';
+
+import './replacehash';
+import 'jquery-ujs';
+import 'bootstrap-sass/assets/javascripts/bootstrap';
+// turbolinks' UMD auto-start check does `window.Turbolinks === `, but
+// under esbuild's CJS interop the module factory runs with `this` bound to
+// module.exports rather than window, so the auto-start's own `this.Turbolinks =`
+// assignment never reaches window and the check silently fails. Start explicitly.
+import Turbolinks from 'turbolinks';
+Turbolinks.start();
+
+import './vendor/purl.min';
+import './vendor/handlebars.min-latest';
+import './vendor/jquery.bxslider';
+import './slider';
+import './theme';
+import './feed_toggle';
+import './mastodon-share';
+
+import './mirrorbrain-fix';
+import './subtitle-loader';
+import './player-deeplinks';
+import './vendor/mediaelement-and-player';
+import './mejs-player';
+import './player-gestures';
+import './mediaelement-plugins/source-chooser/source-chooser';
+import './mediaelement-plugins/speed/speed';
+import './mediaelement-plugins/postroll/postroll';
+import './mediaelement-plugins/skip-back/skip-back';
+import './mediaelement-plugins/jump-forward/jump-forward';
+import './mediaelement-plugins/playlist/playlist';
+
+import './vendor/player.umd';
+import './vendor/clappr-thumbnails-plugin.cjs';
+import './vendor/clappr-playback-rate-plugin.cjs';
+
+import './relive-seek';
+
+import './vendor/timelens';
+import './activate-timelens';
diff --git a/app/assets/javascripts/feed_toggle.js b/app/javascript/feed_toggle.js
similarity index 100%
rename from app/assets/javascripts/feed_toggle.js
rename to app/javascript/feed_toggle.js
diff --git a/app/assets/javascripts/mastodon-share.js b/app/javascript/mastodon-share.js
similarity index 93%
rename from app/assets/javascripts/mastodon-share.js
rename to app/javascript/mastodon-share.js
index fb3d12e7d..4bb6940cf 100644
--- a/app/assets/javascripts/mastodon-share.js
+++ b/app/javascript/mastodon-share.js
@@ -11,4 +11,5 @@ function mastodonShare(text, url) {
 
     const windowHandle = window.open(shareURL, "_blank");
     if (windowHandle) windowHandle.focus();
-}
\ No newline at end of file
+}
+window.mastodonShare = mastodonShare;
\ No newline at end of file
diff --git a/vendor/assets/mediaelement-plugins/jump-forward/jump-forward.js b/app/javascript/mediaelement-plugins/jump-forward/jump-forward.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/jump-forward/jump-forward.js
rename to app/javascript/mediaelement-plugins/jump-forward/jump-forward.js
diff --git a/vendor/assets/mediaelement-plugins/playlist/playlist.js b/app/javascript/mediaelement-plugins/playlist/playlist.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/playlist/playlist.js
rename to app/javascript/mediaelement-plugins/playlist/playlist.js
diff --git a/vendor/assets/mediaelement-plugins/postroll/postroll.js b/app/javascript/mediaelement-plugins/postroll/postroll.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/postroll/postroll.js
rename to app/javascript/mediaelement-plugins/postroll/postroll.js
diff --git a/vendor/assets/mediaelement-plugins/skip-back/skip-back.js b/app/javascript/mediaelement-plugins/skip-back/skip-back.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/skip-back/skip-back.js
rename to app/javascript/mediaelement-plugins/skip-back/skip-back.js
diff --git a/vendor/assets/mediaelement-plugins/source-chooser/source-chooser.js b/app/javascript/mediaelement-plugins/source-chooser/source-chooser.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/source-chooser/source-chooser.js
rename to app/javascript/mediaelement-plugins/source-chooser/source-chooser.js
diff --git a/vendor/assets/mediaelement-plugins/speed/speed.js b/app/javascript/mediaelement-plugins/speed/speed.js
similarity index 100%
rename from vendor/assets/mediaelement-plugins/speed/speed.js
rename to app/javascript/mediaelement-plugins/speed/speed.js
diff --git a/app/assets/javascripts/mejs-player.js.erb b/app/javascript/mejs-player.js
similarity index 96%
rename from app/assets/javascripts/mejs-player.js.erb
rename to app/javascript/mejs-player.js
index f7fca0c5d..9b8702973 100644
--- a/app/assets/javascripts/mejs-player.js.erb
+++ b/app/javascript/mejs-player.js
@@ -7,12 +7,13 @@ class MejsPlayer {
    * Build the player options object by combining the defaults the provided extra options.
    */
   static options(extra) {
+    const svg = document.body.dataset;
     return Object.assign({
       enableAutosize: true,
       stretching: 'responsive', // matches the Ruby `stretching` helper
-      iconSprite: '<%= asset_path("mejs-controls.svg") %>',
-      iconSpritePathSkipBack: '<%= asset_path("mejs-skip-back.svg") %>',
-      iconSpritePathJumpForward: '<%= asset_path("mejs-jump-forward.svg") %>',
+      iconSprite: svg.mejsControlsSvg,
+      iconSpritePathSkipBack: svg.mejsSkipBackSvg,
+      iconSpritePathJumpForward: svg.mejsJumpForwardSvg,
       skipBackInterval: 15
     }, extra);
   }
diff --git a/app/assets/javascripts/mirrorbrain-fix.js b/app/javascript/mirrorbrain-fix.js
similarity index 95%
rename from app/assets/javascripts/mirrorbrain-fix.js
rename to app/javascript/mirrorbrain-fix.js
index eafc370d4..cd5a308cc 100644
--- a/app/assets/javascripts/mirrorbrain-fix.js
+++ b/app/javascript/mirrorbrain-fix.js
@@ -1,4 +1,4 @@
-var MirrorbrainFix = {
+window.MirrorbrainFix = {
   selectMirror: function (url, cb) {
     // Always request CDN via https
     url = url.replace(/^http:/, 'https:');
diff --git a/app/javascript/oembed-player.js b/app/javascript/oembed-player.js
new file mode 100644
index 000000000..5f5a06eb9
--- /dev/null
+++ b/app/javascript/oembed-player.js
@@ -0,0 +1,22 @@
+// esbuild entry point for the oembed/iframe embed bundle. Reduced subset of
+// application.js's manifest - deliberately excludes bxslider/theme/feed_toggle/
+// mastodon-share/clappr/relive-seek/turbolinks/bootstrap.
+//
+// When adding plugins here, also add them to application.js.
+
+import './vendor/jquery-global';
+
+import './replacehash';
+import './mirrorbrain-fix';
+import './subtitle-loader';
+import './player-deeplinks';
+import './vendor/mediaelement-and-player';
+import './mejs-player';
+import './player-gestures';
+
+import './mediaelement-plugins/source-chooser/source-chooser';
+import './mediaelement-plugins/speed/speed';
+import './mediaelement-plugins/skip-back/skip-back';
+import './mediaelement-plugins/jump-forward/jump-forward';
+
+import './vendor/timelens';
diff --git a/app/assets/javascripts/player-deeplinks.js b/app/javascript/player-deeplinks.js
similarity index 100%
rename from app/assets/javascripts/player-deeplinks.js
rename to app/javascript/player-deeplinks.js
diff --git a/app/assets/javascripts/player-gestures.js b/app/javascript/player-gestures.js
similarity index 100%
rename from app/assets/javascripts/player-gestures.js
rename to app/javascript/player-gestures.js
diff --git a/app/assets/javascripts/relive-seek.js b/app/javascript/relive-seek.js
similarity index 100%
rename from app/assets/javascripts/relive-seek.js
rename to app/javascript/relive-seek.js
diff --git a/app/assets/javascripts/replacehash.js b/app/javascript/replacehash.js
similarity index 100%
rename from app/assets/javascripts/replacehash.js
rename to app/javascript/replacehash.js
diff --git a/app/assets/javascripts/slider.js b/app/javascript/slider.js
similarity index 100%
rename from app/assets/javascripts/slider.js
rename to app/javascript/slider.js
diff --git a/app/assets/javascripts/subtitle-loader.js b/app/javascript/subtitle-loader.js
similarity index 100%
rename from app/assets/javascripts/subtitle-loader.js
rename to app/javascript/subtitle-loader.js
diff --git a/app/assets/javascripts/theme.js b/app/javascript/theme.js
similarity index 91%
rename from app/assets/javascripts/theme.js
rename to app/javascript/theme.js
index 983afa764..1ad6afaea 100644
--- a/app/assets/javascripts/theme.js
+++ b/app/javascript/theme.js
@@ -6,4 +6,5 @@ function toggleTheme(newTheme) {
     document.documentElement.classList.toggle('light', newTheme === 'light');
     currentTheme = newTheme;
 }
+window.toggleTheme = toggleTheme;
 toggleTheme(currentTheme);
diff --git a/app/javascript/vendor/clappr-playback-rate-plugin.cjs b/app/javascript/vendor/clappr-playback-rate-plugin.cjs
new file mode 100644
index 000000000..a9d763d35
--- /dev/null
+++ b/app/javascript/vendor/clappr-playback-rate-plugin.cjs
@@ -0,0 +1,2 @@
+!function(t,e){t.PlaybackRatePlugin=e(t.Clappr)}(window,(function(t){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=57)}([function(t,e,n){(function(e){var n=function(t){return t&&t.Math==Math&&t};t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e&&e)||Function("return this")()}).call(this,n(59))},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){var r=n(5);t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,e,n){var r=n(2),o=n(38),i=n(10),a=n(17),c=Object.defineProperty;e.f=r?c:function(t,e,n){if(i(t),e=a(e,!0),i(n),o)try{return c(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(0),o=n(25),i=n(1),a=n(26),c=n(30),u=n(48),f=o("wks"),l=r.Symbol,s=u?l:l&&l.withoutSetter||a;t.exports=function(t){return i(f,t)||(c&&i(l,t)?f[t]=l[t]:f[t]=s("Symbol."+t)),f[t]}},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){var r=n(2),o=n(3),i=n(14);t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(0),o=n(13).f,i=n(7),a=n(11),c=n(24),u=n(42),f=n(47);t.exports=function(t,e){var n,l,s,p,y,d=t.target,v=t.global,b=t.stat;if(n=v?r:b?r[d]||c(d,{}):(r[d]||{}).prototype)for(l in e){if(p=e[l],s=t.noTargetGet?(y=o(n,l))&&y.value:n[l],!f(v?l:d+(b?".":"#")+l,t.forced)&&void 0!==s){if(typeof p==typeof s)continue;u(p,s)}(t.sham||s&&s.sham)&&i(p,"sham",!0),a(n,l,p,t)}}},function(t,e,n){var r=n(37),o=n(16);t.exports=function(t){return r(o(t))}},function(t,e,n){var r=n(6);t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object");return t}},function(t,e,n){var r=n(0),o=n(7),i=n(1),a=n(24),c=n(40),u=n(18),f=u.get,l=u.enforce,s=String(String).split("String");(t.exports=function(t,e,n,c){var u=!!c&&!!c.unsafe,f=!!c&&!!c.enumerable,p=!!c&&!!c.noTargetGet;"function"==typeof n&&("string"!=typeof e||i(n,"name")||o(n,"name",e),l(n).source=s.join("string"==typeof e?e:"")),t!==r?(u?!p&&t[e]&&(f=!0):delete t[e],f?t[e]=n:o(t,e,n)):f?t[e]=n:a(e,n)})(Function.prototype,"toString",(function(){return"function"==typeof this&&f(this).source||c(this)}))},function(t,e,n){var r,o=n(10),i=n(64),a=n(29),c=n(21),u=n(65),f=n(39),l=n(19),s=l("IE_PROTO"),p=function(){},y=function(t){return"