From f90420f3726a7a8043bf1837e62fe97e7c4ec315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 06:23:34 +0000 Subject: [PATCH 01/46] chore(deps): bump the gems group with 2 updates Bumps the gems group with 2 updates: [rbs](https://github.com/ruby/rbs) and [haml](https://github.com/haml/haml). Updates `rbs` from 4.1.2 to 4.1.3 - [Release notes](https://github.com/ruby/rbs/releases) - [Changelog](https://github.com/ruby/rbs/blob/master/CHANGELOG.md) - [Commits](https://github.com/ruby/rbs/compare/v4.1.2...v4.1.3) Updates `haml` from 7.3.0 to 7.3.1 - [Release notes](https://github.com/haml/haml/releases) - [Changelog](https://github.com/haml/haml/blob/main/CHANGELOG.md) - [Commits](https://github.com/haml/haml/compare/v7.3.0...v7.3.1) --- updated-dependencies: - dependency-name: rbs dependency-version: 4.1.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: gems - dependency-name: haml dependency-version: 7.3.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gems ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5d2e30ac..35787ab0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -235,7 +235,7 @@ GEM base64 graphql-query-resolver (0.2.0) graphql (~> 1.0, >= 1.0.0) - haml (7.3.0) + haml (7.3.1) prism (>= 1.1.0) temple (>= 0.8.2) thor @@ -470,7 +470,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rbs (4.1.2) + rbs (4.1.3) logger prism (>= 1.6.0) tsort @@ -592,9 +592,9 @@ GEM attr_required (>= 0.0.5) faraday (~> 2.0) faraday-follow_redirects - temple (0.10.6) + temple (0.10.7) thor (1.5.0) - tilt (2.8.0) + tilt (2.9.0) timeout (0.6.1) tsort (0.2.0) turbolinks (5.2.1) From e1a3da041c2df59b07d4e42fcfc62824c9c80d37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 06:23:35 +0000 Subject: [PATCH 02/46] chore(deps): bump haml from 7.3.1 to 7.4.1 in the gems group Bumps the gems group with 1 update: [haml](https://github.com/haml/haml). Updates `haml` from 7.3.1 to 7.4.1 - [Release notes](https://github.com/haml/haml/releases) - [Changelog](https://github.com/haml/haml/blob/main/CHANGELOG.md) - [Commits](https://github.com/haml/haml/compare/v7.3.1...v7.4.1) --- updated-dependencies: - dependency-name: haml dependency-version: 7.4.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gems ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 35787ab0..0fba10a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -235,7 +235,7 @@ GEM base64 graphql-query-resolver (0.2.0) graphql (~> 1.0, >= 1.0.0) - haml (7.3.1) + haml (7.4.1) prism (>= 1.1.0) temple (>= 0.8.2) thor From 395512e04491f25dcc813f3a3ac02a28321d6612 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 06:27:23 +0000 Subject: [PATCH 03/46] chore(deps): bump the gems group with 2 updates Bumps the gems group with 2 updates: [rbs](https://github.com/ruby/rbs) and [haml](https://github.com/haml/haml). Updates `rbs` from 4.1.3 to 4.2.0 - [Release notes](https://github.com/ruby/rbs/releases) - [Changelog](https://github.com/ruby/rbs/blob/master/CHANGELOG.md) - [Commits](https://github.com/ruby/rbs/compare/v4.1.3...v4.2.0) Updates `haml` from 7.4.1 to 7.5.0 - [Release notes](https://github.com/haml/haml/releases) - [Changelog](https://github.com/haml/haml/blob/main/CHANGELOG.md) - [Commits](https://github.com/haml/haml/compare/v7.4.1...v7.5.0) --- updated-dependencies: - dependency-name: rbs dependency-version: 4.2.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: gems - dependency-name: haml dependency-version: 7.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gems ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0fba10a4..241d663e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -235,7 +235,7 @@ GEM base64 graphql-query-resolver (0.2.0) graphql (~> 1.0, >= 1.0.0) - haml (7.4.1) + haml (7.5.0) prism (>= 1.1.0) temple (>= 0.8.2) thor @@ -470,7 +470,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rbs (4.1.3) + rbs (4.2.0) logger prism (>= 1.6.0) tsort From a8216ee044b6ed8413045aefb209438f8f5c3922 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Sun, 26 Jul 2026 16:40:24 +0200 Subject: [PATCH 04/46] Use URL type for all URLs, and only URLs --- app/graphql/schema.graphql | 4 ++-- app/graphql/types/lecture_type.rb | 2 +- app/graphql/types/resource_type.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 7d0aeefc..8821da19 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -233,7 +233,7 @@ type Lecture { """ The URL slug of this event """ - slug: URL! + slug: String! """ The event's subtitle that may be displayed below the title @@ -493,7 +493,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 diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index fffffdc5..eec8d83f 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -13,7 +13,7 @@ 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, String, "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 diff --git a/app/graphql/types/resource_type.rb b/app/graphql/types/resource_type.rb index d4a32bb3..f710297b 100644 --- a/app/graphql/types/resource_type.rb +++ b/app/graphql/types/resource_type.rb @@ -4,7 +4,7 @@ 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 From 5bc2f717f75d9b63bdc37f30db4450a30a1a2ae5 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Sun, 26 Jul 2026 16:42:43 +0200 Subject: [PATCH 05/46] Make structured types non-nullable if their fields are nullable --- app/graphql/schema.graphql | 10 +++++----- app/graphql/types/lecture_type.rb | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 8821da19..279f750a 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -151,7 +151,7 @@ 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!] + audios: [Resource!]! """ The conference this event belongs to @@ -199,7 +199,7 @@ type Lecture { last: Int ): ResourceConnection! guid: ID! - images: LectureImage + images: LectureImage! """ URL pointing to the conference event website @@ -228,7 +228,7 @@ type Lecture { """ releaseDate: DateTime relive: JSON - slides: [Resource!] + slides: [Resource!]! """ The URL slug of this event @@ -239,13 +239,13 @@ type Lecture { 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 diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index eec8d83f..70f0ac6e 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -34,9 +34,9 @@ class LectureType < Types::BaseObject 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 :audios, [ResourceType], null: false + field :subtitles, [ResourceType], null: false + field :slides, [ResourceType], null: false field :files, ResourceType.connection_type, null: false # field :thumbnail, Types::ImageType, null: true @@ -44,7 +44,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 +53,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 From c30af6db91d5fc2dcca1e0c84a368ad64e948074 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Sun, 26 Jul 2026 16:43:20 +0200 Subject: [PATCH 06/46] Expose primary video via GraphQL API --- app/graphql/schema.graphql | 1 + app/graphql/types/lecture_type.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 279f750a..75ab803d 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -262,6 +262,7 @@ type Lecture { """ url: URL! videoPreferred: Resource! + videoPrimary: Resource! videos: [Resource!]! """ diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index 70f0ac6e..80c02201 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -33,6 +33,7 @@ class LectureType < Types::BaseObject 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 :video_primary, ResourceType, null: false field :videos, [ResourceType], null: false field :audios, [ResourceType], null: false field :subtitles, [ResourceType], null: false @@ -96,6 +97,10 @@ def video_preferred object.preferred_recording end + def video_primary + object.recording_for_master_feed + end + def audios object.recordings.audio end From 76d455670a7f79a9176f2aa771ef4c34812cc702 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 28 Jun 2026 12:29:27 +0200 Subject: [PATCH 07/46] feat(graphql): add Slug scalar, use on slug fields Add SlugType scalar (validates no forward slashes) and use it for slug fields on Channel, Conference, and Movie types instead of plain String. --- app/graphql/types/conference_type.rb | 2 +- app/graphql/types/lecture_type.rb | 2 +- app/graphql/types/slug_type.rb | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 app/graphql/types/slug_type.rb diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb index 673b7c81..78fddbb8 100644 --- a/app/graphql/types/conference_type.rb +++ b/app/graphql/types/conference_type.rb @@ -4,7 +4,7 @@ class ConferenceType < Types::BaseObject field :id, ID, null: false field :acronym, String, "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 :slug, SlugType, "The URL slug of this conference", null: false field :lectures, LectureType.connection_type, null: true field :url, UrlType, "A URL pointing to the conference page in vocotweb frontend", null: false diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index 80c02201..b2536251 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -13,7 +13,7 @@ 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, String, "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 diff --git a/app/graphql/types/slug_type.rb b/app/graphql/types/slug_type.rb new file mode 100644 index 00000000..3bcdf6d2 --- /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 From de25788abcade300371db008ed4edb08198e69eb Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 16 Aug 2026 21:51:26 +0200 Subject: [PATCH 08/46] feat(graphql): rename fields and add descriptions on Lecture and Resource types Rename video_primary to video for cleaner API naming. Add field descriptions to video_preferred, video, videos, audios, slides, and mime_type fields. --- app/graphql/types/lecture_type.rb | 10 +++++----- app/graphql/types/resource_type.rb | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index b2536251..9d3c4642 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -32,12 +32,12 @@ 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 :video_primary, ResourceType, null: false - field :videos, [ResourceType], null: false - field :audios, [ResourceType], null: false + 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 + 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], null: false + field :slides, [ResourceType], "PDF slides, if provided", null: false field :files, ResourceType.connection_type, null: false # field :thumbnail, Types::ImageType, null: true diff --git a/app/graphql/types/resource_type.rb b/app/graphql/types/resource_type.rb index f710297b..b5de9549 100644 --- a/app/graphql/types/resource_type.rb +++ b/app/graphql/types/resource_type.rb @@ -7,7 +7,7 @@ class ResourceType < Types::BaseObject 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 From bb9f103f77bd558c9a4d2a4d528ac85927abbdb9 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 16 Aug 2026 22:48:03 +0200 Subject: [PATCH 09/46] feat(graphql): use SlugType for conference acronym field --- app/graphql/types/conference_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb index 78fddbb8..d563cab1 100644 --- a/app/graphql/types/conference_type.rb +++ b/app/graphql/types/conference_type.rb @@ -2,7 +2,7 @@ module Types class ConferenceType < Types::BaseObject description "This entity groups multiple lectures 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, SlugType, "The URL slug of this conference", null: false field :lectures, LectureType.connection_type, null: true From 417f02e1c47e3fa4c5f13a9b5819991f75bf5313 Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 02:11:23 +0200 Subject: [PATCH 10/46] feat(graphql): add format preference args to Lecture.video field Add `prefer` (VideoPreferenceEnum list) and `quality` (VideoQualityEnum) arguments to the `video` field on LectureType, allowing clients to request a specific container/codec (MP4, AV1, DASH, WebM) before falling back to the master recording. Add VideoPreferenceEnum and VideoQualityEnum types. Cover all three format variants with integration tests. --- README.md | 7 +++++++ app/graphql/types/lecture_type.rb | 14 +++++++++++--- app/graphql/types/video_preference_enum.rb | 9 +++++++++ app/graphql/types/video_quality_enum.rb | 8 ++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 app/graphql/types/video_preference_enum.rb create mode 100644 app/graphql/types/video_quality_enum.rb diff --git a/README.md b/README.md index bb5535da..de7dbebb 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Voctoweb is a rails application that provides a “YouTube like” user interfac 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") { @@ -24,6 +26,11 @@ query LectureQueryExample { persons slug originalLanguage + video(prefer: [AV1, WEBM]) { + label + url + mimeType + } videos { label url diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index 9d3c4642..29171422 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -33,7 +33,10 @@ class LectureType < Types::BaseObject field :doi_url, UrlType, "Digital Object Identifier (DOI) e.g. https://doi.org/10.5446/19566", 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 + 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 @@ -97,8 +100,13 @@ def video_preferred object.preferred_recording end - def video_primary - object.recording_for_master_feed + 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 diff --git a/app/graphql/types/video_preference_enum.rb b/app/graphql/types/video_preference_enum.rb new file mode 100644 index 00000000..925594d1 --- /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 00000000..4c80a297 --- /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 From e04c93c750ec7fe1484abbbdaf81be3c23904100 Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 17:51:10 +0200 Subject: [PATCH 11/46] feat(graphql): add translations field to Lecture type Exposes available translation languages for a lecture as ISO 639-2 encoded strings. --- app/graphql/types/lecture_type.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/graphql/types/lecture_type.rb b/app/graphql/types/lecture_type.rb index 29171422..ccc0af95 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -17,6 +17,7 @@ class LectureType < Types::BaseObject 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 From 4e99c54d024c16aeadad0515c846d598e767cd1e Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 00:48:47 +0200 Subject: [PATCH 12/46] feat(graphql): expose promoted lectures query Add `promoted` connection field to QueryType returning promoted lectures for the start page. Supports `first`/`offset` pagination with a default limit of 10 and max page size of 30. Add integration tests covering basic listing, limiting, and connection-style pagination. --- README.md | 26 ++++++++ app/graphql/types/query_type.rb | 8 +++ test/integration/graphql/lecture_test.rb | 76 ++++++++++++++++++++++++ 3 files changed, 110 insertions(+) diff --git a/README.md b/README.md index de7dbebb..c3b0c967 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,32 @@ query LectureQueryExample { ``` +#### 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 The public API provides a programmatic access to the data behind media.ccc.de. Consumers of this API are typically player apps for different ecosystems, see https://media.ccc.de/about.html#apps for a 'full' list. The whole API is "discoverable" starting from https://api.media.ccc.de/public/conferences ; Available methods: diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index e2fdb32e..39dd1879 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -3,6 +3,14 @@ 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 diff --git a/test/integration/graphql/lecture_test.rb b/test/integration/graphql/lecture_test.rb index 47e8ee39..e62a8757 100644 --- a/test/integration/graphql/lecture_test.rb +++ b/test/integration/graphql/lecture_test.rb @@ -190,4 +190,80 @@ class LectureGraphQLApiTest < ActionDispatch::IntegrationTest assert_equal 1, result['data']['lectureSearch'].count end end + test 'load promoted lectures' do + query_string = <<-GRAPHQL + query($first: Int, $offset: Int) { + promoted(first: $first, offset: $offset) { + nodes { + title + guid + promoted + } + } + } + GRAPHQL + + # Create some promoted and non-promoted events + create(:event, promoted: true, release_date: 2.days.ago) + create(:event, promoted: true, release_date: 1.day.ago) + create(:event, promoted: false) + + result = MediaBackendSchema.execute(query_string, variables: { first: 10, offset: 0 }) + assert_nil result['errors'] + promoted_items = result['data']['promoted']['nodes'] + assert_equal 2, promoted_items.count + assert(promoted_items.all? { |l| l['promoted'] }) + end + + test 'load promoted lectures with limit' do + query_string = <<-GRAPHQL + query($first: Int, $offset: Int) { + promoted(first: $first, offset: $offset) { + nodes { + guid + } + } + } + GRAPHQL + + # Create 15 promoted events + create_list(:event, 15, promoted: true) + + result = MediaBackendSchema.execute(query_string, variables: { first: 5, offset: 0 }) + assert_nil result['errors'] + assert_equal 5, result['data']['promoted']['nodes'].count + end + + test 'load promoted lectures as connection with pagination' do + query_string = <<-GRAPHQL + query($first: Int, $offset: Int) { + promoted(first: $first, offset: $offset) { + nodes { + title + guid + promoted + } + edges { + cursor + node { + guid + } + } + pageInfo { + hasNextPage + } + } + } + GRAPHQL + + create(:event, promoted: true) + create(:event, promoted: true) + create(:event, promoted: false) + + result = MediaBackendSchema.execute(query_string, variables: { first: 10, offset: 0 }) + assert_nil result['errors'] + connection = result['data']['promoted'] + assert_equal 2, connection['nodes'].count + assert(connection['nodes'].all? { |n| n['promoted'] }) + end end From 6dad55dd6ccfec859edb3394ce21a95ffb78fe8d Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 02:02:05 +0200 Subject: [PATCH 13/46] feat(graphql): add nodes-only BaseConnection, drop edges from tests Introduce Types::BaseConnection that suppresses the `edges` field, exposing only `nodes` and `pageInfo` on all connection types. Keeps the public API minimal and consistent with a nodes-first convention. Remove the `edges` assertion from the promoted-lectures integration test to match the new connection shape. --- app/graphql/types/base_connection.rb | 10 ++++++++++ app/graphql/types/base_object.rb | 1 + test/integration/graphql/lecture_test.rb | 6 ------ 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 app/graphql/types/base_connection.rb diff --git a/app/graphql/types/base_connection.rb b/app/graphql/types/base_connection.rb new file mode 100644 index 00000000..31b11e8b --- /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 c6098610..ec056626 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/test/integration/graphql/lecture_test.rb b/test/integration/graphql/lecture_test.rb index e62a8757..4c790814 100644 --- a/test/integration/graphql/lecture_test.rb +++ b/test/integration/graphql/lecture_test.rb @@ -243,12 +243,6 @@ class LectureGraphQLApiTest < ActionDispatch::IntegrationTest guid promoted } - edges { - cursor - node { - guid - } - } pageInfo { hasNextPage } From 47cd9466616e5b9a9a7c000dec2ff2801421af1b Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 00:50:23 +0200 Subject: [PATCH 14/46] feat(graphql): expose raw metadata and streaming fields on Conference Add experimental `metadata_raw` and `streaming_raw` JSON fields to ConferenceType, exposing the underlying database metadata and streaming v2 API data respectively. Both fields are marked deprecated to signal their experimental status. --- app/graphql/types/conference_type.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb index d563cab1..439454ee 100644 --- a/app/graphql/types/conference_type.rb +++ b/app/graphql/types/conference_type.rb @@ -19,6 +19,9 @@ class ConferenceType < Types::BaseObject 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 :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 end @@ -38,5 +41,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 From 77fb97c9d5f17f5b34cb15a2776e120f1ee00efb Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 01:51:33 +0200 Subject: [PATCH 15/46] feat(graphql): add images and logo fields to Conference type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose structured image data on Conference via two new GraphQL fields: - `logo(prefer: ImageMimeTypeEnum)` — returns logo as ImageType with optional MIME preference - `images` — returns all conference images (logo, banners, variants) as [ImageType] Deprecate scalar `logo_url` and `images_url` in favour of the new structured fields. `Conference#images` aggregates logo and metadata-sourced images into a uniform list. Both REST JSON views (public and API) now include the `images` array. --- README.md | 20 +++++++ app/graphql/types/conference_type.rb | 24 +++++++- app/graphql/types/image_mime_type_enum.rb | 9 +++ app/graphql/types/image_type.rb | 10 ++++ app/graphql/types/image_type_enum.rb | 16 +++++ app/models/conference.rb | 18 ++++++ .../api/conferences/_fields.json.jbuilder | 1 + .../public/shared/_conference.json.jbuilder | 1 + test/integration/graphql/conferences_test.rb | 60 ++++++++++++++++++- 9 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 app/graphql/types/image_mime_type_enum.rb create mode 100644 app/graphql/types/image_type.rb create mode 100644 app/graphql/types/image_type_enum.rb diff --git a/README.md b/README.md index c3b0c967..3d53ed63 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,26 @@ 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 diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb index 439454ee..bf29c43f 100644 --- a/app/graphql/types/conference_type.rb +++ b/app/graphql/types/conference_type.rb @@ -10,9 +10,13 @@ class ConferenceType < Types::BaseObject 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 @@ -34,6 +38,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 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 00000000..9e3d1d73 --- /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 00000000..3bc53e1d --- /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 00000000..4ab5e04b --- /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/models/conference.rb b/app/models/conference.rb index d7dc9651..1498d172 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -99,6 +99,24 @@ def logo_url end.freeze end + def logo_img + { type: 'logo', url: logo_url, mime_type: Marcel::MimeType.for(name: logo) } if logo_exists? + end + + # list of all images assigned to this conference, includes banner, logos in light & dark variants, etc. + def images + images = [] + # fist add the logo if it exists, then add all other images + images << logo_img if logo_exists? + # add other images from metadata, if any + images += metadata.fetch('images', []).map do |image| + rest = image.except('filename').transform_keys(&:to_sym) + mime_type = image['mime_type'].presence || Marcel::MimeType.for(name: image['filename']) + { url: File.join(Settings.static_url, images_path, image['filename']), mime_type: mime_type, **rest } + end + images + end + def display_name acronym || id end diff --git a/app/views/api/conferences/_fields.json.jbuilder b/app/views/api/conferences/_fields.json.jbuilder index 2bc487f4..a21ffb2b 100644 --- a/app/views/api/conferences/_fields.json.jbuilder +++ b/app/views/api/conferences/_fields.json.jbuilder @@ -1 +1,2 @@ json.extract! conference, :id +json.images conference.images diff --git a/app/views/public/shared/_conference.json.jbuilder b/app/views/public/shared/_conference.json.jbuilder index 16726fba..416e15a7 100644 --- a/app/views/public/shared/_conference.json.jbuilder +++ b/app/views/public/shared/_conference.json.jbuilder @@ -2,5 +2,6 @@ json.extract! conference, :acronym, :aspect_ratio, :created_at, :updated_at, :ti json.webgen_location conference.slug json.logo_url conference.logo_url json.images_url conference.get_images_url +json.images conference.images json.recordings_url conference.get_recordings_url json.url public_conference_url(id: conference.acronym, format: :json) diff --git a/test/integration/graphql/conferences_test.rb b/test/integration/graphql/conferences_test.rb index 51007dd5..be9c514b 100644 --- a/test/integration/graphql/conferences_test.rb +++ b/test/integration/graphql/conferences_test.rb @@ -103,7 +103,7 @@ class ConferencesGraphQLApiTest < ActionDispatch::IntegrationTest GRAPHQL result = MediaBackendSchema.execute(query_string) assert_nil result['errors'] - assert_equal [matching.acronym], result['data']['conferences'].map { |c| c['acronym'] } + assert_equal([matching.acronym], result['data']['conferences'].map { |c| c['acronym'] }) query_string = <<-GRAPHQL query { @@ -114,6 +114,62 @@ class ConferencesGraphQLApiTest < ActionDispatch::IntegrationTest GRAPHQL result = MediaBackendSchema.execute(query_string) assert_nil result['errors'] - assert_equal [matching.acronym], result['data']['conferences'].map { |c| c['acronym'] } + assert_equal([matching.acronym], result['data']['conferences'].map { |c| c['acronym'] }) + end + + test 'query logo field with mime_type preference' do + query_string = <<-GRAPHQL + query($id: ID!) { + conference(id: $id) { + id + logo(prefer: PNG) { + url + mimeType + type + } + } + } + GRAPHQL + + create :conference, acronym: 'testconf' + result = MediaBackendSchema.execute(query_string, variables: { id: 'testconf' }) + assert_nil result['errors'] + end + test 'query images field' do + query_string = <<-GRAPHQL + query($id: ID!) { + conference(id: $id) { + id + images { + url + mimeType + type + } + } + } + GRAPHQL + + create :conference, acronym: 'testconf' + result = MediaBackendSchema.execute(query_string, variables: { id: 'testconf' }) + assert_nil result['errors'] + end + + test 'query logo field without arguments' do + query_string = <<-GRAPHQL + query($id: ID!) { + conference(id: $id) { + id + logo { + url + mimeType + type + } + } + } + GRAPHQL + + create :conference, acronym: 'testconf' + result = MediaBackendSchema.execute(query_string, variables: { id: 'testconf' }) + assert_nil result['errors'] end end From 1accdb0dac2f3a93393470ceabbaf9590f0cd95f Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 17:44:50 +0200 Subject: [PATCH 16/46] chore: cleanup --- .rubocop_todo.yml | 2 +- app/models/event.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index fb365159..5ef7719b 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/app/models/event.rb b/app/models/event.rb index a7eb3f2e..b6ff734a 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -211,7 +211,7 @@ def doi_url def video_master recordings.video_without_slides - .select { |x| x.filetype == 'video/mp4' && x.high_quality == true } + .select { |x| x.mime_type == 'video/mp4' && x.high_quality == true } .min_by { |x| x.html5 ? 1 : 0 } end From 52e139739b7ce098ec0571065a246a6a58e6e058 Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 17:50:35 +0200 Subject: [PATCH 17/46] feat(graphql): introduce generic `items`, deprecate lecture-named fields Add `item(by: {guid|slug})` query and `items` connection as generic aliases for the lecture-named equivalents. Deprecate `lecture`, `lecture_by_slug`, `lectures`, and `Conference.lectures` in favour of the new names. Add `ItemLookupInput` for the oneOf-style lookup arg. Update descriptions and README to use "item" terminology throughout. --- README.md | 2 +- app/graphql/types/conference_type.rb | 7 +++--- app/graphql/types/item_lookup_input.rb | 7 ++++++ app/graphql/types/lecture_type.rb | 4 +-- app/graphql/types/query_type.rb | 35 ++++++++++++++++++-------- 5 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 app/graphql/types/item_lookup_input.rb diff --git a/README.md b/README.md index 3d53ed63..afee01c4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 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 diff --git a/app/graphql/types/conference_type.rb b/app/graphql/types/conference_type.rb index bf29c43f..9ca57882 100644 --- a/app/graphql/types/conference_type.rb +++ b/app/graphql/types/conference_type.rb @@ -1,11 +1,12 @@ 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, SlugType, "The acronym of this conference, used as its identifier", null: false field :title, String, "The title of this conference", null: false field :slug, SlugType, "The URL slug of this conference", null: false - field :lectures, LectureType.connection_type, null: true + 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 @@ -21,7 +22,7 @@ class ConferenceType < Types::BaseObject 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" diff --git a/app/graphql/types/item_lookup_input.rb b/app/graphql/types/item_lookup_input.rb new file mode 100644 index 00000000..fa278e21 --- /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 ccc0af95..8c8f4b45 100644 --- a/app/graphql/types/lecture_type.rb +++ b/app/graphql/types/lecture_type.rb @@ -74,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 diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 39dd1879..7a727da5 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -12,8 +12,8 @@ def promoted(offset:, first: nil) 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) @@ -22,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 @@ -42,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 @@ -52,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:) From 4b029e3fd1be22a06f9f2dae83f3c0bfee424039 Mon Sep 17 00:00:00 2001 From: Andi Date: Mon, 17 Aug 2026 17:51:42 +0200 Subject: [PATCH 18/46] chore(graphql): update dumped schema --- app/graphql/schema.graphql | 374 +++++++++++++++++++++++++++++-------- 1 file changed, 295 insertions(+), 79 deletions(-) diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 75ab803d..cdfe170f 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,6 +282,9 @@ 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 { + """ + all audio recordings assigned to this item + """ audios: [Resource!]! """ @@ -228,12 +362,16 @@ type Lecture { """ releaseDate: DateTime relive: JSON + + """ + PDF slides, if provided + """ slides: [Resource!]! """ The URL slug of this event """ - slug: String! + slug: Slug! """ The event's subtitle that may be displayed below the title @@ -252,6 +390,11 @@ type Lecture { """ 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,8 +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! - videoPrimary: Resource! + + """ + all video recordings assigned to this item + """ videos: [Resource!]! """ @@ -275,11 +430,6 @@ type Lecture { The connection type for Lecture. """ type LectureConnection { - """ - A list of edges. - """ - edges: [LectureEdge] - """ A list of nodes. """ @@ -291,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!] @@ -371,7 +506,7 @@ type Query { _service: _Service! """ - All conferences as Connection + All conferences with pagination support """ allConferences( """ @@ -396,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( """ @@ -443,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 } """ @@ -472,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! @@ -506,11 +692,6 @@ type Resource { The connection type for Resource. """ type ResourceConnection { - """ - A list of edges. - """ - edges: [ResourceEdge] - """ A list of nodes. """ @@ -522,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 """ @@ -557,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 From 8e963d9dc4be29a2a0811d0dcd0038a86086c82d Mon Sep 17 00:00:00 2001 From: Andi Date: Wed, 2 Sep 2026 00:35:12 +0200 Subject: [PATCH 19/46] fix(public-api): guard against nil metadata in confernces images builder Use `(metadata || {})` to avoid NoMethodError when metadata is nil. Add test to cover nil metadata case. --- app/models/conference.rb | 2 +- test/models/conference_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/conference.rb b/app/models/conference.rb index 1498d172..d982daa0 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -109,7 +109,7 @@ def images # fist add the logo if it exists, then add all other images images << logo_img if logo_exists? # add other images from metadata, if any - images += metadata.fetch('images', []).map do |image| + images += (metadata || {}).fetch('images', []).map do |image| rest = image.except('filename').transform_keys(&:to_sym) mime_type = image['mime_type'].presence || Marcel::MimeType.for(name: image['filename']) { url: File.join(Settings.static_url, images_path, image['filename']), mime_type: mime_type, **rest } diff --git a/test/models/conference_test.rb b/test/models/conference_test.rb index b49259dc..a9731fdb 100644 --- a/test/models/conference_test.rb +++ b/test/models/conference_test.rb @@ -40,6 +40,11 @@ class ConferenceTest < ActiveSupport::TestCase assert_equal "#{Settings.static_url}/frabcon123/test.png", @conference.logo_url end + test 'should build images list without error when metadata is nil' do + @conference = create(:conference, metadata: nil) + assert_equal [], @conference.images + end + test 'should return unknown logo if missing' do @conference = create(:conference, logo: nil) assert_equal "#{Settings.static_url}/unknown.png", @conference.logo_url From 25c7dcf59fb2f64ca7f25525c82712fe2699d4af Mon Sep 17 00:00:00 2001 From: Andi Date: Wed, 2 Sep 2026 00:37:28 +0200 Subject: [PATCH 20/46] fix(public-api): guard against nil metadata in Event#related_events and #remote_id Same NoMethodError class as the Conference#images 500 fixed in 8e963d9: `metadata['related']`/`metadata['remote_id']` crash when the jsonb column is NULL (schema default doesn't apply retroactively, and the column allows NULL). `lectures_related_to` in query_type.rb calls related_events directly, so this was reachable from the public GraphQL API. Assisted-by: LLM --- app/models/event.rb | 8 ++++---- test/models/event_test.rb | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index b6ff734a..62d40d4f 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -161,9 +161,9 @@ def persons_text end def related_events - unless metadata['related'].nil? - ids = metadata['related'].keys - Event.includes([:conference]).find(ids) + related = (metadata || {})['related'] + unless related.nil? + Event.includes([:conference]).find(related.keys) end end @@ -182,7 +182,7 @@ def promotion_disabled=(val) # for elastic search def remote_id - metadata['remote_id'] + (metadata || {})['remote_id'] end # used by player and graphql diff --git a/test/models/event_test.rb b/test/models/event_test.rb index f9947fef..d7941c69 100644 --- a/test/models/event_test.rb +++ b/test/models/event_test.rb @@ -213,6 +213,13 @@ class EventTest < ActiveSupport::TestCase assert_includes e.related_events, @event end + test 'related_events and remote_id should not error when metadata is nil' do + e = create(:event, metadata: nil) + + assert_nil e.related_events + assert_nil e.remote_id + end + test 'videos_sorted_by_language should prioritize by original language and width (html5 is negligible)' do event = create(:event, original_language: 'eng') From ad3a3db988205c0a2812cbf4018f44d0a287c530 Mon Sep 17 00:00:00 2001 From: Andi Date: Wed, 2 Sep 2026 21:47:01 +0200 Subject: [PATCH 21/46] =?UTF-8?q?chore(deps):=20bump=20psych=205.4.0=20?= =?UTF-8?q?=E2=86=92=205.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves ambiguous gem spec warning during bundle install caused by multiple psych versions satisfying rdoc's `>= 4.0.0` constraint. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 241d663e..b0d6d6cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -393,7 +393,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) From 9bf01c683c07e1c29c9d1f939692caea21a96a10 Mon Sep 17 00:00:00 2001 From: Andi Date: Wed, 2 Sep 2026 21:47:50 +0200 Subject: [PATCH 22/46] fix(deploy): switch CAP_REPO to SSH URL --- deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.sh b/deploy.sh index f900ccba..ca9e8e5d 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,6 @@ #!/bin/sh -export CAP_REPO=https://github.com/voc/voctoweb.git +export CAP_REPO=git@github.com:voc/voctoweb.git export CAP_BRANCH=main export CAP_USER=media From 87139269571324b984b664a976e67dda70f40587 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:59:39 +0000 Subject: [PATCH 23/46] chore(deps): bump actions/cache from 5 to 6 (#939) Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f7f795..a02148a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: 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') }} From 0c75adb95150ac36597857e221c019a32762601d Mon Sep 17 00:00:00 2001 From: Andi Date: Sat, 20 Jun 2026 18:56:07 +0200 Subject: [PATCH 24/46] feat: show git revision in footer Add APP_REVISION constant via initializer (reads Capistrano's REVISION file, falls back to `git rev-parse --short HEAD`, then 'dev'). Link it in the footer pointing at the corresponding git commit. --- app/views/frontend/shared/_footer.haml | 1 + config/initializers/revision.rb | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 config/initializers/revision.rb diff --git a/app/views/frontend/shared/_footer.haml b/app/views/frontend/shared/_footer.haml index 183197b1..42487748 100644 --- a/app/views/frontend/shared/_footer.haml +++ b/app/views/frontend/shared/_footer.haml @@ -11,3 +11,4 @@ %a.inverted{href: '/about.html#privacy'} Privacy –– %a.inverted{href: '//c3voc.de/'} c3voc + %a.inverted.pull-right{href: "//forgejo.c3voc.de/voc/voctoweb/commit/#{APP_REVISION}"}= APP_REVISION diff --git a/config/initializers/revision.rb b/config/initializers/revision.rb new file mode 100644 index 00000000..14222d29 --- /dev/null +++ b/config/initializers/revision.rb @@ -0,0 +1,5 @@ +APP_REVISION = if (f = Rails.root.join('REVISION')).exist? + f.read.strip[0, 7] +else + `git rev-parse --short HEAD 2>/dev/null`.strip.presence || 'dev' +end From a0492323aa20c1196021280005b6852a7f7e91b1 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 6 Sep 2026 21:35:15 +0200 Subject: [PATCH 25/46] ci: deploy to staging when PR has "deploy" label --- .github/workflows/ci.yml | 9 +++++ .github/workflows/deploy.yml | 71 +++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a02148a1..160df276 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: @@ -86,3 +87,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 f2d8d554..a6017d65 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,6 +2,17 @@ name: Deploy staging 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: @@ -12,6 +23,8 @@ jobs: steps: - uses: actions/checkout@v7 + with: + ref: ${{ inputs.ref || github.ref_name }} - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -25,9 +38,65 @@ jobs: env: CAP_REPO: https://github.com/voc/voctoweb.git CAP_USER: media - CAP_BRANCH: ${{ github.ref_name }} + 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@v8 + 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@v8 + 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", + }); + } + } From 1cef91afc450bae5d07bd3a62dab9b7a6e9d4dac Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 21 Jun 2026 15:34:22 +0200 Subject: [PATCH 26/46] admin: show schedule_url in conference index --- app/admin/conference.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/admin/conference.rb b/app/admin/conference.rb index f674466f..ef499cbd 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 From ee8cea9a7b864ece6a5bcc8a2153ed177cfe7d1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Sep 2026 06:22:44 +0000 Subject: [PATCH 27/46] chore(deps): bump actions/github-script from 8 to 9 Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v8...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a6017d65..bd664beb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,7 +53,7 @@ jobs: pull-requests: write steps: - name: Remove deploy label from other PRs - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: script: | const { owner, repo } = context.repo; @@ -78,7 +78,7 @@ jobs: pull-requests: write steps: - name: Move deploy label to PR for the deployed branch - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: script: | const { owner, repo } = context.repo; From 4dd2757e4620f715e4584b15688a5381e7287b1e Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Sun, 21 Jun 2026 16:14:25 +0200 Subject: [PATCH 28/46] wip --- rails_8_upgrade_plan.md | 146 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 rails_8_upgrade_plan.md diff --git a/rails_8_upgrade_plan.md b/rails_8_upgrade_plan.md new file mode 100644 index 00000000..5753b77b --- /dev/null +++ b/rails_8_upgrade_plan.md @@ -0,0 +1,146 @@ +# Voctoweb: Rails 8.0 Upgrade & Asset Modernization Plan (pnpm + esbuild + Propshaft) + +This document details the upgrade of **Voctoweb** to **Rails 8.0.x** and the migration of the asset pipeline from **Sprockets** to **Propshaft**, managed through **pnpm** and **esbuild**. + +By adopting this stack, you will be able to manage your frontend player libraries (MediaElement, Clappr, Shaka) via NPM modules rather than committing compiled javascript assets directly to your repository history. + +--- + +## 1. Core Architecture Decisions + +1. **Direct Upgrade (Single Branch):** Skip the dual-booting configuration and execute the upgrade directly on an upgrade branch. +2. **Asset Pipeline:** Migrate from Sprockets to **Propshaft**. +3. **JS Bundling:** Use **`jsbundling-rails`** running **esbuild** to compile javascript entry points. +4. **CSS Bundling:** Use **`cssbundling-rails`** running **sass** to compile SCSS assets. +5. **Package Manager:** Use **pnpm** to manage frontend packages. Having `pnpm-lock.yaml` in the root of the project automatically tells Rails to use `pnpm` for assets compilation. +6. **Infrastructure:** Retain **Redis** and **Sidekiq 8.0** for background processing and caching. + +--- + +## 2. Dependencies Migration (Gemfile & package.json) + +### Gemfile Changes +Remove legacy Sprockets and unused gems, and add the Propshaft/bundlers: + +```ruby +# Remove these gems +gem 'sprockets-rails' +gem 'sass-rails' +gem 'coffee-rails' +gem 'jquery-rails' +gem 'turbolinks' + +# Add these gems +gem 'propshaft' +gem 'jsbundling-rails' +gem 'cssbundling-rails' +``` + +### package.json Setup via pnpm +Initialize your package metadata and install core packages and players: + +```bash +# 1. Initialize pnpm package configuration +pnpm init + +# 2. Add build tooling to development dependencies +pnpm add -D esbuild sass + +# 3. Add core Rails dependencies and layout requirements +pnpm add @hotwired/turbo-rails jquery bootstrap@3 + +# 4. Add video/audio players from the NPM registry +pnpm add mediaelement clappr shaka-player +``` + +Your `package.json` should contain the following scripts to build JS and CSS: +```json +{ + "scripts": { + "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets", + "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules" + } +} +``` + +--- + +## 3. Step-by-Step Migration Guide + +### Step 1: Clean Up Git History +Delete the legacy players and unused config files from your repository: +* `app/assets/javascripts/clappr-dash-shaka-playback.js` +* `app/assets/javascripts/jquery.bxslider-v4.2.1d-ssfrontend.js` +* `app/assets/javascripts/active_admin.js.coffee` (deleted in previous step) +* `vendor/assets/mediaelement/` +* `vendor/assets/mediaelement-plugins/` + +### Step 2: Configure Javascript Entry Points +1. **Expose jQuery globally:** Old plugins and layouts expect `$` on the `window`. Add this to `app/javascript/application.js`: + ```javascript + import jquery from 'jquery'; + window.jQuery = jquery; + window.$ = jquery; + ``` +2. **Import player modules:** Add imports for the player packages: + ```javascript + import 'mediaelement/build/mediaelement-and-player.js'; + import Clappr from 'clappr'; + import ShakaPlayer from 'shaka-player'; + + window.Clappr = Clappr; + window.shaka = ShakaPlayer; + ``` +3. **ActiveAdmin entry point:** Create [app/javascript/active_admin.js](file:///home/mm/co/voctoweb/app/assets/javascripts/active_admin.js) containing only: + ```javascript + import "@activeadmin/activeadmin"; + ``` +4. **Refactor `mejs-player.js.erb` to `mejs-player.js`:** + Remove the ERB interpolation (`<%= asset_path(...) %>`). Instead, retrieve player control SVG assets dynamically at runtime using HTML5 data attributes: + ```javascript + // In mejs-player.js + const configEl = document.getElementById('player-config'); + const options = { + iconSprite: configEl.dataset.controlsSvg, + iconSpritePathSkipBack: configEl.dataset.skipBackSvg, + iconSpritePathJumpForward: configEl.dataset.jumpForwardSvg + }; + ``` + +### Step 3: Configure CSS Entry Point +Create `app/assets/stylesheets/application.bootstrap.scss` and import Bootstrap 3 and the players: +```scss +// Import Bootstrap 3 from node_modules +@import "bootstrap/dist/css/bootstrap.css"; + +// Import your custom SCSS sheets +@import "frontend/styles"; +@import "mediaelement/build/mediaelementplayer.css"; +``` + +### Step 4: Upgrade Rails +1. Change your `Gemfile` Rails version constraint to `gem 'rails', '~> 8.0.0'`. +2. Run `pnpm install` (this generates `pnpm-lock.yaml` which tells Rails to compile assets using `pnpm`). +3. Run `bundle install`. +4. Run the update task: + ```bash + bin/rails app:update + ``` +5. Remove Sprockets compression setups in [production.rb](file:///home/mm/co/voctoweb/config/environments/production.rb): + ```diff + - config.assets.configure do |env| + - env.js_compressor = :uglifier + - env.css_compressor = :sass + - end + - config.assets.compile = false + ``` + +### Step 5: Test the Build +1. Compile assets locally to verify `pnpm` and `esbuild` tasks: + ```bash + rails assets:precompile + ``` +2. Run the Minitest suite to verify full codebase stability: + ```bash + bin/rails test + ``` From 1087ba7c6b8e5b554223c7fe3cd12055ea10cab3 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 6 Sep 2026 16:33:17 +0200 Subject: [PATCH 29/46] chore: upgrade Rails from 7.2 to 8.1 Bump rails constraint to ~> 8.0 in Gemfile and relock dependencies, updating actionpack/activerecord/activesupport etc. to 8.1.3.1 and pulling in action_text-trix. --- Gemfile | 2 +- Gemfile.lock | 123 +++++++++++++++++++++++++-------------------------- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/Gemfile b/Gemfile index d136f963..e2cdd8f9 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' gem 'openssl' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 7.2.2' +gem 'rails', '~> 8.0' gem 'dotenv-rails' gem 'activeadmin' diff --git a/Gemfile.lock b/Gemfile.lock index b0d6d6cd..b8afb8d5 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,33 +66,34 @@ 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) @@ -142,7 +143,6 @@ 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) @@ -426,20 +426,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 +451,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) @@ -681,7 +680,7 @@ DEPENDENCIES puma puma_worker_killer rack-cors - rails (~> 7.2.2) + rails (~> 8.0) rails-controller-testing rbs redcarpet From 8226e39158a733bc140d7c7b1750d7a6b593eed3 Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 6 Sep 2026 17:43:53 +0200 Subject: [PATCH 30/46] build: migrate asset pipeline from Sprockets to Propshaft + esbuild/sass via pnpm Replaces sass-rails/coffee-rails/jquery-rails/turbolinks/bootstrap-sass/uglifier with propshaft + jsbundling-rails (esbuild) + cssbundling-rails (sass), managed via pnpm. Rails 8 dropped default Sprockets support in favor of this stack. - JS: app/javascript/{application,oembed-player,active_admin}.js are the three esbuild entry points, mirroring the old Sprockets manifests file-for-file and in the same require order. Genuinely custom/vendored code (mediaelement + plugins, the in-house VOCPlayer/Clappr fork and its 2 plugins, timelens, the bxslider fork, purl, an old handlebars alpha) is kept byte-identical as local imports rather than swapped for fresh npm packages, to avoid version-drift risk on a live video site. jquery/jquery-ujs/turbolinks are pinned to the exact versions the old jquery-rails/turbolinks gems served. bootstrap-sass and @activeadmin/activeadmin are pinned to the exact gem versions they replace (3.4.1 and 3.5.2, matching exactly). - clappr-thumbnails-plugin/clappr-playback-rate-plugin do `require("clappr")` in their UMD wrapper for a real Clappr peer that doesn't exist here (our Clappr global is the vendored VOCPlayer fork) - marked external so esbuild leaves the unreachable require() call alone instead of failing to resolve it. - mejs-player.js: dropped the .erb extension and its 3 asset_path() calls (esbuild can't process ERB) in favor of reading SVG icon sprite paths off data-* attributes rendered onto by the two frontend layouts. - CSS: application.scss/embed.scss/active_admin.scss replace the Sprockets `*= require` manifests with plain @import chains. font-url()/image-url()/ asset-url() (sass-rails-only Sass functions, unavailable in plain dart-sass) become plain url(...) - Propshaft rewrites those to fingerprinted paths itself as a post-processing step, so this is a lossless conversion. - vendor/assets/mediaelement-plugins/ renamed to vendor/assets/player-plugins/: Propshaft::LoadPath#dedup does a naive string prefix check, and would otherwise silently drop this path as a false "duplicate" of the sibling vendor/assets/mediaelement path (mediaelement-plugins.start_with?(mediaelement)). - Extra asset roots moved from config/initializers/assets.rb into config/application.rb: Propshaft snapshots config.assets.paths into its asset assembly before user initializers run, so paths added from an initializer were silently ignored. - config/initializers/sass_skip.rb deleted: worked around a Sprockets+GraphiQL CSS-compression bug; Propshaft doesn't compress anything, so it's moot. - production.rb's `config.assets.configure do |env| ... end` block called a Sprockets-only method that no longer exists on config.assets - would have crashed production boot outright. Removed along with config.assets.compile. Verified: bin/rails test (0 failures, same pre-existing ES-connection errors as before), assets:precompile, and manual requests against the homepage, admin login, an event page, and its oembed page all render the correct fingerprinted bundles with no server errors. Not verified in an actual browser (no browser tool available this session) - JS runtime behavior of the players themselves still needs a manual check. Assisted-by: LLM --- .gitignore | 3 + Gemfile | 22 +- Gemfile.lock | 53 +- app/assets/builds/.keep | 0 app/assets/config/manifest.js | 10 - app/assets/javascripts/active_admin.js | 1 - app/assets/javascripts/application.js | 48 - .../javascripts/clappr-dash-shaka-playback.js | 1362 ----------------- app/assets/javascripts/oembed-player.js | 15 - ...ctive_admin.css.scss => active_admin.scss} | 11 +- app/assets/stylesheets/application.css | 29 - app/assets/stylesheets/application.scss | 20 + app/assets/stylesheets/embed.css.scss | 31 - app/assets/stylesheets/embed.scss | 33 + .../stylesheets/frontend/lib/_fonts.scss | 16 +- .../stylesheets/frontend/lib/_variables.scss | 2 +- app/assets/stylesheets/frontend/styles.scss | 4 +- ...bxslider.css.scss => jquery.bxslider.scss} | 10 +- app/assets/stylesheets/player-fixes.scss | 4 +- .../activate-timelens.js | 0 app/javascript/active_admin.js | 1 + app/javascript/application.js | 43 + .../javascripts => javascript}/feed_toggle.js | 0 .../mastodon-share.js | 0 .../jump-forward/jump-forward.js | 0 .../mediaelement-plugins/playlist/playlist.js | 0 .../mediaelement-plugins/postroll/postroll.js | 0 .../skip-back/skip-back.js | 0 .../source-chooser/source-chooser.js | 0 .../mediaelement-plugins/speed/speed.js | 0 .../mejs-player.js} | 7 +- .../mirrorbrain-fix.js | 0 app/javascript/oembed-player.js | 24 + .../player-deeplinks.js | 0 .../player-gestures.js | 0 .../javascripts => javascript}/relive-seek.js | 0 .../javascripts => javascript}/replacehash.js | 0 .../javascripts => javascript}/slider.js | 0 .../subtitle-loader.js | 0 .../javascripts => javascript}/theme.js | 0 .../vendor/clappr-playback-rate-plugin.cjs | 0 .../vendor/clappr-thumbnails-plugin.cjs | 0 .../vendor}/handlebars.min-latest.js | 0 .../vendor/jquery.bxslider.js} | 0 .../vendor}/mediaelement-and-player.js | 0 .../javascript/vendor}/player.umd.js | 0 .../javascript/vendor}/purl.min.js | 0 .../javascript/vendor}/timelens.js | 0 app/views/layouts/frontend/frontend.html.haml | 2 +- app/views/layouts/frontend/oembed.html.haml | 2 +- config/application.rb | 13 + config/environments/development.rb | 5 - config/environments/production.rb | 12 - config/initializers/assets.rb | 28 - config/initializers/sass_skip.rb | 20 - package.json | 35 + pnpm-lock.yaml | 671 ++++++++ pnpm-workspace.yaml | 3 + vendor/assets/icomoon-font/icomoon-font.scss | 10 +- .../jump-forward/jump-forward-i18n.js | 0 .../plugins/jump-forward/jump-forward.js | 47 + .../plugins}/jump-forward/jumpforward.svg | 0 .../plugins}/playlist/playlist-controls.svg | 0 .../plugins}/playlist/playlist-i18n.js | 0 .../mediaelement/plugins/playlist/playlist.js | 348 +++++ .../plugins}/playlist/playlist.scss | 2 +- .../plugins}/postroll/postroll-i18n.js | 0 .../plugins}/postroll/postroll.css | 0 .../mediaelement/plugins/postroll/postroll.js | 58 + .../plugins}/skip-back/skip-back-i18n.js | 0 .../plugins/skip-back/skip-back.js | 46 + .../plugins}/skip-back/skipback.svg | 0 .../plugins}/source-chooser/settings.png | Bin .../plugins}/source-chooser/settings.svg | 0 .../source-chooser/source-chooser-i18n.js | 0 .../plugins/source-chooser/source-chooser.js | 206 +++ .../source-chooser/source-chooser.scss | 2 +- .../plugins}/speed/speed-i18n.js | 0 .../plugins}/speed/speed.css | 0 .../mediaelement/plugins/speed/speed.js | 206 +++ 80 files changed, 1808 insertions(+), 1657 deletions(-) create mode 100644 app/assets/builds/.keep delete mode 100644 app/assets/config/manifest.js delete mode 100644 app/assets/javascripts/active_admin.js delete mode 100644 app/assets/javascripts/application.js delete mode 100644 app/assets/javascripts/clappr-dash-shaka-playback.js delete mode 100644 app/assets/javascripts/oembed-player.js rename app/assets/stylesheets/{active_admin.css.scss => active_admin.scss} (74%) delete mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/application.scss delete mode 100644 app/assets/stylesheets/embed.css.scss create mode 100644 app/assets/stylesheets/embed.scss rename app/assets/stylesheets/{jquery.bxslider.css.scss => jquery.bxslider.scss} (90%) rename app/{assets/javascripts => javascript}/activate-timelens.js (100%) create mode 100644 app/javascript/active_admin.js create mode 100644 app/javascript/application.js rename app/{assets/javascripts => javascript}/feed_toggle.js (100%) rename app/{assets/javascripts => javascript}/mastodon-share.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/jump-forward/jump-forward.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/playlist/playlist.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/postroll/postroll.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/skip-back/skip-back.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/source-chooser/source-chooser.js (100%) rename {vendor/assets => app/javascript}/mediaelement-plugins/speed/speed.js (100%) rename app/{assets/javascripts/mejs-player.js.erb => javascript/mejs-player.js} (96%) rename app/{assets/javascripts => javascript}/mirrorbrain-fix.js (100%) create mode 100644 app/javascript/oembed-player.js rename app/{assets/javascripts => javascript}/player-deeplinks.js (100%) rename app/{assets/javascripts => javascript}/player-gestures.js (100%) rename app/{assets/javascripts => javascript}/relive-seek.js (100%) rename app/{assets/javascripts => javascript}/replacehash.js (100%) rename app/{assets/javascripts => javascript}/slider.js (100%) rename app/{assets/javascripts => javascript}/subtitle-loader.js (100%) rename app/{assets/javascripts => javascript}/theme.js (100%) rename vendor/assets/javascripts/clappr-playback-rate-plugin.js => app/javascript/vendor/clappr-playback-rate-plugin.cjs (100%) rename vendor/assets/javascripts/clappr-thumbnails-plugin.js => app/javascript/vendor/clappr-thumbnails-plugin.cjs (100%) rename {vendor/assets/javascripts => app/javascript/vendor}/handlebars.min-latest.js (100%) rename app/{assets/javascripts/jquery.bxslider-v4.2.1d-ssfrontend.js => javascript/vendor/jquery.bxslider.js} (100%) rename {vendor/assets/javascripts => app/javascript/vendor}/mediaelement-and-player.js (100%) rename {vendor/assets/javascripts => app/javascript/vendor}/player.umd.js (100%) rename {vendor/assets/javascripts => app/javascript/vendor}/purl.min.js (100%) rename {vendor/assets/javascripts => app/javascript/vendor}/timelens.js (100%) delete mode 100644 config/initializers/assets.rb delete mode 100644 config/initializers/sass_skip.rb create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/jump-forward/jump-forward-i18n.js (100%) create mode 100644 vendor/assets/mediaelement/plugins/jump-forward/jump-forward.js rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/jump-forward/jumpforward.svg (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/playlist/playlist-controls.svg (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/playlist/playlist-i18n.js (100%) create mode 100644 vendor/assets/mediaelement/plugins/playlist/playlist.js rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/playlist/playlist.scss (98%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/postroll/postroll-i18n.js (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/postroll/postroll.css (100%) create mode 100644 vendor/assets/mediaelement/plugins/postroll/postroll.js rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/skip-back/skip-back-i18n.js (100%) create mode 100644 vendor/assets/mediaelement/plugins/skip-back/skip-back.js rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/skip-back/skipback.svg (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/source-chooser/settings.png (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/source-chooser/settings.svg (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/source-chooser/source-chooser-i18n.js (100%) create mode 100644 vendor/assets/mediaelement/plugins/source-chooser/source-chooser.js rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/source-chooser/source-chooser.scss (95%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/speed/speed-i18n.js (100%) rename vendor/assets/{mediaelement-plugins => mediaelement/plugins}/speed/speed.css (100%) create mode 100644 vendor/assets/mediaelement/plugins/speed/speed.js diff --git a/.gitignore b/.gitignore index ff20cd25..eff9bf88 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/Gemfile b/Gemfile index e2cdd8f9..57c51fad 100644 --- a/Gemfile +++ b/Gemfile @@ -54,24 +54,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 b8afb8d5..fbedd494 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,8 +108,6 @@ 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) @@ -117,9 +115,6 @@ GEM benchmark (0.5.0) bigdecimal (4.1.2) 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) @@ -144,18 +139,13 @@ GEM capistrano-bundler sidekiq (>= 7.0) 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) @@ -267,6 +256,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) @@ -384,6 +375,10 @@ GEM 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) @@ -511,16 +506,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) @@ -570,14 +555,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 @@ -596,13 +573,8 @@ 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) @@ -642,15 +614,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 @@ -665,7 +636,7 @@ DEPENDENCIES graphql-query-resolver haml jbuilder - jquery-rails + jsbundling-rails kaminari listen minitest (~> 5.1) @@ -675,6 +646,7 @@ DEPENDENCIES omniauth_openid_connect openssl pg + propshaft pry-byebug pry-rails puma @@ -687,14 +659,11 @@ DEPENDENCIES 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/app/assets/builds/.keep b/app/assets/builds/.keep new file mode 100644 index 00000000..e69de29b diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js deleted file mode 100644 index 699bfd7b..00000000 --- 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 d2b66c59..00000000 --- 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 0036b372..00000000 --- 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 49d24a4a..00000000 --- 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 94640b35..00000000 --- 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 74% rename from app/assets/stylesheets/active_admin.css.scss rename to app/assets/stylesheets/active_admin.scss index 744597f8..d863bac9 100644 --- a/app/assets/stylesheets/active_admin.css.scss +++ b/app/assets/stylesheets/active_admin.scss @@ -1,8 +1,7 @@ // 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; @@ -19,13 +18,13 @@ $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 b5400803..00000000 --- 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 00000000..a5bce5df --- /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 343d1e33..00000000 --- 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 00000000..9d17d66c --- /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/lib/_fonts.scss b/app/assets/stylesheets/frontend/lib/_fonts.scss index c001df3a..e5b1306a 100644 --- a/app/assets/stylesheets/frontend/lib/_fonts.scss +++ b/app/assets/stylesheets/frontend/lib/_fonts.scss @@ -1,38 +1,38 @@ /* font Estrangelo Edessa */ @font-face { font-family: 'Estrangelo Edessa'; - src: font-url('estre.eot'); + src: url('estre.eot'); font-weight: normal; font-style: normal; } @font-face { font-family: 'Estrangelo Edessa'; - src: font-url('estre.otf') format('opentype'); + src: 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'); + src: url('estre.ttf') format('truetype'), + url('estre.woff') format('woff'), + 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 63c2f18a..1496079c 100644 --- a/app/assets/stylesheets/frontend/lib/_variables.scss +++ b/app/assets/stylesheets/frontend/lib/_variables.scss @@ -1,4 +1,4 @@ -@import "bootstrap/variables"; +@import "bootstrap-sass/assets/stylesheets/bootstrap/variables"; // Override Bootstrap variables here (defaults from bootstrap-sass v3.1.1.1): diff --git a/app/assets/stylesheets/frontend/styles.scss b/app/assets/stylesheets/frontend/styles.scss index 1a545ad4..07c27e32 100644 --- a/app/assets/stylesheets/frontend/styles.scss +++ b/app/assets/stylesheets/frontend/styles.scss @@ -1,8 +1,8 @@ // library setup @import "lib/fonts"; @import "lib/variables"; -@import "bootstrap-sprockets"; -@import "bootstrap"; +@import "bootstrap-sass/assets/stylesheets/bootstrap-sprockets"; +@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 b8cebd58..d30b4db4 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 3313de9a..780494f6 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/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 00000000..c16caf68 --- /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 00000000..a9d83e2f --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,43 @@ +// 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 jquery from 'jquery'; +window.jQuery = jquery; +window.$ = jquery; + +import './replacehash'; +import 'jquery-ujs'; +import 'bootstrap-sass/assets/javascripts/bootstrap'; +import 'turbolinks'; + +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 100% rename from app/assets/javascripts/mastodon-share.js rename to app/javascript/mastodon-share.js 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 f7fca0c5..9b870297 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 100% rename from app/assets/javascripts/mirrorbrain-fix.js rename to app/javascript/mirrorbrain-fix.js diff --git a/app/javascript/oembed-player.js b/app/javascript/oembed-player.js new file mode 100644 index 00000000..3096f7b9 --- /dev/null +++ b/app/javascript/oembed-player.js @@ -0,0 +1,24 @@ +// 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 jquery from 'jquery'; +window.jQuery = jquery; +window.$ = jquery; + +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 100% rename from app/assets/javascripts/theme.js rename to app/javascript/theme.js diff --git a/vendor/assets/javascripts/clappr-playback-rate-plugin.js b/app/javascript/vendor/clappr-playback-rate-plugin.cjs similarity index 100% rename from vendor/assets/javascripts/clappr-playback-rate-plugin.js rename to app/javascript/vendor/clappr-playback-rate-plugin.cjs diff --git a/vendor/assets/javascripts/clappr-thumbnails-plugin.js b/app/javascript/vendor/clappr-thumbnails-plugin.cjs similarity index 100% rename from vendor/assets/javascripts/clappr-thumbnails-plugin.js rename to app/javascript/vendor/clappr-thumbnails-plugin.cjs diff --git a/vendor/assets/javascripts/handlebars.min-latest.js b/app/javascript/vendor/handlebars.min-latest.js similarity index 100% rename from vendor/assets/javascripts/handlebars.min-latest.js rename to app/javascript/vendor/handlebars.min-latest.js diff --git a/app/assets/javascripts/jquery.bxslider-v4.2.1d-ssfrontend.js b/app/javascript/vendor/jquery.bxslider.js similarity index 100% rename from app/assets/javascripts/jquery.bxslider-v4.2.1d-ssfrontend.js rename to app/javascript/vendor/jquery.bxslider.js diff --git a/vendor/assets/javascripts/mediaelement-and-player.js b/app/javascript/vendor/mediaelement-and-player.js similarity index 100% rename from vendor/assets/javascripts/mediaelement-and-player.js rename to app/javascript/vendor/mediaelement-and-player.js diff --git a/vendor/assets/javascripts/player.umd.js b/app/javascript/vendor/player.umd.js similarity index 100% rename from vendor/assets/javascripts/player.umd.js rename to app/javascript/vendor/player.umd.js diff --git a/vendor/assets/javascripts/purl.min.js b/app/javascript/vendor/purl.min.js similarity index 100% rename from vendor/assets/javascripts/purl.min.js rename to app/javascript/vendor/purl.min.js diff --git a/vendor/assets/javascripts/timelens.js b/app/javascript/vendor/timelens.js similarity index 100% rename from vendor/assets/javascripts/timelens.js rename to app/javascript/vendor/timelens.js diff --git a/app/views/layouts/frontend/frontend.html.haml b/app/views/layouts/frontend/frontend.html.haml index 98541bd4..267813df 100644 --- a/app/views/layouts/frontend/frontend.html.haml +++ b/app/views/layouts/frontend/frontend.html.haml @@ -17,7 +17,7 @@ %style = @conference.try(:custom_css).html_safe - %body{:class => content_for(:body_class)} + %body{:class => content_for(:body_class), data: {mejs_controls_svg: asset_path('mejs-controls.svg'), mejs_skip_back_svg: asset_path('mejs-skip-back.svg'), mejs_jump_forward_svg: asset_path('mejs-jump-forward.svg')}} = render partial: 'frontend/shared/navbar' = yield diff --git a/app/views/layouts/frontend/oembed.html.haml b/app/views/layouts/frontend/oembed.html.haml index 4424624d..4c2ead1f 100644 --- a/app/views/layouts/frontend/oembed.html.haml +++ b/app/views/layouts/frontend/oembed.html.haml @@ -7,6 +7,6 @@ = javascript_include_tag 'oembed-player' = stylesheet_link_tag 'embed' - %body{style: 'height: 100%; overflow: hidden;'} + %body{style: 'height: 100%; overflow: hidden;', data: {mejs_controls_svg: asset_path('mejs-controls.svg'), mejs_skip_back_svg: asset_path('mejs-skip-back.svg'), mejs_jump_forward_svg: asset_path('mejs-jump-forward.svg')}} .player.video = render partial: 'frontend/shared/player_video', locals: { height: '450', width: '800', event: @event } diff --git a/config/application.rb b/config/application.rb index fcc3d8b3..9e5d66d8 100644 --- a/config/application.rb +++ b/config/application.rb @@ -33,5 +33,18 @@ class Application < Rails::Application config.app_generators.scaffold_controller = :scaffold_controller config.custom_css = nil + + # Extra asset roots (bare-filename resolution, matching the old Sprockets + # config.assets.paths setup). Must be set here, not in config/initializers, + # since Propshaft builds its asset assembly from a snapshot of this config + # before user initializers run. + config.assets.paths << Rails.root.join('app', 'assets', 'images', 'frontend') + config.assets.paths << Rails.root.join('app', 'assets', 'fonts') + config.assets.paths << Rails.root.join('vendor', 'assets', 'mediaelement') + # NB: cannot be named starting with "mediaelement" - Propshaft::LoadPath#dedup + # does a naive string prefix check and would silently drop it as a duplicate + # of the vendor/assets/mediaelement path registered above. + config.assets.paths << Rails.root.join('vendor', 'assets', 'player-plugins') + config.assets.paths << Rails.root.join('vendor', 'assets', 'icomoon-font') end end diff --git a/config/environments/development.rb b/config/environments/development.rb index a141bd66..388beec3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -79,11 +79,6 @@ # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. config.generators.apply_rubocop_autocorrect_after_generate! - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker diff --git a/config/environments/production.rb b/config/environments/production.rb index ae12f866..e12c8ff6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -23,18 +23,6 @@ # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.configure do |env| - env.js_compressor = :uglifier # or :closure, :yui - env.css_compressor = :sass # or :yui - end - - # Compress CSS using a preprocessor. - # config.assets.css_compressor = :sass - - # Do not fall back to assets pipeline if a precompiled asset is missed. - config.assets.compile = false - # Enable serving of images, stylesheets, and JavaScripts from an asset server. # config.asset_host = "http://assets.example.com" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index c6716539..00000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,28 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path -Rails.application.config.assets.paths << Rails.root.join('node_modules') -Rails.application.config.assets.paths << Rails.root.join('app', 'assets', 'images', 'frontend') -Rails.application.config.assets.paths << Rails.root.join("app", "assets", "fonts") -Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets') -Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'mediaelement') -Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'mediaelement-plugins') -Rails.application.config.assets.paths << Rails.root.join('vendor', 'assets', 'icomoon-font') - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) -Rails.application.config.assets.precompile = ["manifest.js"] - -Rails.application.config.assets.precompile << ["*.svg", "*.eot", "*.woff", "*.ttf", "*.otf"] # [/\.(?:eot|otf|svg|ttf|woff|swf|svg|gif|png)/] - -# for oembed layout -Rails.application.config.assets.precompile += %w[embed.css] - -# players -Rails.application.config.assets.precompile += %w[oembed-player] diff --git a/config/initializers/sass_skip.rb b/config/initializers/sass_skip.rb deleted file mode 100644 index 6c735e26..00000000 --- a/config/initializers/sass_skip.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "sprockets/sass_compressor" - -# https://stackoverflow.com/a/77544219 -module SkipSassCompression - SEARCH = "graphiql-react".freeze - - def call(input) - if skip_compression?(input[:data]) - input[:data] - else - super - end - end - - def skip_compression?(body) - body.include?(SEARCH) - end -end - -Sprockets::SassCompressor.prepend SkipSassCompression diff --git a/package.json b/package.json new file mode 100644 index 00000000..9064ce56 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "voctoweb-main", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "esbuild app/javascript/application.js app/javascript/oembed-player.js app/javascript/active_admin.js --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets", + "build:css": "sass ./app/assets/stylesheets/application.scss:./app/assets/builds/application.css ./app/assets/stylesheets/embed.scss:./app/assets/builds/embed.css ./app/assets/stylesheets/active_admin.scss:./app/assets/builds/active_admin.css --no-source-map --load-path=node_modules --load-path=vendor/assets/icomoon-font --load-path=vendor/assets/stylesheets --load-path=vendor/assets/mediaelement/plugins" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": "12.3.4", + "onFail": "download" + } + }, + "packageManager": "pnpm@12.3.4", + "type": "module", + "devDependencies": { + "esbuild": "^0.28.2", + "sass": "^1.104.0" + }, + "dependencies": { + "@activeadmin/activeadmin": "3.5.2", + "bootstrap-sass": "3.4.1", + "jquery": "1.12.4", + "jquery-ui": "^1.14.2", + "jquery-ujs": "1.2.3", + "turbolinks": "^5.2.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..43b8cec4 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,671 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.3.4 + version: 12.3.4 + +packages: + + '@pnpm/exe.darwin-arm64@12.3.4': + resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.3.4': + resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.3.4': + resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.3.4': + resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.3.4': + resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.3.4': + resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.3.4': + resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.3.4': + resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==} + cpu: [x64] + os: [win32] + + pnpm@12.3.4: + resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.3.4': + optional: true + + '@pnpm/exe.darwin-x64@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64@12.3.4': + optional: true + + '@pnpm/exe.linux-x64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-x64@12.3.4': + optional: true + + '@pnpm/exe.win32-arm64@12.3.4': + optional: true + + '@pnpm/exe.win32-x64@12.3.4': + optional: true + + pnpm@12.3.4: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.3.4 + '@pnpm/exe.darwin-x64': 12.3.4 + '@pnpm/exe.linux-arm64': 12.3.4 + '@pnpm/exe.linux-arm64-musl': 12.3.4 + '@pnpm/exe.linux-x64': 12.3.4 + '@pnpm/exe.linux-x64-musl': 12.3.4 + '@pnpm/exe.win32-arm64': 12.3.4 + '@pnpm/exe.win32-x64': 12.3.4 + +--- +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@activeadmin/activeadmin': + specifier: 3.5.2 + version: 3.5.2 + bootstrap-sass: + specifier: 3.4.1 + version: 3.4.1 + jquery: + specifier: 1.12.4 + version: 1.12.4 + jquery-ui: + specifier: ^1.14.2 + version: 1.14.2 + jquery-ujs: + specifier: 1.2.3 + version: 1.2.3(jquery@1.12.4) + turbolinks: + specifier: ^5.2.0 + version: 5.2.0 + devDependencies: + esbuild: + specifier: ^0.28.2 + version: 0.28.2 + sass: + specifier: ^1.104.0 + version: 1.104.0 + +packages: + + '@activeadmin/activeadmin@3.5.2': + resolution: {integrity: sha512-eVCOpdyeIo19Ctyqi9UxWoUxGkhyQcKb52hqY+FgVIgjAgpkhf4BvQO3HpRp/D58gCrJk5NyqIxgK2xwXl2HEw==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@parcel/watcher-android-arm64@2.6.0': + resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.6.0': + resolution: {integrity: sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.6.0': + resolution: {integrity: sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.6.0': + resolution: {integrity: sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.6.0': + resolution: {integrity: sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.6.0': + resolution: {integrity: sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.6.0': + resolution: {integrity: sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.6.0': + resolution: {integrity: sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.6.0': + resolution: {integrity: sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.6.0': + resolution: {integrity: sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.6.0': + resolution: {integrity: sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-x64@2.6.0': + resolution: {integrity: sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.6.0': + resolution: {integrity: sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==} + engines: {node: '>= 10.0.0'} + + bootstrap-sass@3.4.1: + resolution: {integrity: sha512-p5rxsK/IyEDQm2CwiHxxUi0MZZtvVFbhWmyMOt4lLkA4bujDA1TGoKT0i1FKIWiugAdP+kK8T5KMDFIKQCLYIA==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + jquery-ui@1.14.2: + resolution: {integrity: sha512-1gSl7PUjyipa2adSr780Ujk16faicrV7PjPPzPtvWk7tTqBnsqp67NNV9jZK2+BIxUPXWSnIUU/LBCgwgGZE+Q==} + + jquery-ujs@1.2.3: + resolution: {integrity: sha512-59wvfx5vcCTHMeQT1/OwFiAj+UffLIwjRIoXdpO7Z7BCFGepzq9T9oLVeoItjTqjoXfUrHJvV7QU6pUR+UzOoA==} + peerDependencies: + jquery: '>=1.8.0' + + jquery@1.12.4: + resolution: {integrity: sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg==} + deprecated: This version is deprecated. Please upgrade to the latest version or find support at https://www.herodevs.com/support/jquery-nes. + + jquery@4.0.0: + resolution: {integrity: sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + + sass@1.104.0: + resolution: {integrity: sha512-btHMApW2bgolvClhRW8AlQJzgI9lUB3pPSofBQQT+E46GWvf9o0TVQ13SYv5riWZVFyPN+JNz3TKW9XhBlc10w==} + engines: {node: '>=20.19.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + turbolinks@5.2.0: + resolution: {integrity: sha512-pMiez3tyBo6uRHFNNZoYMmrES/IaGgMhQQM+VFF36keryjb5ms0XkVpmKHkfW/4Vy96qiGW3K9bz0tF5sK9bBw==} + +snapshots: + + '@activeadmin/activeadmin@3.5.2': + dependencies: + jquery: 4.0.0 + jquery-ui: 1.14.2 + jquery-ujs: 1.2.3(jquery@4.0.0) + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@parcel/watcher-android-arm64@2.6.0': + optional: true + + '@parcel/watcher-darwin-arm64@2.6.0': + optional: true + + '@parcel/watcher-darwin-x64@2.6.0': + optional: true + + '@parcel/watcher-freebsd-x64@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-musl@2.6.0': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.6.0': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-x64-musl@2.6.0': + optional: true + + '@parcel/watcher-win32-arm64@2.6.0': + optional: true + + '@parcel/watcher-win32-x64@2.6.0': + optional: true + + '@parcel/watcher@2.6.0': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.7 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.6.0 + '@parcel/watcher-darwin-arm64': 2.6.0 + '@parcel/watcher-darwin-x64': 2.6.0 + '@parcel/watcher-freebsd-x64': 2.6.0 + '@parcel/watcher-linux-arm-glibc': 2.6.0 + '@parcel/watcher-linux-arm-musl': 2.6.0 + '@parcel/watcher-linux-arm64-glibc': 2.6.0 + '@parcel/watcher-linux-arm64-musl': 2.6.0 + '@parcel/watcher-linux-x64-glibc': 2.6.0 + '@parcel/watcher-linux-x64-musl': 2.6.0 + '@parcel/watcher-win32-arm64': 2.6.0 + '@parcel/watcher-win32-x64': 2.6.0 + optional: true + + bootstrap-sass@3.4.1: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + + detect-libc@2.1.2: + optional: true + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + immutable@5.1.9: {} + + is-extglob@2.1.1: + optional: true + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + optional: true + + jquery-ui@1.14.2: + dependencies: + jquery: 1.12.4 + + jquery-ujs@1.2.3(jquery@1.12.4): + dependencies: + jquery: 1.12.4 + + jquery-ujs@1.2.3(jquery@4.0.0): + dependencies: + jquery: 4.0.0 + + jquery@1.12.4: {} + + jquery@4.0.0: {} + + node-addon-api@7.1.1: + optional: true + + picomatch@4.0.7: + optional: true + + readdirp@5.1.1: {} + + sass@1.104.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.9 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.6.0 + + source-map-js@1.2.1: {} + + turbolinks@5.2.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..24a72cab --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + '@parcel/watcher': true + esbuild: true diff --git a/vendor/assets/icomoon-font/icomoon-font.scss b/vendor/assets/icomoon-font/icomoon-font.scss index 5cf3c155..5c9d3b6b 100644 --- a/vendor/assets/icomoon-font/icomoon-font.scss +++ b/vendor/assets/icomoon-font/icomoon-font.scss @@ -1,10 +1,10 @@ @font-face { font-family: 'icomoon'; - src: font-url('fonts/icomoon.eot?7wpnun'); - src: font-url('fonts/icomoon.eot?7wpnun#iefix') format('embedded-opentype'), - font-url('fonts/icomoon.ttf?7wpnun') format('truetype'), - font-url('fonts/icomoon.woff?7wpnun') format('woff'), - font-url('fonts/icomoon.svg?7wpnun#icomoon') format('svg'); + src: url('fonts/icomoon.eot?7wpnun'); + src: url('fonts/icomoon.eot?7wpnun#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?7wpnun') format('truetype'), + url('fonts/icomoon.woff?7wpnun') format('woff'), + url('fonts/icomoon.svg?7wpnun#icomoon') format('svg'); font-weight: normal; font-style: normal; font-display: block; diff --git a/vendor/assets/mediaelement-plugins/jump-forward/jump-forward-i18n.js b/vendor/assets/mediaelement/plugins/jump-forward/jump-forward-i18n.js similarity index 100% rename from vendor/assets/mediaelement-plugins/jump-forward/jump-forward-i18n.js rename to vendor/assets/mediaelement/plugins/jump-forward/jump-forward-i18n.js diff --git a/vendor/assets/mediaelement/plugins/jump-forward/jump-forward.js b/vendor/assets/mediaelement/plugins/jump-forward/jump-forward.js new file mode 100644 index 00000000..a206eb1c --- /dev/null +++ b/vendor/assets/mediaelement/plugins/jump-forward/jump-forward.js @@ -0,0 +1,47 @@ +/*! + * MediaElement.js + * http://www.mediaelementjs.com/ + * + * Wrapper that mimics native HTML5 MediaElement (audio and video) + * using a variety of technologies (pure JavaScript, Flash, iframe) + * + * Copyright 2010-2017, John Dyer (http://j.hn/) + * License: MIT + * + */(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i'; + } + + currentItem.innerHTML += '

' + (player.options.currentMessage || '') + ' ' + player.playlist[player.currentPlaylistItem].title + ''; + if (typeof player.playlist[player.currentPlaylistItem].description !== 'undefined') { + currentItem.innerHTML += ' - ' + player.playlist[player.currentPlaylistItem].description + ''; + } + currentItem.innerHTML += '

'; + player.resetSize(); + }; + currentItem.className = player.options.classPrefix + 'playlist-current ' + player.options.classPrefix + 'layer'; + audioCallback(); + layers.insertBefore(currentItem, layers.firstChild); + media.addEventListener('play', audioCallback); + } + + if (player.options.showPlaylist) { + player.playlistLayer = document.createElement('div'); + player.playlistLayer.className = player.options.classPrefix + 'playlist-layer ' + player.options.classPrefix + 'layer ' + (player.isVideo ? player.options.classPrefix + 'playlist-hidden' : '') + ' ' + player.options.classPrefix + 'playlist-selector'; + player.playlistLayer.innerHTML = '
    '; + layers.insertBefore(player.playlistLayer, layers.firstChild); + + for (var i = 0, total = player.listItems.length; i < total; i++) { + player.playlistLayer.querySelector('ul').innerHTML += player.listItems[i]; + } + + if (player.isVideo) { + player.playlistButton = document.createElement('div'); + player.playlistButton.className = player.options.classPrefix + 'button ' + player.options.classPrefix + 'playlist-button'; + player.playlistButton.innerHTML = ''; + player.playlistButton.addEventListener('click', function () { + mejs.Utils.toggleClass(player.playlistLayer, player.options.classPrefix + 'playlist-hidden'); + }); + player.addControlElement(player.playlistButton, 'playlist'); + } else { + var _items = player.playlistLayer.querySelectorAll('li'); + + if (_items.length <= 10) { + var height = 0; + for (var _i = 0, _total = _items.length; _i < _total; _i++) { + height += _items[_i].offsetHeight; + } + player.container.style.height = height + 'px'; + } + } + + var items = player.playlistLayer.querySelectorAll('.' + player.options.classPrefix + 'playlist-selector-list-item'), + inputs = player.playlistLayer.querySelectorAll('input[type=radio]'); + + for (var _i2 = 0, _total2 = inputs.length; _i2 < _total2; _i2++) { + inputs[_i2].disabled = false; + inputs[_i2].addEventListener('click', function () { + var radios = player.playlistLayer.querySelectorAll('input[type="radio"]'), + selected = player.playlistLayer.querySelectorAll('.' + player.options.classPrefix + 'playlist-selected'); + + for (var j = 0, total2 = radios.length; j < total2; j++) { + radios[j].checked = false; + } + for (var _j = 0, _total3 = selected.length; _j < _total3; _j++) { + mejs.Utils.removeClass(selected[_j], player.options.classPrefix + 'playlist-selected'); + selected[_j].querySelector('label').querySelector('span').remove(); + } + + this.checked = true; + this.closest('.' + player.options.classPrefix + 'playlist-selector-list-item').querySelector('label').innerHTML = '\u25B6 ' + this.closest('.' + player.options.classPrefix + 'playlist-selector-list-item').querySelector('label').innerHTML; + mejs.Utils.addClass(this.closest('.' + player.options.classPrefix + 'playlist-selector-list-item'), player.options.classPrefix + 'playlist-selected'); + player.currentPlaylistItem = this.getAttribute('data-playlist-index'); + player.setSrc(this.value); + player.load(); + player.play(); + + if (player.isVideo && player.options.autoClosePlaylist === true) { + mejs.Utils.toggleClass(player.playlistLayer, player.options.classPrefix + 'playlist-hidden'); + } + }); + } + + for (var _i3 = 0, _total4 = items.length; _i3 < _total4; _i3++) { + items[_i3].addEventListener('click', function () { + var radio = mejs.Utils.siblings(this.querySelector('.' + player.options.classPrefix + 'playlist-selector-label'), function (el) { + return el.tagName === 'INPUT'; + })[0], + event = mejs.Utils.createEvent('click', radio); + radio.dispatchEvent(event); + }); + } + + player.keydownCallback = function (e) { + var event = mejs.Utils.createEvent('click', e.target); + e.target.dispatchEvent(event); + return false; + }; + + player.playlistLayer.addEventListener('keydown', function (e) { + var keyCode = e.which || e.keyCode || 0; + if (~[13, 32, 38, 40].indexOf(keyCode)) { + player.keydownCallback(e); + } + }); + } else { + mejs.Utils.addClass(player.container, player.options.classPrefix + 'no-playlist'); + } + }, + cleanplaylist: function cleanplaylist(player, controls, layers, media) { + media.removeEventListener('ended', player.endedCallback); + }, + buildprevtrack: function buildprevtrack(player) { + + var defaultPrevTitle = mejs.i18n.t('mejs.playlist-prev'), + prevTitle = mejs.Utils.isString(player.options.prevText) ? player.options.prevText : defaultPrevTitle; + player.prevButton = document.createElement('div'); + player.prevButton.className = player.options.classPrefix + 'button ' + player.options.classPrefix + 'prev-button'; + player.prevButton.innerHTML = ''; + + player.prevPlaylistCallback = function () { + if (player.playlist[--player.currentPlaylistItem]) { + player.setSrc(player.playlist[player.currentPlaylistItem].src); + player.load(); + player.play(); + } else { + ++player.currentPlaylistItem; + } + }; + + player.prevButton.addEventListener('click', player.prevPlaylistCallback); + player.addControlElement(player.prevButton, 'prevtrack'); + }, + cleanprevtrack: function cleanprevtrack(player) { + player.prevButton.removeEventListener('click', player.prevPlaylistCallback); + }, + buildnexttrack: function buildnexttrack(player) { + var defaultNextTitle = mejs.i18n.t('mejs.playlist-next'), + nextTitle = mejs.Utils.isString(player.options.nextText) ? player.options.nextText : defaultNextTitle; + player.nextButton = document.createElement('div'); + player.nextButton.className = player.options.classPrefix + 'button ' + player.options.classPrefix + 'next-button'; + player.nextButton.innerHTML = ''; + + player.nextPlaylistCallback = function () { + if (player.playlist[++player.currentPlaylistItem]) { + player.setSrc(player.playlist[player.currentPlaylistItem].src); + player.load(); + player.play(); + } else { + --player.currentPlaylistItem; + } + }; + + player.nextButton.addEventListener('click', player.nextPlaylistCallback); + player.addControlElement(player.nextButton, 'nexttrack'); + }, + cleannexttrack: function cleannexttrack(player) { + player.nextButton.removeEventListener('click', player.nextPlaylistCallback); + }, + buildloop: function buildloop(player) { + var defaultLoopTitle = mejs.i18n.t('mejs.playlist-loop'), + loopTitle = mejs.Utils.isString(player.options.loopText) ? player.options.loopText : defaultLoopTitle; + + player.loopButton = document.createElement('div'); + player.loopButton.className = player.options.classPrefix + 'button ' + player.options.classPrefix + 'loop-button ' + (player.options.loop ? player.options.classPrefix + 'loop-on' : player.options.classPrefix + 'loop-off'); + player.loopButton.innerHTML = ''; + player.loopCallback = function () { + player.options.loop = !player.options.loop; + if (player.options.loop) { + mejs.Utils.removeClass(player.loopButton, player.options.classPrefix + 'loop-off'); + mejs.Utils.addClass(player.loopButton, player.options.classPrefix + 'loop-on'); + } else { + mejs.Utils.removeClass(player.loopButton, player.options.classPrefix + 'loop-on'); + mejs.Utils.addClass(player.loopButton, player.options.classPrefix + 'loop-off'); + } + }; + + player.loopButton.addEventListener('click', player.loopCallback); + player.addControlElement(player.loopButton, 'loop'); + }, + cleanloop: function cleanloop(player) { + player.loopButton.removeEventListener('click', player.loopCallback); + }, + buildshuffle: function buildshuffle(player) { + var defaultShuffleTitle = mejs.i18n.t('mejs.playlist-shuffle'), + shuffleTitle = mejs.Utils.isString(player.options.shuffleText) ? player.options.shuffleText : defaultShuffleTitle; + player.shuffleButton = document.createElement('div'); + player.shuffleButton.className = player.options.classPrefix + 'button ' + player.options.classPrefix + 'shuffle-button ' + player.options.classPrefix + 'shuffle-off'; + player.shuffleButton.innerHTML = ''; + player.shuffleButton.style.display = 'none'; + player.media.addEventListener('play', function () { + player.shuffleButton.style.display = ''; + player.resetSize(); + }); + + var enabled = false, + playedItems = []; + var randomizeCallback = function randomizeCallback() { + if (!player.options.loop) { + var randomItem = Math.floor(Math.random() * player.playlist.length); + if (playedItems.indexOf(randomItem) === -1) { + player.setSrc(player.playlist[randomItem].src); + player.load(); + player.play(); + player.currentPlaylistItem = randomItem; + playedItems.push(randomItem); + } else if (playedItems.length < player.playlist.length) { + player.shuffleCallback(); + } else if (playedItems.length < player.playlist.length) { + playedItems = []; + player.currentPlaylistItem = randomItem; + playedItems.push(randomItem); + } + } + }; + + player.shuffleCallback = function () { + if (!enabled) { + mejs.Utils.removeClass(player.shuffleButton, player.options.classPrefix + 'shuffle-off'); + mejs.Utils.addClass(player.shuffleButton, player.options.classPrefix + 'shuffle-on'); + enabled = true; + player.media.addEventListener('ended', randomizeCallback); + } else { + mejs.Utils.removeClass(player.shuffleButton, player.options.classPrefix + 'shuffle-on'); + mejs.Utils.addClass(player.shuffleButton, player.options.classPrefix + 'shuffle-off'); + enabled = false; + player.media.removeEventListener('ended', randomizeCallback); + } + }; + + player.shuffleButton.addEventListener('click', player.shuffleCallback); + player.addControlElement(player.shuffleButton, 'shuffle'); + }, + cleanshuffle: function cleanshuffle(player) { + player.shuffleButton.removeEventListener('click', player.shuffleCallback); + }, + createPlayList_: function createPlayList_() { + var t = this; + + t.playlist = t.options.playlist.length ? t.options.playlist : t.mediaFiles && t.mediaFiles.length ? t.mediaFiles : []; + + if (!t.playlist.length) { + var children = t.media.originalNode.children; + + for (var i = 0, total = children.length; i < total; i++) { + var childNode = children[i]; + + if (childNode.tagName.toLowerCase() === 'source') { + (function () { + var elements = {}; + Array.prototype.slice.call(childNode.attributes).forEach(function (item) { + elements[item.name] = item.value; + }); + + if (elements.src && elements.type && elements.title) { + elements.type = mejs.Utils.formatType(elements.src, elements.type); + t.playlist.push(elements); + } + })(); + } + } + } + + if (t.playlist.length < 2) { + return; + } + + t.listItems = []; + for (var _i4 = 0, _total5 = t.playlist.length; _i4 < _total5; _i4++) { + var element = t.playlist[_i4], + item = document.createElement('li'), + id = t.id + '_playlist_item_' + _i4, + thumbnail = element['data-playlist-thumbnail'] ? '
    ' : '', + description = element['data-playlist-description'] ? '
    ' + element['data-playlist-description'] + '
    ' : ''; + item.tabIndex = 0; + item.className = t.options.classPrefix + 'playlist-selector-list-item' + (_i4 === 0 ? ' ' + t.options.classPrefix + 'playlist-selected' : ''); + item.innerHTML = '
    ' + ('' + thumbnail) + ('
    ') + ('
    ') + ('
    ' + description + '
    '); + + t.listItems.push(item.outerHTML); + } + } +}); + +},{}]},{},[1]); diff --git a/vendor/assets/mediaelement-plugins/playlist/playlist.scss b/vendor/assets/mediaelement/plugins/playlist/playlist.scss similarity index 98% rename from vendor/assets/mediaelement-plugins/playlist/playlist.scss rename to vendor/assets/mediaelement/plugins/playlist/playlist.scss index 7319595a..804cb0e0 100644 --- a/vendor/assets/mediaelement-plugins/playlist/playlist.scss +++ b/vendor/assets/mediaelement/plugins/playlist/playlist.scss @@ -24,7 +24,7 @@ .mejs-loop-button > button, .mejs__shuffle-button > button, .mejs-shuffle-button > button { - background: asset-url('playlist/playlist-controls.svg') transparent no-repeat; + background: url('playlist/playlist-controls.svg') transparent no-repeat; } .mejs__playlist-button > button, .mejs-playlist-button > button { diff --git a/vendor/assets/mediaelement-plugins/postroll/postroll-i18n.js b/vendor/assets/mediaelement/plugins/postroll/postroll-i18n.js similarity index 100% rename from vendor/assets/mediaelement-plugins/postroll/postroll-i18n.js rename to vendor/assets/mediaelement/plugins/postroll/postroll-i18n.js diff --git a/vendor/assets/mediaelement-plugins/postroll/postroll.css b/vendor/assets/mediaelement/plugins/postroll/postroll.css similarity index 100% rename from vendor/assets/mediaelement-plugins/postroll/postroll.css rename to vendor/assets/mediaelement/plugins/postroll/postroll.css diff --git a/vendor/assets/mediaelement/plugins/postroll/postroll.js b/vendor/assets/mediaelement/plugins/postroll/postroll.js new file mode 100644 index 00000000..7d8a579a --- /dev/null +++ b/vendor/assets/mediaelement/plugins/postroll/postroll.js @@ -0,0 +1,58 @@ +/*! + * MediaElement.js + * http://www.mediaelementjs.com/ + * + * Wrapper that mimics native HTML5 MediaElement (audio and video) + * using a variety of technologies (pure JavaScript, Flash, iframe) + * + * Copyright 2010-2017, John Dyer (http://j.hn/) + * License: MIT + * + */(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i' + postrollTitle + '' + ('
    '); + player.postroll.style.display = 'none'; + + layers.insertBefore(player.postroll, layers.firstChild); + + player.postroll.querySelector('.' + t.options.classPrefix + 'postroll-close').addEventListener('click', function (e) { + this.parentNode.style.display = 'none'; + e.preventDefault(); + e.stopPropagation(); + }); + + t.media.addEventListener('ended', function () { + mejs.Utils.ajax(postrollLink.getAttribute('href'), 'html', function (data) { + layers.querySelector('.' + t.options.classPrefix + 'postroll-layer-content').innerHTML = data; + }); + player.postroll.style.display = 'block'; + }, false); + + t.media.addEventListener('seeked', function () { + player.postroll.style.display = 'none'; + }, false); + + t.media.addEventListener('playing', function () { + player.postroll.style.display = 'none'; + }, false); + } + } +}); + +},{}]},{},[1]); diff --git a/vendor/assets/mediaelement-plugins/skip-back/skip-back-i18n.js b/vendor/assets/mediaelement/plugins/skip-back/skip-back-i18n.js similarity index 100% rename from vendor/assets/mediaelement-plugins/skip-back/skip-back-i18n.js rename to vendor/assets/mediaelement/plugins/skip-back/skip-back-i18n.js diff --git a/vendor/assets/mediaelement/plugins/skip-back/skip-back.js b/vendor/assets/mediaelement/plugins/skip-back/skip-back.js new file mode 100644 index 00000000..ebbae626 --- /dev/null +++ b/vendor/assets/mediaelement/plugins/skip-back/skip-back.js @@ -0,0 +1,46 @@ +/*! + * MediaElement.js + * http://www.mediaelementjs.com/ + * + * Wrapper that mimics native HTML5 MediaElement (audio and video) + * using a variety of technologies (pure JavaScript, Flash, iframe) + * + * Copyright 2010-2017, John Dyer (http://j.hn/) + * License: MIT + * + */(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i' + (''); + + t.addControlElement(player.sourcechooserButton, 'sourcechooser'); + + for (var _i = 0, _total = sources.length; _i < _total; _i++) { + var src = sources[_i]; + if (src.type !== undefined && typeof media.canPlayType === 'function') { + player.addSourceButton(src.src, src.title, src.type, media.src === src.src); + } + } + + player.sourcechooserButton.addEventListener('mouseover', function () { + clearTimeout(hoverTimeout); + player.showSourcechooserSelector(); + }); + player.sourcechooserButton.addEventListener('mouseout', function () { + hoverTimeout = setTimeout(function () { + player.hideSourcechooserSelector(); + }, 0); + }); + + player.sourcechooserButton.addEventListener('keydown', function (e) { + + if (t.options.keyActions.length) { + var keyCode = e.which || e.keyCode || 0; + + switch (keyCode) { + case 32: + if (!mejs.MediaFeatures.isFirefox) { + player.showSourcechooserSelector(); + } + player.sourcechooserButton.querySelector('input[type=radio]:checked').focus(); + break; + case 13: + player.showSourcechooserSelector(); + player.sourcechooserButton.querySelector('input[type=radio]:checked').focus(); + break; + case 27: + player.hideSourcechooserSelector(); + player.sourcechooserButton.querySelector('button').focus(); + break; + default: + return true; + } + + e.preventDefault(); + e.stopPropagation(); + } + }); + + player.sourcechooserButton.addEventListener('focusout', mejs.Utils.debounce(function () { + setTimeout(function () { + var parent = document.activeElement.closest('.' + t.options.classPrefix + 'sourcechooser-selector'); + if (!parent) { + player.hideSourcechooserSelector(); + } + }, 0); + }, 100)); + + var radios = player.sourcechooserButton.querySelectorAll('input[type=radio]'); + + for (var _i2 = 0, _total2 = radios.length; _i2 < _total2; _i2++) { + radios[_i2].addEventListener('click', function () { + this.setAttribute('aria-selected', true); + this.checked = true; + + var otherRadios = this.closest('.' + t.options.classPrefix + 'sourcechooser-selector').querySelectorAll('input[type=radio]'); + + for (var j = 0, radioTotal = otherRadios.length; j < radioTotal; j++) { + if (otherRadios[j] !== this) { + otherRadios[j].setAttribute('aria-selected', 'false'); + otherRadios[j].removeAttribute('checked'); + } + } + + var src = this.value; + + if (media.getSrc() !== src) { + var currentTime = media.currentTime; + + var paused = media.paused, + canPlayAfterSourceSwitchHandler = function canPlayAfterSourceSwitchHandler() { + if (!paused) { + media.setCurrentTime(currentTime); + media.play(); + } + media.removeEventListener('canplay', canPlayAfterSourceSwitchHandler); + }; + + media.pause(); + media.setSrc(src); + media.load(); + media.addEventListener('canplay', canPlayAfterSourceSwitchHandler); + } + }); + } + + player.sourcechooserButton.querySelector('button').addEventListener('click', function () { + if (mejs.Utils.hasClass(mejs.Utils.siblings(this, '.' + t.options.classPrefix + 'sourcechooser-selector'), t.options.classPrefix + 'offscreen')) { + player.showSourcechooserSelector(); + player.sourcechooserButton.querySelector('input[type=radio]:checked').focus(); + } else { + player.hideSourcechooserSelector(); + } + }); + }, + addSourceButton: function addSourceButton(src, label, type, isCurrent) { + var t = this; + if (label === '' || label === undefined) { + label = src; + } + type = type.split('/')[1]; + + t.sourcechooserButton.querySelector('ul').innerHTML += '
  • ' + ('') + ('') + '
  • '; + + t.adjustSourcechooserBox(); + }, + adjustSourcechooserBox: function adjustSourcechooserBox() { + var t = this; + + t.sourcechooserButton.querySelector('.' + t.options.classPrefix + 'sourcechooser-selector').style.height = parseFloat(t.sourcechooserButton.querySelector('.' + t.options.classPrefix + 'sourcechooser-selector ul').offsetHeight) + 'px'; + }, + hideSourcechooserSelector: function hideSourcechooserSelector() { + + var t = this; + + if (t.sourcechooserButton === undefined || !t.sourcechooserButton.querySelector('input[type=radio]')) { + return; + } + + var selector = t.sourcechooserButton.querySelector('.' + t.options.classPrefix + 'sourcechooser-selector'), + radios = selector.querySelectorAll('input[type=radio]'); + selector.setAttribute('aria-expanded', 'false'); + selector.setAttribute('aria-hidden', 'true'); + mejs.Utils.addClass(selector, t.options.classPrefix + 'offscreen'); + + for (var i = 0, total = radios.length; i < total; i++) { + radios[i].setAttribute('tabindex', '-1'); + } + }, + showSourcechooserSelector: function showSourcechooserSelector() { + + var t = this; + + if (t.sourcechooserButton === undefined || !t.sourcechooserButton.querySelector('input[type=radio]')) { + return; + } + + var selector = t.sourcechooserButton.querySelector('.' + t.options.classPrefix + 'sourcechooser-selector'), + radios = selector.querySelectorAll('input[type=radio]'); + selector.setAttribute('aria-expanded', 'true'); + selector.setAttribute('aria-hidden', 'false'); + mejs.Utils.removeClass(selector, t.options.classPrefix + 'offscreen'); + + for (var i = 0, total = radios.length; i < total; i++) { + radios[i].setAttribute('tabindex', '0'); + } + } +}); + +},{}]},{},[1]); diff --git a/vendor/assets/mediaelement-plugins/source-chooser/source-chooser.scss b/vendor/assets/mediaelement/plugins/source-chooser/source-chooser.scss similarity index 95% rename from vendor/assets/mediaelement-plugins/source-chooser/source-chooser.scss rename to vendor/assets/mediaelement/plugins/source-chooser/source-chooser.scss index 139d7746..4c8b4a61 100644 --- a/vendor/assets/mediaelement-plugins/source-chooser/source-chooser.scss +++ b/vendor/assets/mediaelement/plugins/source-chooser/source-chooser.scss @@ -5,7 +5,7 @@ .mejs__sourcechooser-button > button, .mejs-sourcechooser-button > button { - background: asset-url('source-chooser/settings.svg') transparent no-repeat; + background: url('source-chooser/settings.svg') transparent no-repeat; background-position: 0 1px; } diff --git a/vendor/assets/mediaelement-plugins/speed/speed-i18n.js b/vendor/assets/mediaelement/plugins/speed/speed-i18n.js similarity index 100% rename from vendor/assets/mediaelement-plugins/speed/speed-i18n.js rename to vendor/assets/mediaelement/plugins/speed/speed-i18n.js diff --git a/vendor/assets/mediaelement-plugins/speed/speed.css b/vendor/assets/mediaelement/plugins/speed/speed.css similarity index 100% rename from vendor/assets/mediaelement-plugins/speed/speed.css rename to vendor/assets/mediaelement/plugins/speed/speed.css diff --git a/vendor/assets/mediaelement/plugins/speed/speed.js b/vendor/assets/mediaelement/plugins/speed/speed.js new file mode 100644 index 00000000..c3070e0f --- /dev/null +++ b/vendor/assets/mediaelement/plugins/speed/speed.js @@ -0,0 +1,206 @@ +/*! + * MediaElement.js + * http://www.mediaelementjs.com/ + * + * Wrapper that mimics native HTML5 MediaElement (audio and video) + * using a variety of technologies (pure JavaScript, Flash, iframe) + * + * Copyright 2010-2017, John Dyer (http://j.hn/) + * License: MIT + * + */(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i' + getSpeedNameFromValue(t.options.defaultSpeed) + '') + ('
    ') + ('
      ') + '
      '; + + t.addControlElement(player.speedButton, 'speed'); + + for (var _i = 0, _total = speeds.length; _i < _total; _i++) { + + var inputId = t.id + '-speed-' + speeds[_i].value; + + player.speedButton.querySelector('ul').innerHTML += '
    • ' + ('') + ('') + '
    • '; + } + + playbackSpeed = t.options.defaultSpeed; + + player.speedSelector = player.speedButton.querySelector('.' + t.options.classPrefix + 'speed-selector'); + + var inEvents = ['mouseenter', 'focusin'], + outEvents = ['mouseleave', 'focusout'], + radios = player.speedButton.querySelectorAll('input[type="radio"]'), + labels = player.speedButton.querySelectorAll('.' + t.options.classPrefix + 'speed-selector-label'); + + player.speedRadioButtons = radios; + + for (var _i2 = 0, _total2 = inEvents.length; _i2 < _total2; _i2++) { + player.speedButton.addEventListener(inEvents[_i2], function () { + mejs.Utils.removeClass(player.speedSelector, t.options.classPrefix + 'offscreen'); + player.speedSelector.style.height = player.speedSelector.querySelector('ul').offsetHeight; + player.speedSelector.style.top = -1 * parseFloat(player.speedSelector.offsetHeight) + 'px'; + }); + } + + for (var _i3 = 0, _total3 = outEvents.length; _i3 < _total3; _i3++) { + player.speedSelector.addEventListener(outEvents[_i3], function () { + mejs.Utils.addClass(this, t.options.classPrefix + 'offscreen'); + }); + } + + for (var _i4 = 0, _total4 = radios.length; _i4 < _total4; _i4++) { + var radio = radios[_i4]; + radio.disabled = false; + radio.addEventListener('click', function () { + var self = this, + newSpeed = self.value; + + playbackSpeed = newSpeed; + media.playbackRate = parseFloat(newSpeed); + player.speedButton.querySelector('button').innerHTML = getSpeedNameFromValue(newSpeed); + var selected = player.speedButton.querySelectorAll('.' + t.options.classPrefix + 'speed-selected'); + for (var _i5 = 0, _total5 = selected.length; _i5 < _total5; _i5++) { + mejs.Utils.removeClass(selected[_i5], t.options.classPrefix + 'speed-selected'); + } + + self.checked = true; + var siblings = mejs.Utils.siblings(self, function (el) { + return mejs.Utils.hasClass(el, t.options.classPrefix + 'speed-selector-label'); + }); + for (var j = 0, _total6 = siblings.length; j < _total6; j++) { + mejs.Utils.addClass(siblings[j], t.options.classPrefix + 'speed-selected'); + } + }); + } + + for (var _i6 = 0, _total7 = labels.length; _i6 < _total7; _i6++) { + labels[_i6].addEventListener('click', function () { + var radio = mejs.Utils.siblings(this, function (el) { + return el.tagName === 'INPUT'; + })[0], + event = mejs.Utils.createEvent('click', radio); + radio.dispatchEvent(event); + }); + } + + t.options.keyActions.push({ + keys: [60, 188], + action: function action(player, media, key, event) { + if (event.key != '<') return; + + var _radios = player.speedRadioButtons; + for (var _i7 = 0; _i7 < _radios.length - 1; _i7++) { + if (_radios[_i7].checked) { + var nextRadio = _radios[_i7 + 1]; + nextRadio.dispatchEvent(mejs.Utils.createEvent('click', nextRadio)); + break; + } + } + } + }, { + keys: [62, 190], + action: function action(player, media, key, event) { + if (event.key != '>') return; + + var _radios = player.speedRadioButtons; + for (var _i8 = 1; _i8 < _radios.length; _i8++) { + if (_radios[_i8].checked) { + var prevRadio = _radios[_i8 - 1]; + prevRadio.dispatchEvent(mejs.Utils.createEvent('click', prevRadio)); + break; + } + } + } + }); + + player.speedSelector.addEventListener('keydown', function (e) { + e.stopPropagation(); + }); + + media.addEventListener('loadedmetadata', function () { + if (playbackSpeed) { + media.playbackRate = parseFloat(playbackSpeed); + } + }); + }, + cleanspeed: function cleanspeed(player) { + if (player) { + if (player.speedButton) { + player.speedButton.parentNode.removeChild(player.speedButton); + } + if (player.speedSelector) { + player.speedSelector.parentNode.removeChild(player.speedSelector); + } + } + } +}); + +},{}]},{},[1]); From adffd55cc3be01c30e18d0c18ce830987bf0e3fc Mon Sep 17 00:00:00 2001 From: Andi Date: Sun, 6 Sep 2026 20:27:21 +0200 Subject: [PATCH 31/46] fix: repair JS globals broken by the Sprockets->esbuild bundling switch Two distinct bug classes surfaced after the asset-pipeline migration, both because esbuild gives each file its own module scope instead of Sprockets' old single-concatenated-script model: 1. Bare top-level `var`/`function` declarations used to leak onto `window` for free under Sprockets concatenation. Under esbuild they stay private to their module, breaking anything that referenced them as a global: - mirrorbrain-fix.js: `var MirrorbrainFix = {...}` -> `window.MirrorbrainFix` - vendor/timelens.js: bare `function timelens()` was never exposed alongside the `Timelens` class - broke every timelens widget - theme.js: `toggleTheme` is called from onclick="" in _navbar.haml, which runs in true global scope, not the bundle's module closure - mastodon-share.js: `mastodonShare` is called from a separate inline