From 5e60bfe2b30523ede07f41e45cdf0b469cbf86b6 Mon Sep 17 00:00:00 2001 From: cld9x <> Date: Thu, 9 May 2019 20:49:58 +0200 Subject: [PATCH] Initial commit --- .assets.toml | 19 + .drone.yml | 37 + .gitignore | 12 + .goreleaser.yml | 66 + .realize.yaml | 32 + command/common.go | 19 + command/log.go | 23 + command/run.go | 18 + command/task.go | 42 + command/volume.go | 78 ++ dms/dlna/dlna.go | 102 ++ dms/dlna/dlna_test.go | 16 + dms/dlna/dms/cd-service-desc.go | 451 ++++++ dms/dlna/dms/cds.go | 665 +++++++++ dms/dlna/dms/cds_test.go | 28 + dms/dlna/dms/cm-service-desc.go | 275 ++++ dms/dlna/dms/dms.go | 934 +++++++++++++ dms/dlna/dms/dms_others.go | 11 + dms/dlna/dms/dms_test.go | 66 + dms/dlna/dms/dms_unix.go | 25 + dms/dlna/dms/dms_unix_test.go | 22 + dms/dlna/dms/dms_windows.go | 35 + dms/dlna/dms/ffmpeg.go | 31 + dms/dlna/dms/html.go | 21 + dms/dlna/dms/mimetype.go | 90 ++ dms/dlna/dms/xbase_link.go | 95 ++ dms/main.go | 265 ++++ dms/rrcache/rrcache.go | 79 ++ dms/soap/soap.go | 68 + dms/ssdp/ssdp.go | 330 +++++ dms/transcode/transcode.go | 154 +++ dms/upnp/eventing.go | 91 ++ dms/upnp/eventing_test.go | 42 + dms/upnp/upnp.go | 159 +++ dms/upnpav/upnpav.go | 45 + go.mod | 103 ++ main.go | 35 + ui/.gitignore | 3 + ui/package.json | 30 + ui/public/index.html | 18 + ui/public/static/bulma.min.css | 1 + ui/public/static/video-js.css | 1386 +++++++++++++++++++ ui/public/static/videojs-vr.css | 1 + ui/rollup.config.js | 44 + ui/src/App.svelte | 13 + ui/src/Navbar.svelte | 34 + ui/src/main.js | 7 + ui/src/scenes/Filters.svelte | 141 ++ ui/src/scenes/SceneList.svelte | 125 ++ ui/src/scenes/Video.svelte | 46 + ui/src/scenes/index.svelte | 20 + ui/src/store/filters.js | 10 + ui/yarn.lock | 2293 +++++++++++++++++++++++++++++++ xbase/api_dms.go | 145 ++ xbase/api_extension.go | 202 +++ xbase/api_scenes.go | 223 +++ xbase/api_tasks.go | 27 + xbase/bindata.go | 260 ++++ xbase/db.go | 65 + xbase/dms.go | 214 +++ xbase/log.go | 30 + xbase/model_actor.go | 15 + xbase/model_file.go | 46 + xbase/model_image.go | 39 + xbase/model_scene.go | 154 +++ xbase/model_tag.go | 272 ++++ xbase/model_volume.go | 244 ++++ xbase/paths.go | 24 + xbase/scrape/badoink.go | 172 +++ xbase/scrape/milfvr.go | 130 ++ xbase/scrape/navr.go | 153 +++ xbase/scrape/scrape.go | 31 + xbase/scrape/virtualtaboo.go | 129 ++ xbase/scrape/vrbangers.go | 138 ++ xbase/scrape/wankzvr.go | 126 ++ xbase/server.go | 82 ++ xbase/task_content.go | 126 ++ xbase/task_volume.go | 15 + 78 files changed, 11818 insertions(+) create mode 100644 .assets.toml create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 .goreleaser.yml create mode 100755 .realize.yaml create mode 100644 command/common.go create mode 100644 command/log.go create mode 100644 command/run.go create mode 100644 command/task.go create mode 100644 command/volume.go create mode 100644 dms/dlna/dlna.go create mode 100644 dms/dlna/dlna_test.go create mode 100644 dms/dlna/dms/cd-service-desc.go create mode 100644 dms/dlna/dms/cds.go create mode 100644 dms/dlna/dms/cds_test.go create mode 100644 dms/dlna/dms/cm-service-desc.go create mode 100644 dms/dlna/dms/dms.go create mode 100644 dms/dlna/dms/dms_others.go create mode 100644 dms/dlna/dms/dms_test.go create mode 100644 dms/dlna/dms/dms_unix.go create mode 100644 dms/dlna/dms/dms_unix_test.go create mode 100644 dms/dlna/dms/dms_windows.go create mode 100644 dms/dlna/dms/ffmpeg.go create mode 100644 dms/dlna/dms/html.go create mode 100644 dms/dlna/dms/mimetype.go create mode 100644 dms/dlna/dms/xbase_link.go create mode 100644 dms/main.go create mode 100644 dms/rrcache/rrcache.go create mode 100644 dms/soap/soap.go create mode 100644 dms/ssdp/ssdp.go create mode 100644 dms/transcode/transcode.go create mode 100644 dms/upnp/eventing.go create mode 100644 dms/upnp/eventing_test.go create mode 100644 dms/upnp/upnp.go create mode 100644 dms/upnpav/upnpav.go create mode 100644 go.mod create mode 100644 main.go create mode 100644 ui/.gitignore create mode 100644 ui/package.json create mode 100644 ui/public/index.html create mode 100644 ui/public/static/bulma.min.css create mode 100644 ui/public/static/video-js.css create mode 100644 ui/public/static/videojs-vr.css create mode 100644 ui/rollup.config.js create mode 100644 ui/src/App.svelte create mode 100644 ui/src/Navbar.svelte create mode 100644 ui/src/main.js create mode 100644 ui/src/scenes/Filters.svelte create mode 100644 ui/src/scenes/SceneList.svelte create mode 100644 ui/src/scenes/Video.svelte create mode 100644 ui/src/scenes/index.svelte create mode 100644 ui/src/store/filters.js create mode 100644 ui/yarn.lock create mode 100644 xbase/api_dms.go create mode 100644 xbase/api_extension.go create mode 100644 xbase/api_scenes.go create mode 100644 xbase/api_tasks.go create mode 100644 xbase/bindata.go create mode 100644 xbase/db.go create mode 100644 xbase/dms.go create mode 100644 xbase/log.go create mode 100644 xbase/model_actor.go create mode 100644 xbase/model_file.go create mode 100644 xbase/model_image.go create mode 100644 xbase/model_scene.go create mode 100644 xbase/model_tag.go create mode 100644 xbase/model_volume.go create mode 100644 xbase/paths.go create mode 100644 xbase/scrape/badoink.go create mode 100644 xbase/scrape/milfvr.go create mode 100644 xbase/scrape/navr.go create mode 100644 xbase/scrape/scrape.go create mode 100644 xbase/scrape/virtualtaboo.go create mode 100644 xbase/scrape/vrbangers.go create mode 100644 xbase/scrape/wankzvr.go create mode 100644 xbase/server.go create mode 100644 xbase/task_content.go create mode 100644 xbase/task_volume.go diff --git a/.assets.toml b/.assets.toml new file mode 100644 index 000000000..c0a4c2fdb --- /dev/null +++ b/.assets.toml @@ -0,0 +1,19 @@ +pkg = "assets" +dest = "./xbase/assets/" +fmt = false + +[compression] +compress = true +method = "DefaultCompression" +keep = false + +clean = false +output = "ab0x.go" +unexporTed = false +spread = false +debug = true + +[[custom]] +files = ["./ui/public/"] +base = "./ui/public/" +exclude = [".DS_Store"] diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 000000000..b2c129daa --- /dev/null +++ b/.drone.yml @@ -0,0 +1,37 @@ +kind: pipeline +name: default + +workspace: + base: /go + path: appsrc/ + +steps: + - name: fetch + image: docker:git + when: + event: + - tag + commands: + - git fetch --tags + + - name: ui + image: node + when: + event: + - tag + commands: + - cd ui + - yarn install + - yarn build + + - name: build + image: mailchain/goreleaser-xcgo:latest + environment: + GITHUB_TOKEN: + from_secret: GITHUB_TOKEN + when: + event: + - tag + commands: + - apt-get update && apt-get install -y gcc-arm-linux-gnueabi g++-arm-linux-gnueabi + - goreleaser --skip-validate --rm-dist diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3ee44f694 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/.idea +.DS_Store + +npm-debug.log +yarn-error.log +go.sum +test.db + +/dist/ +/node_modules/ +/xbase/assets/ +/ui/public/static/bundle.* \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 000000000..3f5e647ec --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,66 @@ +before: + hooks: + - go mod download + - go get github.com/UnnoTed/fileb0x + - go get github.com/go-bindata/go-bindata + - go generate ./... +builds: +- id: xbvr-mac + binary: xbvr + env: + - CGO_ENABLED=1 + - CC=o64-clang + - CXX=o64-clang++ + goos: + - darwin + goarch: + - amd64 +- id: xbvr-windows + binary: xbvr + env: + - CGO_ENABLED=1 + - CC=x86_64-w64-mingw32-gcc + - CXX=x86_64-w64-mingw32-g++ + goos: + - windows + goarch: + - amd64 +- id: xbvr-linux-amd64 + binary: xbvr + env: + - CGO_ENABLED=1 + goos: + - linux + goarch: + - amd64 +- id: xbvr-linux-arm + binary: xbvr + env: + - CGO_ENABLED=1 + - CC=arm-linux-gnueabi-gcc + goos: + - linux + goarch: + - arm + goarm: + - 6 + - 7 +archives: +- replacements: + darwin: macOS + linux: Linux + windows: Windows + amd64: x86_64 +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' +release: + disable: false + github: + owner: cld9x + name: test diff --git a/.realize.yaml b/.realize.yaml new file mode 100755 index 000000000..413883bad --- /dev/null +++ b/.realize.yaml @@ -0,0 +1,32 @@ +settings: + legacy: + force: false + interval: 0s +server: + status: true + open: false + host: localhost + port: 5001 +schema: +- name: xbvr + env: + DEBUG: 1 + path: . + commands: + generate: + status: true + run: + status: true + args: + - run + watcher: + extensions: + - go + paths: + - / + ignore: + paths: + - .git + - .realize + - node_modules/ + - ui/ diff --git a/command/common.go b/command/common.go new file mode 100644 index 000000000..7746f4c66 --- /dev/null +++ b/command/common.go @@ -0,0 +1,19 @@ +package command + +import ( + "gopkg.in/urfave/cli.v1" +) + +var commands []cli.Command + +type Commander interface { + Execute(c *cli.Context) +} + +func RegisterCommand(command cli.Command) { + commands = append(commands, command) +} + +func GetCommands() []cli.Command { + return commands +} diff --git a/command/log.go b/command/log.go new file mode 100644 index 000000000..e5ee5ffcf --- /dev/null +++ b/command/log.go @@ -0,0 +1,23 @@ +package command + +import ( + "github.com/sirupsen/logrus" + "github.com/x-cray/logrus-prefixed-formatter" + "os" + "runtime" +) + +var log = logrus.New() + +func init() { + log.Out = os.Stdout + log.SetLevel(logrus.InfoLevel) + + if runtime.GOOS == "windows" { + log.Formatter = &prefixed.TextFormatter{ + DisableColors: true, + } + } else { + log.Formatter = &prefixed.TextFormatter{} + } +} diff --git a/command/run.go b/command/run.go new file mode 100644 index 000000000..ea597cc14 --- /dev/null +++ b/command/run.go @@ -0,0 +1,18 @@ +package command + +import ( + "github.com/cld9x/xbvr/xbase" + "gopkg.in/urfave/cli.v1" +) + +func ActionRun(c *cli.Context) { + xbase.StartServer() +} + +func init() { + RegisterCommand(cli.Command{ + Name: "run", + Usage: "run xbvr", + Action: ActionRun, + }) +} diff --git a/command/task.go b/command/task.go new file mode 100644 index 000000000..43a3b651d --- /dev/null +++ b/command/task.go @@ -0,0 +1,42 @@ +package command + +import ( + "github.com/cld9x/xbvr/xbase" + "github.com/cld9x/xbvr/xbase/scrape" + "gopkg.in/urfave/cli.v1" +) + +func ActionCleanTags(c *cli.Context) { + xbase.RenameTags() + xbase.CountTags() +} + +func ActionScrape(c *cli.Context) { + scrape.ScrapeNA() + scrape.ScrapeBadoink() + scrape.ScrapeMilfVR() + scrape.ScrapeVRB() + scrape.ScrapeWankz() + scrape.ScrapeVirtualTaboo() +} + +func init() { + RegisterCommand(cli.Command{ + Name: "task", + Usage: "run various tasks", + Subcommands: []cli.Command{ + { + Name: "clean-tags", + Category: "tasks", + Usage: "clean tags", + Action: ActionCleanTags, + }, + { + Name: "scrape", + Category: "tasks", + Usage: "run scrapers", + Action: ActionScrape, + }, + }, + }) +} diff --git a/command/volume.go b/command/volume.go new file mode 100644 index 000000000..7f2cf0d18 --- /dev/null +++ b/command/volume.go @@ -0,0 +1,78 @@ +package command + +import ( + "os" + "path/filepath" + + "github.com/cld9x/xbvr/xbase" + "gopkg.in/urfave/cli.v1" +) + +func ActionAddVolume(c *cli.Context) { + if len(c.Args()) == 1 { + path := c.Args()[0] + if fi, err := os.Stat(path); os.IsNotExist(err) || !fi.IsDir() { + log.Fatal("Path does not exist or is not a directory") + } + + path, _ = filepath.Abs(path) + + db, _ := xbase.GetDB() + defer db.Close() + + var vol []xbase.Volume + db.Where(&xbase.Volume{Path: path}).Find(&vol) + + if len(vol) > 0 { + log.Fatal("Volume already exists") + } + + nv := xbase.Volume{Path: path, IsEnabled: true, IsAvailable: true} + nv.Save() + + log.Info("Added new volume", path) + } +} + +func ActionRescanVolumes(c *cli.Context) { + xbase.RescanVolumes() +} + +func ActionSaveLocalInfo(c *cli.Context) { + db, _ := xbase.GetDB() + defer db.Close() + + var vol []xbase.Volume + db.Find(&vol) + + for i := range vol { + vol[i].SaveLocalInfo() + } +} + +func init() { + RegisterCommand(cli.Command{ + Name: "volume", + Usage: "manage Volumes", + Subcommands: []cli.Command{ + { + Name: "add", + Category: "volume", + Usage: "add new volume", + Action: ActionAddVolume, + }, + { + Name: "rescan", + Category: "volume", + Usage: "rescan Volumes", + Action: ActionRescanVolumes, + }, + { + Name: "localinfo", + Category: "volume", + Usage: "save File info", + Action: ActionSaveLocalInfo, + }, + }, + }) +} diff --git a/dms/dlna/dlna.go b/dms/dlna/dlna.go new file mode 100644 index 000000000..07f5880f2 --- /dev/null +++ b/dms/dlna/dlna.go @@ -0,0 +1,102 @@ +package dlna + +import ( + "fmt" + "strings" + "time" +) + +const ( + TimeSeekRangeDomain = "TimeSeekRange.dlna.org" + ContentFeaturesDomain = "contentFeatures.dlna.org" + TransferModeDomain = "transferMode.dlna.org" +) + +type ContentFeatures struct { + ProfileName string + SupportTimeSeek bool + SupportRange bool + // Play speeds, DLNA.ORG_PS would go here if supported. + Transcoded bool +} + +func BinaryInt(b bool) uint { + if b { + return 1 + } else { + return 0 + } +} + +// flags are in hex. trailing 24 zeroes, 26 are after the space +// "DLNA.ORG_OP=" time-seek-range-supp bytes-range-header-supp +func (cf ContentFeatures) String() (ret string) { + //DLNA.ORG_PN=[a-zA-Z0-9_]* + params := make([]string, 0, 2) + if cf.ProfileName != "" { + params = append(params, "DLNA.ORG_PN="+cf.ProfileName) + } + params = append(params, fmt.Sprintf( + "DLNA.ORG_OP=%b%b;DLNA.ORG_CI=%b", + BinaryInt(cf.SupportTimeSeek), + BinaryInt(cf.SupportRange), + BinaryInt(cf.Transcoded))) + return strings.Join(params, ";") +} + +func ParseNPTTime(s string) (time.Duration, error) { + var h, m, sec, ms time.Duration + n, err := fmt.Sscanf(s, "%d:%2d:%2d.%3d", &h, &m, &sec, &ms) + if err != nil { + return -1, err + } + if n < 3 { + return -1, fmt.Errorf("invalid npt time: %s", s) + } + ret := time.Duration(h) * time.Hour + ret += time.Duration(m) * time.Minute + ret += sec * time.Second + ret += ms * time.Millisecond + return ret, nil +} + +func FormatNPTTime(npt time.Duration) string { + npt /= time.Millisecond + ms := npt % 1000 + npt /= 1000 + s := npt % 60 + npt /= 60 + m := npt % 60 + npt /= 60 + h := npt + return fmt.Sprintf("%02d:%02d:%02d.%03d", h, m, s, ms) +} + +type NPTRange struct { + Start, End time.Duration +} + +func ParseNPTRange(s string) (ret NPTRange, err error) { + ss := strings.SplitN(s, "-", 2) + if ss[0] != "" { + ret.Start, err = ParseNPTTime(ss[0]) + if err != nil { + return + } + } + if ss[1] != "" { + ret.End, err = ParseNPTTime(ss[1]) + if err != nil { + return + } + } + return +} + +func (me NPTRange) String() (ret string) { + ret = me.Start.String() + "-" + if me.End >= 0 { + ret += me.End.String() + } + return +} diff --git a/dms/dlna/dlna_test.go b/dms/dlna/dlna_test.go new file mode 100644 index 000000000..d14953601 --- /dev/null +++ b/dms/dlna/dlna_test.go @@ -0,0 +1,16 @@ +package dlna + +import ( + "testing" +) + +func TestContentFeaturesString(t *testing.T) { + a := ContentFeatures{ + Transcoded: true, + SupportTimeSeek: true, + }.String() + e := "DLNA.ORG_OP=10;DLNA.ORG_CI=1" + if e != a { + t.Fatal(a) + } +} diff --git a/dms/dlna/dms/cd-service-desc.go b/dms/dlna/dms/cd-service-desc.go new file mode 100644 index 000000000..c171cc418 --- /dev/null +++ b/dms/dlna/dms/cd-service-desc.go @@ -0,0 +1,451 @@ +package dms + +const contentDirectoryServiceDescription = ` + + + 1 + 0 + + + + GetSearchCapabilities + + + SearchCaps + out + SearchCapabilities + + + + + GetSortCapabilities + + + SortCaps + out + SortCapabilities + + + + + GetSortExtensionCapabilities + + + SortExtensionCaps + out + SortExtensionCapabilities + + + + + GetFeatureList + + + FeatureList + out + FeatureList + + + + + GetSystemUpdateID + + + Id + out + SystemUpdateID + + + + + Browse + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + BrowseFlag + in + A_ARG_TYPE_BrowseFlag + + + Filter + in + A_ARG_TYPE_Filter + + + StartingIndex + in + A_ARG_TYPE_Index + + + RequestedCount + in + A_ARG_TYPE_Count + + + SortCriteria + in + A_ARG_TYPE_SortCriteria + + + Result + out + A_ARG_TYPE_Result + + + NumberReturned + out + A_ARG_TYPE_Count + + + TotalMatches + out + A_ARG_TYPE_Count + + + UpdateID + out + A_ARG_TYPE_UpdateID + + + + + Search + + + ContainerID + in + A_ARG_TYPE_ObjectID + + + SearchCriteria + in + A_ARG_TYPE_SearchCriteria + + + Filter + in + A_ARG_TYPE_Filter + + + StartingIndex + in + A_ARG_TYPE_Index + + + RequestedCount + in + A_ARG_TYPE_Count + + + SortCriteria + in + A_ARG_TYPE_SortCriteria + + + Result + out + A_ARG_TYPE_Result + + + NumberReturned + out + A_ARG_TYPE_Count + + + TotalMatches + out + A_ARG_TYPE_Count + + + UpdateID + out + A_ARG_TYPE_UpdateID + + + + + CreateObject + + + ContainerID + in + A_ARG_TYPE_ObjectID + + + Elements + in + A_ARG_TYPE_Result + + + ObjectID + out + A_ARG_TYPE_ObjectID + + + Result + out + A_ARG_TYPE_Result + + + + + DestroyObject + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + + + UpdateObject + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + CurrentTagValue + in + A_ARG_TYPE_TagValueList + + + NewTagValue + in + A_ARG_TYPE_TagValueList + + + + + MoveObject + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + NewParentID + in + A_ARG_TYPE_ObjectID + + + NewObjectID + out + A_ARG_TYPE_ObjectID + + + + + ImportResource + + + SourceURI + in + A_ARG_TYPE_URI + + + DestinationURI + in + A_ARG_TYPE_URI + + + TransferID + out + A_ARG_TYPE_TransferID + + + + + ExportResource + + + SourceURI + in + A_ARG_TYPE_URI + + + DestinationURI + in + A_ARG_TYPE_URI + + + TransferID + out + A_ARG_TYPE_TransferID + + + + + StopTransferResource + + + TransferID + in + A_ARG_TYPE_TransferID + + + + + DeleteResource + + + ResourceURI + in + A_ARG_TYPE_URI + + + + + GetTransferProgress + + + TransferID + in + A_ARG_TYPE_TransferID + + + TransferStatus + out + A_ARG_TYPE_TransferStatus + + + TransferLength + out + A_ARG_TYPE_TransferLength + + + TransferTotal + out + A_ARG_TYPE_TransferTotal + + + + + CreateReference + + + ContainerID + in + A_ARG_TYPE_ObjectID + + + ObjectID + in + A_ARG_TYPE_ObjectID + + + NewID + out + A_ARG_TYPE_ObjectID + + + + + + + SearchCapabilities + string + + + SortCapabilities + string + + + SortExtensionCapabilities + string + + + SystemUpdateID + ui4 + + + ContainerUpdateIDs + string + + + TransferIDs + string + + + FeatureList + string + + + A_ARG_TYPE_ObjectID + string + + + A_ARG_TYPE_Result + string + + + A_ARG_TYPE_SearchCriteria + string + + + A_ARG_TYPE_BrowseFlag + string + + BrowseMetadata + BrowseDirectChildren + + + + A_ARG_TYPE_Filter + string + + + A_ARG_TYPE_SortCriteria + string + + + A_ARG_TYPE_Index + ui4 + + + A_ARG_TYPE_Count + ui4 + + + A_ARG_TYPE_UpdateID + ui4 + + + A_ARG_TYPE_TransferID + ui4 + + + A_ARG_TYPE_TransferStatus + string + + COMPLETED + ERROR + IN_PROGRESS + STOPPED + + + + A_ARG_TYPE_TransferLength + string + + + A_ARG_TYPE_TransferTotal + string + + + A_ARG_TYPE_TagValueList + string + + + A_ARG_TYPE_URI + uri + + +` diff --git a/dms/dlna/dms/cds.go b/dms/dlna/dms/cds.go new file mode 100644 index 000000000..0667021e5 --- /dev/null +++ b/dms/dlna/dms/cds.go @@ -0,0 +1,665 @@ +package dms + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/anacrolix/ffprobe" + "github.com/cld9x/xbvr/dms/dlna" + "github.com/cld9x/xbvr/dms/upnp" + "github.com/cld9x/xbvr/dms/upnpav" + "gopkg.in/resty.v1" +) + +type jsonInfo struct { + DisplayName string `json:"display"` +} + +type browse struct { + ObjectID string + BrowseFlag string + Filter string + StartingIndex int + RequestedCount int +} + +type contentDirectoryService struct { + *Server + upnp.Eventing +} + +func FormatDurationSexagesimal(d time.Duration) string { + ns := d % time.Second + d /= time.Second + s := d % 60 + d /= 60 + m := d % 60 + d /= 60 + h := d + ret := fmt.Sprintf("%d:%02d:%02d.%09d", h, m, s, ns) + ret = strings.TrimRight(ret, "0") + ret = strings.TrimRight(ret, ".") + return ret +} + +func (cds *contentDirectoryService) updateIDString() string { + return fmt.Sprintf("%d", uint32(os.Getpid())) +} + +// Turns the given entry and DMS host into a UPnP object. A nil object is +// returned if the entry is not of interest. +func (me *contentDirectoryService) cdsObjectToUpnpavObject(cdsObject object, fileInfo os.FileInfo, host, userAgent string) (ret interface{}, err error) { + log.Println("cdsObjectToUpnpavObject", cdsObject.ID()) + + entryFilePath := cdsObject.FilePath() + + ignored, err := me.IgnorePath(entryFilePath) + if err != nil { + return + } + if ignored { + return + } + obj := upnpav.Object{ + ID: cdsObject.ID(), + Restricted: 1, + ParentID: cdsObject.ParentID(), + } + if fileInfo.IsDir() { + obj.Class = "object.container.storageFolder" + obj.Title = fileInfo.Name() + ret = upnpav.Container{Object: obj} + return + } + + // Check if JSON file is available + jsonPath := cdsObject.FilePath() + ".json" + if _, errt := os.Stat(jsonPath); errt == nil { + b, errt := ioutil.ReadFile(jsonPath) + if errt != nil { + return + } + + dat := &jsonInfo{} + if errt := json.Unmarshal(b, &dat); errt != nil { + return + } + + obj.Title = dat.DisplayName + "_180_180x180_3dh_LR.mp4" + } + + if !fileInfo.Mode().IsRegular() { + // log.Printf("%s ignored: non-regular file", cdsObject.FilePath()) + return + } + mimeType, err := MimeTypeByPath(entryFilePath) + if err != nil { + return + } + // Use IsMedia() below if images should be shown + if !mimeType.IsVideo() { + // log.Printf("%s ignored: non-media file (%s)", cdsObject.FilePath(), mimeType) + return + } + iconURI := (&url.URL{ + Scheme: "http", + Host: host, + Path: iconPath, + RawQuery: url.Values{ + "path": {cdsObject.Path}, + }.Encode(), + }).String() + obj.Icon = iconURI + // TODO(anacrolix): This might not be necessary due to item res image + // element. + obj.AlbumArtURI = iconURI + obj.Class = "object.item." + mimeType.Type() + "Item" + var ( + ffInfo *ffprobe.Info + nativeBitrate uint + resDuration string + ) + if !me.NoProbe { + ffInfo, probeErr := me.ffmpegProbe(entryFilePath) + switch probeErr { + case nil: + if ffInfo != nil { + nativeBitrate, _ = ffInfo.Bitrate() + if d, err := ffInfo.Duration(); err == nil { + resDuration = FormatDurationSexagesimal(d) + } + } + case ffprobe.ExeNotFound: + default: + log.Printf("error probing %s: %s", entryFilePath, probeErr) + } + } + if obj.Title == "" { + obj.Title = fileInfo.Name() + } + resolution := func() string { + if ffInfo != nil { + for _, strm := range ffInfo.Streams { + if strm["codec_type"] != "video" { + continue + } + width := strm["width"] + height := strm["height"] + return fmt.Sprintf("%.0fx%.0f", width, height) + } + } + return "" + }() + item := upnpav.Item{ + Object: obj, + // Capacity: 1 for raw, 1 for icon, plus transcodes. + Res: make([]upnpav.Resource, 0, 2+len(transcodes)), + } + item.Res = append(item.Res, upnpav.Resource{ + URL: (&url.URL{ + Scheme: "http", + Host: host, + Path: resPath, + RawQuery: url.Values{ + "path": {cdsObject.Path}, + }.Encode(), + }).String(), + ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", mimeType, dlna.ContentFeatures{ + SupportRange: true, + }.String()), + Bitrate: nativeBitrate, + Duration: resDuration, + Size: uint64(fileInfo.Size()), + Resolution: resolution, + }) + if mimeType.IsVideo() { + if !me.NoTranscode { + item.Res = append(item.Res, transcodeResources(host, cdsObject.Path, resolution, resDuration)...) + } + } + if mimeType.IsVideo() || mimeType.IsImage() { + item.Res = append(item.Res, upnpav.Resource{ + URL: (&url.URL{ + Scheme: "http", + Host: host, + Path: iconPath, + RawQuery: url.Values{ + "path": {cdsObject.Path}, + "c": {"jpeg"}, + }.Encode(), + }).String(), + ProtocolInfo: "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN", + }) + } + ret = item + return +} + +func (me *contentDirectoryService) sceneToContainer(scene XbaseScene, parent string, host string) interface{} { + c := make([]string, 0) + for i := range scene.Cast { + c = append(c, scene.Cast[i].Name) + } + + if len(scene.File) == 0 { + return nil + } + + // Object goes first + obj := upnpav.Object{ + ID: scene.SceneID, + Restricted: 1, + ParentID: parent, + Title: strings.Join(c, ", ") + " - " + scene.Title + " _180_180x180_3dh_LR.mp4", + } + + // Wrap up + item := upnpav.Item{ + Object: obj, + Res: make([]upnpav.Resource, 0, 2), + } + + item.Res = append(item.Res, upnpav.Resource{ + URL: (&url.URL{ + Scheme: "http", + Host: host, + Path: iconPath, + RawQuery: url.Values{ + "scene": {scene.SceneID}, + "c": {"jpeg"}, + }.Encode(), + }).String(), + ProtocolInfo: "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN", + }) + + file := scene.File[0] + mimeType := "video/mp4" + + item.Res = append(item.Res, upnpav.Resource{ + URL: (&url.URL{ + Scheme: "http", + Host: host, + Path: resPath, + RawQuery: url.Values{ + "scene": {scene.SceneID}, + }.Encode(), + }).String(), + ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", mimeType, dlna.ContentFeatures{ + SupportRange: true, + }.String()), + Bitrate: file.VideoBitrate, + // Duration: resDuration, + Size: uint64(file.Size), + // Resolution: resolution, + }) + + return item +} + +// Returns all the upnpav objects in a directory. +func (me *contentDirectoryService) readContainer(o object, host, userAgent string) (ret []interface{}, err error) { + sfis := sortableFileInfoSlice{ + // TODO(anacrolix): Dig up why this special cast was added. + FoldersLast: strings.Contains(userAgent, `AwoX/1.1`), + } + sfis.fileInfoSlice, err = o.readDir() + if err != nil { + return + } + sort.Sort(sfis) + for _, fi := range sfis.fileInfoSlice { + child := object{path.Join(o.Path, fi.Name()), me.RootObjectPath} + obj, err := me.cdsObjectToUpnpavObject(child, fi, host, userAgent) + if err != nil { + log.Printf("error with %s: %s", child.FilePath(), err) + continue + } + if obj != nil { + ret = append(ret, obj) + } + } + return +} + +// ContentDirectory object from ObjectID. +func (me *contentDirectoryService) objectFromID(id string) (o object, err error) { + o.Path, err = url.QueryUnescape(id) + if err != nil { + return + } + if o.Path == "0" { + o.Path = "/" + } + // o.Path = path.Clean(o.Path) + // if !path.IsAbs(o.Path) { + // err = fmt.Errorf("bad ObjectID %v", o.Path) + // return + // } + o.RootObjectPath = me.RootObjectPath + + return +} + +func (me *contentDirectoryService) Handle(action string, argsXML []byte, r *http.Request) (map[string]string, error) { + host := r.Host + // userAgent := r.UserAgent() + switch action { + case "GetSystemUpdateID": + return map[string]string{ + "Id": me.updateIDString(), + }, nil + case "GetSortCapabilities": + return map[string]string{ + "SortCaps": "dc:title", + }, nil + case "Browse": + var browse browse + if err := xml.Unmarshal([]byte(argsXML), &browse); err != nil { + return nil, err + } + + obj, err := me.objectFromID(browse.ObjectID) + if err != nil { + return nil, upnp.Errorf(upnpav.NoSuchObjectErrorCode, err.Error()) + } + + switch browse.BrowseFlag { + case "BrowseDirectChildren": + // Read folder and return children + // TODO: check if obj == 0 and return root objects + // TODO: check if special path and return files + + var objs []interface{} + + if obj.IsRoot() { + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "actors", + Restricted: 1, + ParentID: "0", + Class: "object.container.storageFolder", + Title: "actors", + }}) + + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "tags", + Restricted: 1, + ParentID: "0", + Class: "object.container.storageFolder", + Title: "tags", + }}) + + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "released", + Restricted: 1, + ParentID: "0", + Class: "object.container.storageFolder", + Title: "released", + }}) + + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "sites", + Restricted: 1, + ParentID: "0", + Class: "object.container.storageFolder", + Title: "sites", + }}) + + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "all", + Restricted: 1, + ParentID: "0", + Class: "object.container.storageFolder", + Title: "all", + }}) + + } + + // All videos + if obj.Path == "all" { + var data XbaseScenes + resty.R().SetResult(&data).Get("http://127.0.0.1:9999/api/scene/list?is_accessible=1") + + for i := range data.Scenes { + objs = append(objs, me.sceneToContainer(data.Scenes[i], "all", host)) + } + } + + // Sites + if obj.Path == "sites" { + data := XbaseGet() + + for i := range data.Sites { + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "sites/" + data.Sites[i], + Restricted: 1, + ParentID: "sites", + Class: "object.container.storageFolder", + Title: data.Sites[i], + }}) + } + } + + if strings.HasPrefix(obj.Path, "sites/") { + id := strings.Split(obj.Path, "/") + + listURL := (&url.URL{ + Scheme: "http", + Host: "127.0.0.1:9999", + Path: "/api/scene/list", + RawQuery: url.Values{ + "is_accessible": {"1"}, + "site": {id[1]}, + }.Encode(), + }).String() + + var data XbaseScenes + resty.R().SetResult(&data).Get(listURL) + + for i := range data.Scenes { + objs = append(objs, me.sceneToContainer(data.Scenes[i], "sites/"+id[1], host)) + } + } + + // Tags + if obj.Path == "tags" { + data := XbaseGet() + + for i := range data.Tags { + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "tags/" + data.Tags[i], + Restricted: 1, + ParentID: "tags", + Class: "object.container.storageFolder", + Title: data.Tags[i], + }}) + } + } + + if strings.HasPrefix(obj.Path, "tags/") { + id := strings.Split(obj.Path, "/") + + listURL := (&url.URL{ + Scheme: "http", + Host: "127.0.0.1:9999", + Path: "/api/scene/list", + RawQuery: url.Values{ + "is_accessible": {"1"}, + "tag": {id[1]}, + }.Encode(), + }).String() + + var data XbaseScenes + resty.R().SetResult(&data).Get(listURL) + + for i := range data.Scenes { + objs = append(objs, me.sceneToContainer(data.Scenes[i], "tags/"+id[1], host)) + } + } + + // Actors + if obj.Path == "actors" { + data := XbaseGet() + + for i := range data.Actors { + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "actors/" + data.Actors[i], + Restricted: 1, + ParentID: "actors", + Class: "object.container.storageFolder", + Title: data.Actors[i], + }}) + } + } + + if strings.HasPrefix(obj.Path, "actors/") { + id := strings.Split(obj.Path, "/") + + listURL := (&url.URL{ + Scheme: "http", + Host: "127.0.0.1:9999", + Path: "/api/scene/list", + RawQuery: url.Values{ + "is_accessible": {"1"}, + "cast": {id[1]}, + }.Encode(), + }).String() + + var data XbaseScenes + resty.R().SetResult(&data).Get(listURL) + + for i := range data.Scenes { + objs = append(objs, me.sceneToContainer(data.Scenes[i], "actors/"+id[1], host)) + } + } + + // Release date + if obj.Path == "released" { + data := XbaseGet() + + for i := range data.ReleaseGroup { + objs = append(objs, upnpav.Container{Object: upnpav.Object{ + ID: "released/" + data.ReleaseGroup[i], + Restricted: 1, + ParentID: "released", + Class: "object.container.storageFolder", + Title: data.ReleaseGroup[i], + }}) + } + } + + result, err := xml.Marshal(objs) + if err != nil { + return nil, err + } + + return map[string]string{ + "TotalMatches": fmt.Sprint(len(objs)), + "NumberReturned": fmt.Sprint(len(objs)), + "Result": didl_lite(string(result)), + "UpdateID": me.updateIDString(), + }, nil + // case "BrowseMetadata": + // fileInfo, err := os.Stat(obj.FilePath()) + // if err != nil { + // if os.IsNotExist(err) { + // return nil, &upnp.Error{ + // Code: upnpav.NoSuchObjectErrorCode, + // Desc: err.Error(), + // } + // } + // return nil, err + // } + // upnp, err := me.cdsObjectToUpnpavObject(obj, fileInfo, host, userAgent) + // if err != nil { + // return nil, err + // } + // buf, err := xml.Marshal(upnp) + // if err != nil { + // return nil, err + // } + // return map[string]string{ + // "TotalMatches": "1", + // "NumberReturned": "1", + // "Result": didl_lite(func() string { return string(buf) }()), + // "UpdateID": me.updateIDString(), + // }, nil + default: + return nil, upnp.Errorf(upnp.ArgumentValueInvalidErrorCode, "unhandled browse flag: %v", browse.BrowseFlag) + } + case "GetSearchCapabilities": + return map[string]string{ + "SearchCaps": "", + }, nil + default: + return nil, upnp.InvalidActionError + } +} + +// Returns the number of children this object has, such as for a container. +func (cds *contentDirectoryService) objectChildCount(me object) int { + objs, err := cds.readContainer(me, "", "") + if err != nil { + log.Printf("error reading container: %s", err) + } + return len(objs) +} + +func (cds *contentDirectoryService) objectHasChildren(obj object) bool { + return cds.objectChildCount(obj) != 0 +} + +// Represents a ContentDirectory object. +type object struct { + Path string // The cleaned, absolute path for the object relative to the server. + RootObjectPath string +} + +// Returns the actual local filesystem path for the object. +func (o *object) FilePath() string { + return filepath.Join(o.RootObjectPath, filepath.FromSlash(o.Path)) +} + +// Returns the ObjectID for the object. This is used in various ContentDirectory actions. +func (o object) ID() string { + if !path.IsAbs(o.Path) { + log.Panicf("Relative object path: %s", o.Path) + } + if len(o.Path) == 1 { + return "0" + } + return url.QueryEscape(o.Path) +} + +func (o *object) IsRoot() bool { + return o.Path == "/" +} + +// Returns the object's parent ObjectID. Fortunately it can be deduced from the +// ObjectID (for now). +func (o object) ParentID() string { + if o.IsRoot() { + return "-1" + } + o.Path = path.Dir(o.Path) + return o.ID() +} + +// This function exists rather than just calling os.(*File).Readdir because I +// want to stat(), not lstat() each entry. +func (o *object) readDir() (fis []os.FileInfo, err error) { + dirPath := o.FilePath() + dirFile, err := os.Open(dirPath) + if err != nil { + return + } + defer dirFile.Close() + var dirContent []string + dirContent, err = dirFile.Readdirnames(-1) + if err != nil { + return + } + fis = make([]os.FileInfo, 0, len(dirContent)) + for _, file := range dirContent { + fi, err := os.Stat(filepath.Join(dirPath, file)) + if err != nil { + continue + } + fis = append(fis, fi) + // spew.Dump(fi) + } + return +} + +type sortableFileInfoSlice struct { + fileInfoSlice []os.FileInfo + FoldersLast bool +} + +func (me sortableFileInfoSlice) Len() int { + return len(me.fileInfoSlice) +} + +func (me sortableFileInfoSlice) Less(i, j int) bool { + if me.fileInfoSlice[i].IsDir() && !me.fileInfoSlice[j].IsDir() { + return !me.FoldersLast + } + if !me.fileInfoSlice[i].IsDir() && me.fileInfoSlice[j].IsDir() { + return me.FoldersLast + } + return strings.ToLower(me.fileInfoSlice[i].Name()) < strings.ToLower(me.fileInfoSlice[j].Name()) +} + +func (me sortableFileInfoSlice) Swap(i, j int) { + me.fileInfoSlice[i], me.fileInfoSlice[j] = me.fileInfoSlice[j], me.fileInfoSlice[i] +} diff --git a/dms/dlna/dms/cds_test.go b/dms/dlna/dms/cds_test.go new file mode 100644 index 000000000..e12b6b442 --- /dev/null +++ b/dms/dlna/dms/cds_test.go @@ -0,0 +1,28 @@ +package dms + +import ( + "strings" + "testing" +) + +func TestEscapeObjectID(t *testing.T) { + o := object{ + Path: "/some/file", + } + id := o.ID() + if strings.ContainsAny(id, "/") { + t.Skip("may not work with some players: object IDs contain '/'") + } +} + +func TestRootObjectID(t *testing.T) { + if (object{Path: "/"}).ID() != "0" { + t.FailNow() + } +} + +func TestRootParentObjectID(t *testing.T) { + if (object{Path: "/"}).ParentID() != "-1" { + t.FailNow() + } +} diff --git a/dms/dlna/dms/cm-service-desc.go b/dms/dlna/dms/cm-service-desc.go new file mode 100644 index 000000000..855d19c2f --- /dev/null +++ b/dms/dlna/dms/cm-service-desc.go @@ -0,0 +1,275 @@ +package dms + +const connectionManagerServiceDesc = ` + + + 1 + 0 + + + + GetProtocolInfo + + + Source + out + +SourceProtocolInfo + + + + Sink + out + +SinkProtocolInfo + + + + + + PrepareForConnection + + + RemoteProtocolInfo + in + +A_ARG_TYPE_ProtocolInfo + + + + PeerConnectionManager + in + +A_ARG_TYPE_ConnectionManager + + + + PeerConnectionID + in + +A_ARG_TYPE_ConnectionID + + + + Direction + in + +A_ARG_TYPE_Direction + + + + ConnectionID + out + +A_ARG_TYPE_ConnectionID + + + + AVTransportID + out + A_ARG_TYPE_AVTransportID + + + RcsID + out + A_ARG_TYPE_RcsID + + + + + ConnectionComplete + + + ConnectionID + in + A_ARG_TYPE_ConnectionID + + + + + GetCurrentConnectionIDs + + + ConnectionIDs + out + +CurrentConnectionIDs + + + + + + GetCurrentConnectionInfo + + + ConnectionID + in + +A_ARG_TYPE_ConnectionID + + + + RcsID + out + +A_ARG_TYPE_RcsID + + + + AVTransportID + out + +A_ARG_TYPE_AVTransportID + + + + ProtocolInfo + out + +A_ARG_TYPE_ProtocolInfo + + + + PeerConnectionManager + out + +A_ARG_TYPE_ConnectionManager + + + + PeerConnectionID + out + +A_ARG_TYPE_ConnectionID + + + + Direction + out + +A_ARG_TYPE_Direction + + + + Status + out + +A_ARG_TYPE_ConnectionStatus + + + + + + GetRendererItemInfo + + + ItemInfoFilter + in + +A_ARG_TYPE_ItemInfoFilter + + + + ItemMetadataList + in + +A_ARG_TYPE_Result + + + + ItemRenderingInfoList + out + +A_ARG_TYPE_RenderingInfoList + + + + + + GetFeatureList + + + FeatureList + out + +FeatureList + + + + + + + + SourceProtocolInfo + string + + + SinkProtocolInfo + string + + + CurrentConnectionIDs + string + + + A_ARG_TYPE_ConnectionStatus + string + + OK + ContentFormatMismatch + InsufficientBandwidth + UnreliableChannel + Unknown + + + + A_ARG_TYPE_ConnectionManager + string + + + A_ARG_TYPE_Direction + string + + Input + Output + + + + A_ARG_TYPE_ProtocolInfo + string + + + A_ARG_TYPE_ConnectionID + i4 + + + A_ARG_TYPE_AVTransportID + i4 + + + A_ARG_TYPE_RcsID + i4 + + + A_ARG_TYPE_ItemInfoFilter + string + + + A_ARG_TYPE_Result + string + + + A_ARG_TYPE_RenderingInfoList + string + + + ClockUpdateID + ui4 + + + DeviceClockInfoUpdates + string + + + +` diff --git a/dms/dlna/dms/dms.go b/dms/dlna/dms/dms.go new file mode 100644 index 000000000..e06c85821 --- /dev/null +++ b/dms/dlna/dms/dms.go @@ -0,0 +1,934 @@ +package dms + +import ( + "bytes" + "crypto/md5" + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "net/http/pprof" + "net/url" + "os" + "os/user" + "path" + "path/filepath" + "strings" + "time" + + "github.com/cld9x/xbvr/dms/dlna" + "github.com/cld9x/xbvr/dms/soap" + "github.com/cld9x/xbvr/dms/ssdp" + "github.com/cld9x/xbvr/dms/transcode" + "github.com/cld9x/xbvr/dms/upnp" + "github.com/cld9x/xbvr/dms/upnpav" + "github.com/anacrolix/ffprobe" +) + +const ( + serverField = "Linux/3.4 DLNADOC/1.50 UPnP/1.0 DMS/1.0" + rootDeviceType = "urn:schemas-upnp-org:device:MediaServer:1" + rootDeviceModelName = "dms 1.0xb" + resPath = "/res" + iconPath = "/icon" + rootDescPath = "/rootDesc.xml" + contentDirectorySCPDURL = "/scpd/ContentDirectory.xml" + contentDirectoryEventSubURL = "/evt/ContentDirectory" + serviceControlURL = "/ctl" + deviceIconPath = "/deviceIcon" +) + +type transcodeSpec struct { + mimeType string + DLNAProfileName string + Transcode func(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) +} + +var transcodes = map[string]transcodeSpec{ + "t": { + mimeType: "video/mpeg", + DLNAProfileName: "MPEG_PS_PAL", + Transcode: transcode.Transcode, + }, + "vp8": {mimeType: "video/webm", Transcode: transcode.VP8Transcode}, + "chromecast": {mimeType: "video/mp4", Transcode: transcode.ChromecastTranscode}, +} + +func makeDeviceUuid(unique string) string { + h := md5.New() + if _, err := io.WriteString(h, unique); err != nil { + log.Panicf("makeDeviceUuid write failed: %s", err) + } + buf := h.Sum(nil) + return upnp.FormatUUID(buf) +} + +// Groups the service definition with its XML description. +type service struct { + upnp.Service + SCPD string +} + +// Exposed UPnP AV services. +var services = []*service{ + { + Service: upnp.Service{ + ServiceType: "urn:schemas-upnp-org:service:ContentDirectory:1", + ServiceId: "urn:upnp-org:serviceId:ContentDirectory", + EventSubURL: contentDirectoryEventSubURL, + }, + SCPD: contentDirectoryServiceDescription, + }, + // { + // Service: upnp.Service{ + // ServiceType: "urn:schemas-upnp-org:service:ConnectionManager:3", + // ServiceId: "urn:upnp-org:serviceId:ConnectionManager", + // }, + // SCPD: connectionManagerServiceDesc, + // }, +} + +// The control URL for every service is the same. We're able to infer the desired service from the request headers. +func init() { + for _, s := range services { + s.ControlURL = serviceControlURL + } +} + +func devices() []string { + return []string{ + "urn:schemas-upnp-org:device:MediaServer:1", + } +} + +func serviceTypes() (ret []string) { + for _, s := range services { + ret = append(ret, s.ServiceType) + } + return +} +func (me *Server) httpPort() int { + return me.HTTPConn.Addr().(*net.TCPAddr).Port +} + +func (me *Server) serveHTTP() error { + srv := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if me.LogHeaders { + fmt.Fprintf(os.Stderr, "%s %s\r\n", r.Method, r.RequestURI) + r.Header.Write(os.Stderr) + fmt.Fprintln(os.Stderr) + } + w.Header().Set("Ext", "") + w.Header().Set("Server", serverField) + me.httpServeMux.ServeHTTP(&mitmRespWriter{ + ResponseWriter: w, + logHeader: me.LogHeaders, + }, r) + }), + } + err := srv.Serve(me.HTTPConn) + select { + case <-me.closed: + return nil + default: + return err + } +} + +// An interface with these flags should be valid for SSDP. +const ssdpInterfaceFlags = net.FlagUp | net.FlagMulticast + +func (me *Server) doSSDP() { + active := 0 + stopped := make(chan struct{}) + for _, if_ := range me.Interfaces { + active++ + go func(if_ net.Interface) { + defer func() { + stopped <- struct{}{} + }() + me.ssdpInterface(if_) + }(if_) + } + for active > 0 { + <-stopped + active-- + } +} + +// Run SSDP server on an interface. +func (me *Server) ssdpInterface(if_ net.Interface) { + s := ssdp.Server{ + Interface: if_, + Devices: devices(), + Services: serviceTypes(), + Location: func(ip net.IP) string { + return me.location(ip) + }, + Server: serverField, + UUID: me.rootDeviceUUID, + NotifyInterval: me.NotifyInterval, + } + if err := s.Init(); err != nil { + if if_.Flags&ssdpInterfaceFlags != ssdpInterfaceFlags { + // Didn't expect it to work anyway. + return + } + if strings.Contains(err.Error(), "listen") { + // OSX has a lot of dud interfaces. Failure to create a socket on + // the interface are what we're expecting if the interface is no + // good. + return + } + log.Printf("error creating ssdp server on %s: %s", if_.Name, err) + return + } + defer s.Close() + log.Println("started SSDP on", if_.Name) + stopped := make(chan struct{}) + go func() { + defer close(stopped) + if err := s.Serve(); err != nil { + log.Printf("%q: %q\n", if_.Name, err) + } + }() + select { + case <-me.closed: + // Returning will close the server. + case <-stopped: + } +} + +var ( + startTime time.Time +) + +type Icon struct { + Width, Height, Depth int + Mimetype string + io.ReadSeeker +} + +type Server struct { + HTTPConn net.Listener + FriendlyName string + Interfaces []net.Interface + httpServeMux *http.ServeMux + RootObjectPath string + rootDescXML []byte + rootDeviceUUID string + FFProbeCache Cache + closed chan struct{} + ssdpStopped chan struct{} + // The service SOAP handler keyed by service URN. + services map[string]UPnPService + LogHeaders bool + // Disable transcoding, and the resource elements implied in the CDS. + NoTranscode bool + // Disable media probing with ffprobe + NoProbe bool + Icons []Icon + // Stall event subscription requests until they drop. A workaround for + // some bad clients. + StallEventSubscribe bool + // Time interval between SSPD announces + NotifyInterval time.Duration + // Ignore hidden files and directories + IgnoreHidden bool + // Ingnore unreadable files and directories + IgnoreUnreadable bool +} + +// UPnP SOAP service. +type UPnPService interface { + Handle(action string, argsXML []byte, r *http.Request) (respArgs map[string]string, err error) + Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) + Unsubscribe(sid string) error +} + +type Cache interface { + Set(key interface{}, value interface{}) + Get(key interface{}) (value interface{}, ok bool) +} + +type dummyFFProbeCache struct{} + +func (dummyFFProbeCache) Set(interface{}, interface{}) {} + +func (dummyFFProbeCache) Get(interface{}) (interface{}, bool) { + return nil, false +} + +// Public definition so that external modules can persist cache contents. +type FfprobeCacheItem struct { + Key ffmpegInfoCacheKey + Value *ffprobe.Info +} + +// update the UPnP object fields from ffprobe data +// priority is given the format section, and then the streams sequentially +func itemExtra(item *upnpav.Object, info *ffprobe.Info) { + setFromTags := func(m map[string]interface{}) { + for key, val := range m { + setIfUnset := func(s *string) { + if *s == "" { + *s = val.(string) + } + } + switch strings.ToLower(key) { + case "tag:artist": + setIfUnset(&item.Artist) + case "tag:album": + setIfUnset(&item.Album) + case "tag:genre": + setIfUnset(&item.Genre) + } + } + } + setFromTags(info.Format) + for _, m := range info.Streams { + setFromTags(m) + } +} + +type ffmpegInfoCacheKey struct { + Path string + ModTime int64 +} + +func transcodeResources(host, path, resolution, duration string) (ret []upnpav.Resource) { + ret = make([]upnpav.Resource, 0, len(transcodes)) + for k, v := range transcodes { + ret = append(ret, upnpav.Resource{ + ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", v.mimeType, dlna.ContentFeatures{ + SupportTimeSeek: true, + Transcoded: true, + ProfileName: v.DLNAProfileName, + }.String()), + URL: (&url.URL{ + Scheme: "http", + Host: host, + Path: resPath, + RawQuery: url.Values{ + "path": {path}, + "transcode": {k}, + }.Encode(), + }).String(), + Resolution: resolution, + Duration: duration, + }) + } + return +} + +func parseDLNARangeHeader(val string) (ret dlna.NPTRange, err error) { + if !strings.HasPrefix(val, "npt=") { + err = errors.New("bad prefix") + return + } + ret, err = dlna.ParseNPTRange(val[len("npt="):]) + if err != nil { + return + } + return +} + +// Determines the time-based range to transcode, and sets the appropriate +// headers. Returns !ok if there was an error and the caller should stop +// handling the request. +func handleDLNARange(w http.ResponseWriter, hs http.Header) (r dlna.NPTRange, partialResponse, ok bool) { + if len(hs[http.CanonicalHeaderKey(dlna.TimeSeekRangeDomain)]) == 0 { + ok = true + return + } + partialResponse = true + h := hs.Get(dlna.TimeSeekRangeDomain) + r, err := parseDLNARangeHeader(h) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + // Passing an exact NPT duration seems to cause trouble pass the "iono" + // (*) duration instead. + // + // TODO: Check that the request range can't already have /. + w.Header().Set(dlna.TimeSeekRangeDomain, h+"/*") + ok = true + return +} + +func (me *Server) serveDLNATranscode(w http.ResponseWriter, r *http.Request, path_ string, ts transcodeSpec, tsname string) { + w.Header().Set(dlna.TransferModeDomain, "Streaming") + w.Header().Set("content-type", ts.mimeType) + w.Header().Set(dlna.ContentFeaturesDomain, (dlna.ContentFeatures{ + Transcoded: true, + SupportTimeSeek: true, + }).String()) + // If a range of any kind is given, we have to respond with 206 if we're + // interpreting that range. Since only the DLNA range is handled in this + // function, it alone determines if we'll give a partial response. + range_, partialResponse, ok := handleDLNARange(w, r.Header) + if !ok { + return + } + ffInfo, _ := me.ffmpegProbe(path_) + if ffInfo != nil { + if duration, err := ffInfo.Duration(); err == nil { + s := fmt.Sprintf("%f", duration.Seconds()) + w.Header().Set("content-duration", s) + w.Header().Set("x-content-duration", s) + } + } + stderrPath := func() string { + u, _ := user.Current() + return filepath.Join(u.HomeDir, ".dms", "log", tsname, filepath.Base(path_)) + }() + os.MkdirAll(filepath.Dir(stderrPath), 0750) + logFile, err := os.Create(stderrPath) + if err != nil { + log.Printf("couldn't create transcode log file: %s", err) + } else { + defer logFile.Close() + log.Printf("logging transcode to %q", stderrPath) + } + p, err := ts.Transcode(path_, range_.Start, range_.End-range_.Start, logFile) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer p.Close() + // I recently switched this to returning 200 if no range is specified for + // pure UPnP clients. It's possible that DLNA clients will *always* expect + // 206. It appears the HTTP standard requires that 206 only be used if a + // response is not interpreting any range headers. + w.WriteHeader(func() int { + if partialResponse { + return http.StatusPartialContent + } else { + return http.StatusOK + } + }()) + io.Copy(w, p) +} + +func init() { + startTime = time.Now() +} + +func getDefaultFriendlyName() string { + return "xbvr" +} + +func xmlMarshalOrPanic(value interface{}) []byte { + ret, err := xml.MarshalIndent(value, "", " ") + if err != nil { + log.Panicf("xmlMarshalOrPanic failed to marshal %v: %s", value, err) + } + return ret +} + +// TODO: Document the use of this for debugging. +type mitmRespWriter struct { + http.ResponseWriter + loggedHeader bool + logHeader bool +} + +func (me *mitmRespWriter) WriteHeader(code int) { + me.doLogHeader(code) + me.ResponseWriter.WriteHeader(code) +} + +func (me *mitmRespWriter) doLogHeader(code int) { + if !me.logHeader { + return + } + fmt.Fprintln(os.Stderr, code) + for k, v := range me.Header() { + fmt.Fprintln(os.Stderr, k, v) + } + fmt.Fprintln(os.Stderr) + me.loggedHeader = true +} + +func (me *mitmRespWriter) Write(b []byte) (int, error) { + if !me.loggedHeader { + me.doLogHeader(200) + } + return me.ResponseWriter.Write(b) +} + +func (me *mitmRespWriter) CloseNotify() <-chan bool { + return me.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +// Set the SCPD serve paths. +func init() { + for _, s := range services { + p := path.Join("/scpd", s.ServiceId) + s.SCPDURL = p + } +} + +// Install handlers to serve SCPD for each UPnP service. +func handleSCPDs(mux *http.ServeMux) { + for _, s := range services { + mux.HandleFunc(s.SCPDURL, func(serviceDesc string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", `text/xml; charset="utf-8"`) + http.ServeContent(w, r, ".xml", startTime, bytes.NewReader([]byte(serviceDesc))) + } + }(s.SCPD)) + } +} + +// Marshal SOAP response arguments into a response XML snippet. +func marshalSOAPResponse(sa upnp.SoapAction, args map[string]string) []byte { + soapArgs := make([]soap.Arg, 0, len(args)) + for argName, value := range args { + soapArgs = append(soapArgs, soap.Arg{ + XMLName: xml.Name{Local: argName}, + Value: value, + }) + } + return []byte(fmt.Sprintf(`%[3]s`, sa.Action, sa.ServiceURN.String(), xmlMarshalOrPanic(soapArgs))) +} + +// Handle a SOAP request and return the response arguments or UPnP error. +func (me *Server) soapActionResponse(sa upnp.SoapAction, actionRequestXML []byte, r *http.Request) (map[string]string, error) { + service, ok := me.services[sa.Type] + if !ok { + // TODO: What's the invalid service error?! + return nil, upnp.Errorf(upnp.InvalidActionErrorCode, "Invalid service: %s", sa.Type) + } + return service.Handle(sa.Action, actionRequestXML, r) +} + +// Handle a service control HTTP request. +func (me *Server) serviceControlHandler(w http.ResponseWriter, r *http.Request) { + soapActionString := r.Header.Get("SOAPACTION") + soapAction, err := upnp.ParseActionHTTPHeader(soapActionString) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + var env soap.Envelope + if err := xml.NewDecoder(r.Body).Decode(&env); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + //AwoX/1.1 UPnP/1.0 DLNADOC/1.50 + //log.Println(r.UserAgent()) + w.Header().Set("Content-Type", `text/xml; charset="utf-8"`) + w.Header().Set("Ext", "") + w.Header().Set("Server", serverField) + soapRespXML, code := func() ([]byte, int) { + respArgs, err := me.soapActionResponse(soapAction, env.Body.Action, r) + if err != nil { + upnpErr := upnp.ConvertError(err) + return xmlMarshalOrPanic(soap.NewFault("UPnPError", upnpErr)), 500 + } + return marshalSOAPResponse(soapAction, respArgs), 200 + }() + bodyStr := fmt.Sprintf(`%s`, soapRespXML) + w.WriteHeader(code) + if _, err := w.Write([]byte(bodyStr)); err != nil { + log.Print(err) + } +} + +func safeFilePath(root, given string) string { + return filepath.Join(root, filepath.FromSlash(path.Clean("/" + given))[1:]) +} + +func (s *Server) filePath(_path string) string { + return safeFilePath(s.RootObjectPath, _path) +} + +func (me *Server) serveIcon(w http.ResponseWriter, r *http.Request) { + sceneId := r.URL.Query().Get("scene") + data := XbaseGetScene(sceneId) + + read, write := io.Pipe() + + go func() { + defer write.Close() + resp, err := http.Get("http://127.0.0.1:9999/img/700x/" + strings.Replace(data.CoverURL, "://", ":/", -1)) + if err != nil { + return + } + defer resp.Body.Close() + io.Copy(write, resp.Body) + + }() + + io.Copy(w, read) +} + +func (server *Server) contentDirectoryInitialEvent(urls []*url.URL, sid string) { + body := xmlMarshalOrPanic(upnp.PropertySet{ + Properties: []upnp.Property{ + { + Variable: upnp.Variable{ + XMLName: xml.Name{ + Local: "SystemUpdateID", + }, + Value: "0", + }, + }, + // upnp.Property{ + // Variable: upnp.Variable{ + // XMLName: xml.Name{ + // Local: "ContainerUpdateIDs", + // }, + // }, + // }, + // upnp.Property{ + // Variable: upnp.Variable{ + // XMLName: xml.Name{ + // Local: "TransferIDs", + // }, + // }, + // }, + }, + Space: "urn:schemas-upnp-org:event-1-0", + }) + body = append([]byte(``+"\n"), body...) + eventingLogger.Print(string(body)) + for _, _url := range urls { + bodyReader := bytes.NewReader(body) + req, err := http.NewRequest("NOTIFY", _url.String(), bodyReader) + if err != nil { + log.Printf("Could not create a request to notify %s: %s", _url.String(), err) + continue + } + req.Header["CONTENT-TYPE"] = []string{`text/xml; charset="utf-8"`} + req.Header["NT"] = []string{"upnp:event"} + req.Header["NTS"] = []string{"upnp:propchange"} + req.Header["SID"] = []string{sid} + req.Header["SEQ"] = []string{"0"} + // req.Header["TRANSFER-ENCODING"] = []string{"chunked"} + // req.ContentLength = int64(bodyReader.Len()) + eventingLogger.Print(req.Header) + eventingLogger.Print("starting notify") + resp, err := http.DefaultClient.Do(req) + eventingLogger.Print("finished notify") + if err != nil { + log.Printf("Could not notify %s: %s", _url.String(), err) + continue + } + eventingLogger.Print(resp) + b, _ := ioutil.ReadAll(resp.Body) + eventingLogger.Println(string(b)) + resp.Body.Close() + } +} + +var eventingLogger = log.New(ioutil.Discard, "", 0) + +func (server *Server) contentDirectoryEventSubHandler(w http.ResponseWriter, r *http.Request) { + if server.StallEventSubscribe { + // I have an LG TV that doesn't like my eventing implementation. + // Returning unimplemented (501?) errors, results in repeat subscribe + // attempts which hits some kind of error count limit on the TV + // causing it to forcefully disconnect. It also won't work if the CDS + // service doesn't include an EventSubURL. The best thing I can do is + // cause every attempt to subscribe to timeout on the TV end, which + // reduces the error rate enough that the TV continues to operate + // without eventing. + // + // I've not found a reliable way to identify this TV, since it and + // others don't seem to include any client-identifying headers on + // SUBSCRIBE requests. + // + // TODO: Get eventing to work with the problematic TV. + t := time.Now() + <-w.(http.CloseNotifier).CloseNotify() + eventingLogger.Printf("stalled subscribe connection went away after %s", time.Since(t)) + return + } + // The following code is a work in progress. It partially implements + // the spec on eventing but hasn't been completed as I have nothing to + // test it with. + eventingLogger.Print(r.Header) + service := server.services["ContentDirectory"] + eventingLogger.Println(r.RemoteAddr, r.Method, r.Header.Get("SID")) + if r.Method == "SUBSCRIBE" && r.Header.Get("SID") == "" { + urls := upnp.ParseCallbackURLs(r.Header.Get("CALLBACK")) + eventingLogger.Println(urls) + var timeout int + fmt.Sscanf(r.Header.Get("TIMEOUT"), "Second-%d", &timeout) + eventingLogger.Println(timeout, r.Header.Get("TIMEOUT")) + sid, timeout, _ := service.Subscribe(urls, timeout) + w.Header()["SID"] = []string{sid} + w.Header()["TIMEOUT"] = []string{fmt.Sprintf("Second-%d", timeout)} + // TODO: Shouldn't have to do this to get headers logged. + w.WriteHeader(http.StatusOK) + go func() { + time.Sleep(100 * time.Millisecond) + server.contentDirectoryInitialEvent(urls, sid) + }() + } else if r.Method == "SUBSCRIBE" { + http.Error(w, "meh", http.StatusPreconditionFailed) + } else { + eventingLogger.Printf("unhandled event method: %s", r.Method) + } +} + +func (server *Server) initMux(mux *http.ServeMux) { + mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("content-type", "text/html") + err := rootTmpl.Execute(resp, struct { + Readonly bool + Path string + }{ + true, + server.RootObjectPath, + }) + if err != nil { + log.Println(err) + } + }) + mux.HandleFunc(contentDirectoryEventSubURL, server.contentDirectoryEventSubHandler) + mux.HandleFunc(iconPath, server.serveIcon) + mux.HandleFunc(resPath, func(w http.ResponseWriter, r *http.Request) { + sceneId := r.URL.Query().Get("scene") + data := XbaseGetScene(sceneId) + + filePath := filepath.Join(data.File[0].Path, data.File[0].Filename) + mimeType, err := MimeTypeByPath(filePath) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", string(mimeType)) + http.ServeFile(w, r, filePath) + return + + // if ignored, err := server.IgnorePath(filePath); err != nil { + // http.Error(w, err.Error(), http.StatusInternalServerError) + // return + // } else if ignored { + // http.Error(w, "no such object", http.StatusNotFound) + // return + // } + // k := r.URL.Query().Get("transcode") + // if k == "" { + // mimeType, err := MimeTypeByPath(filePath) + // if err != nil { + // http.Error(w, err.Error(), http.StatusInternalServerError) + // return + // } + // w.Header().Set("Content-Type", string(mimeType)) + // http.ServeFile(w, r, filePath) + // return + // } + // if server.NoTranscode { + // http.Error(w, "transcodes disabled", http.StatusNotFound) + // return + // } + // spec, ok := transcodes[k] + // if !ok { + // http.Error(w, fmt.Sprintf("bad transcode spec key: %s", k), http.StatusBadRequest) + // return + // } + // server.serveDLNATranscode(w, r, filePath, spec, k) + }) + mux.HandleFunc(rootDescPath, func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("content-type", `text/xml; charset="utf-8"`) + w.Header().Set("content-length", fmt.Sprint(len(server.rootDescXML))) + w.Header().Set("server", serverField) + w.Write(server.rootDescXML) + }) + handleSCPDs(mux) + mux.HandleFunc(serviceControlURL, server.serviceControlHandler) + mux.HandleFunc("/debug/pprof/", pprof.Index) + for i, di := range server.Icons { + mux.HandleFunc(fmt.Sprintf("%s/%d", deviceIconPath, i), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", di.Mimetype) + http.ServeContent(w, r, "", time.Time{}, di.ReadSeeker) + }) + } +} + +func (s *Server) initServices() (err error) { + urn, err := upnp.ParseServiceType(services[0].ServiceType) + if err != nil { + return + } + s.services = map[string]UPnPService{ + urn.Type: &contentDirectoryService{ + Server: s, + }, + } + return +} + +func (srv *Server) Serve() (err error) { + if err = srv.initServices(); err != nil { + return + } + srv.closed = make(chan struct{}) + if srv.FriendlyName == "" { + srv.FriendlyName = getDefaultFriendlyName() + } + if srv.HTTPConn == nil { + srv.HTTPConn, err = net.Listen("tcp", "") + if err != nil { + return + } + } + if srv.Interfaces == nil { + ifs, err := net.Interfaces() + if err != nil { + log.Print(err) + } + var tmp []net.Interface + for _, if_ := range ifs { + if if_.Flags&net.FlagUp == 0 || if_.MTU <= 0 { + continue + } + tmp = append(tmp, if_) + } + srv.Interfaces = tmp + } + if srv.FFProbeCache == nil { + srv.FFProbeCache = dummyFFProbeCache{} + } + srv.httpServeMux = http.NewServeMux() + srv.rootDeviceUUID = makeDeviceUuid(srv.FriendlyName) + srv.rootDescXML, err = xml.MarshalIndent( + upnp.DeviceDesc{ + SpecVersion: upnp.SpecVersion{Major: 1, Minor: 0}, + Device: upnp.Device{ + DeviceType: rootDeviceType, + FriendlyName: srv.FriendlyName, + Manufacturer: "Matt Joiner ", + ModelName: rootDeviceModelName, + UDN: srv.rootDeviceUUID, + ServiceList: func() (ss []upnp.Service) { + for _, s := range services { + ss = append(ss, s.Service) + } + return + }(), + IconList: func() (ret []upnp.Icon) { + for i, di := range srv.Icons { + ret = append(ret, upnp.Icon{ + Height: di.Height, + Width: di.Width, + Depth: di.Depth, + Mimetype: di.Mimetype, + URL: fmt.Sprintf("%s/%d", deviceIconPath, i), + }) + } + return + }(), + }, + }, + " ", " ") + if err != nil { + return + } + srv.rootDescXML = append([]byte(``), srv.rootDescXML...) + log.Println("HTTP srv on", srv.HTTPConn.Addr()) + srv.initMux(srv.httpServeMux) + srv.ssdpStopped = make(chan struct{}) + go func() { + srv.doSSDP() + close(srv.ssdpStopped) + }() + return srv.serveHTTP() +} + +func (srv *Server) Close() (err error) { + close(srv.closed) + err = srv.HTTPConn.Close() + <-srv.ssdpStopped + return +} + +func didl_lite(chardata string) string { + return `` + + chardata + + `` +} + +func (me *Server) location(ip net.IP) string { + url := url.URL{ + Scheme: "http", + Host: (&net.TCPAddr{ + IP: ip, + Port: me.httpPort(), + }).String(), + Path: rootDescPath, + } + return url.String() +} + +// Can return nil info with nil err if an earlier Probe gave an error. +func (srv *Server) ffmpegProbe(path string) (info *ffprobe.Info, err error) { + // We don't want relative paths in the cache. + path, err = filepath.Abs(path) + if err != nil { + return + } + fi, err := os.Stat(path) + if err != nil { + return + } + key := ffmpegInfoCacheKey{path, fi.ModTime().UnixNano()} + value, ok := srv.FFProbeCache.Get(key) + if !ok { + info, err = ffprobe.Run(path) + err = suppressFFmpegProbeDataErrors(err) + srv.FFProbeCache.Set(key, info) + return + } + info = value.(*ffprobe.Info) + return +} + +// IgnorePath detects if a file/directory should be ignored. +func (server *Server) IgnorePath(path string) (bool, error) { + if !filepath.IsAbs(path) { + return false, fmt.Errorf("Path must be absolute: %s", path) + } + if server.IgnoreHidden { + if hidden, err := isHiddenPath(path); err != nil { + return false, err + } else if hidden { + log.Print(path, " ignored: hidden") + return true, nil + } + } + if server.IgnoreUnreadable { + if readable, err := isReadablePath(path); err != nil { + return false, err + } else if !readable { + log.Print(path, " ignored: unreadable") + return true, nil + } + } + return false, nil +} + +func tryToOpenPath(path string) (bool, error) { + // Ugly but portable way to check if we can open a file/directory + if fh, err := os.Open(path); err == nil { + fh.Close() + return true, nil + } else if !os.IsPermission(err) { + return false, err + } + return false, nil +} diff --git a/dms/dlna/dms/dms_others.go b/dms/dlna/dms/dms_others.go new file mode 100644 index 000000000..a595c1ddd --- /dev/null +++ b/dms/dlna/dms/dms_others.go @@ -0,0 +1,11 @@ +//+build !unix,!windows + +package dms + +func isHiddenPath(path string) (bool, error) { + return false, nil +} + +func isReadablePath(path string) (bool, error) { + return tryToOpenPath(path) +} diff --git a/dms/dlna/dms/dms_test.go b/dms/dlna/dms/dms_test.go new file mode 100644 index 000000000..26a6f5d20 --- /dev/null +++ b/dms/dlna/dms/dms_test.go @@ -0,0 +1,66 @@ +package dms + +import ( + "bytes" + "net/http" + "runtime" + "testing" +) + +type safeFilePathTestCase struct { + root, given, expected string +} + +func TestSafeFilePath(t *testing.T) { + var cases []safeFilePathTestCase + if runtime.GOOS == "windows" { + cases = []safeFilePathTestCase{ + {"c:", "/", "c:."}, + {"c:", "/test", "c:test"}, + {"c:\\", "/", "c:\\"}, + {"c:\\", "/test", "c:\\test"}, + {"c:\\hello", "../windows", "c:\\hello\\windows"}, + {"c:\\hello", "/../windows", "c:\\hello\\windows"}, + {"c:\\hello", "/", "c:\\hello"}, + {"c:\\hello", "./world", "c:\\hello\\world"}, + {"c:\\hello", "/", "c:\\hello"}, + // These two ones are invalid but, as this actually prevents to serve them, it is fine + {"c:\\foo", "c:/windows/", "c:\\foo\\c:\\windows"}, + {"c:\\foo", "e:/", "c:\\foo\\e:"}, + } + } else { + cases = []safeFilePathTestCase{ + {"/", "..", "/"}, + {"/hello", "..//", "/hello"}, + {"", "/precious", "precious"}, + {".", "///precious", "precious"}, + } + } + t.Logf("running %d test cases", len(cases)) + for _, _case := range cases { + a := safeFilePath(_case.root, _case.given) + if a != _case.expected { + t.Errorf("expected %q from %q and %q but got %q", _case.expected, _case.root, _case.given, a) + } + } +} + +func TestRequest(t *testing.T) { + resp, err := http.NewRequest("NOTIFY", "/", nil) + if err != nil { + t.Fatal(err) + } + buf := bytes.NewBuffer(nil) + resp.Write(buf) + t.Logf("%q", buf.String()) +} + +func TestResponse(t *testing.T) { + var resp http.Response + resp.StatusCode = http.StatusOK + resp.Header = make(http.Header) + resp.Header["SID"] = []string{"uuid:1337"} + var buf bytes.Buffer + resp.Write(&buf) + t.Logf("%q", buf.String()) +} diff --git a/dms/dlna/dms/dms_unix.go b/dms/dlna/dms/dms_unix.go new file mode 100644 index 000000000..0b6a8cf24 --- /dev/null +++ b/dms/dlna/dms/dms_unix.go @@ -0,0 +1,25 @@ +//+build unix + +package dms + +import ( + "strings" + + "golang.org/x/sys/unix" +) + +func isHiddenPath(path string) (bool, error) { + return strings.Contains(path, "/."), nil +} + +func isReadablePath(path string) (bool, error) { + err := unix.Access(path, unix.R_OK) + switch err { + case nil: + return true, nil + case unix.EACCES: + return false, nil + default: + return false, err + } +} diff --git a/dms/dlna/dms/dms_unix_test.go b/dms/dlna/dms/dms_unix_test.go new file mode 100644 index 000000000..a1be6fd12 --- /dev/null +++ b/dms/dlna/dms/dms_unix_test.go @@ -0,0 +1,22 @@ +//+build unix + +package dms + +import "testing" + +func TestIsHiddenPath(t *testing.T) { + var data = map[string]bool{ + "/some/path": false, + "/some/foo.bar": false, + "/some/path/.hidden": true, + "/some/.hidden/path": true, + "/.hidden/path": true, + } + for path, expected := range data { + if actual, err := isHiddenPath(path); err != nil { + t.Errorf("isHiddenPath(%v) returned unexpected error: %s", path, err) + ] else if expected != actual { + t.Errorf("isHiddenPath(%v), expected %v, got %v", path, expected, actual) + } + } +} diff --git a/dms/dlna/dms/dms_windows.go b/dms/dlna/dms/dms_windows.go new file mode 100644 index 000000000..03b051353 --- /dev/null +++ b/dms/dlna/dms/dms_windows.go @@ -0,0 +1,35 @@ +//+build windows + +package dms + +import ( + "path/filepath" + + "golang.org/x/sys/windows" +) + +const hiddenAttributes = windows.FILE_ATTRIBUTE_HIDDEN | windows.FILE_ATTRIBUTE_SYSTEM + +func isHiddenPath(path string) (hidden bool, err error) { + if path == filepath.VolumeName(path)+"\\" { + // Volumes always have the "SYSTEM" flag, so do not even test them + return false, nil + } + winPath, err := windows.UTF16PtrFromString(path) + if err != nil { + return + } + attrs, err := windows.GetFileAttributes(winPath) + if err != nil { + return + } + if attrs&hiddenAttributes != 0 { + hidden = true + return + } + return isHiddenPath(filepath.Dir(path)) +} + +func isReadablePath(path string) (bool, error) { + return tryToOpenPath(path) +} diff --git a/dms/dlna/dms/ffmpeg.go b/dms/dlna/dms/ffmpeg.go new file mode 100644 index 000000000..aefc1ac51 --- /dev/null +++ b/dms/dlna/dms/ffmpeg.go @@ -0,0 +1,31 @@ +package dms + +import ( + "os/exec" + "runtime" + "syscall" +) + +func suppressFFmpegProbeDataErrors(_err error) (err error) { + if _err == nil { + return + } + err = _err + exitErr, ok := err.(*exec.ExitError) + if !ok { + return + } + waitStat, ok := exitErr.Sys().(syscall.WaitStatus) + if !ok { + return + } + code := waitStat.ExitStatus() + if runtime.GOOS == "windows" { + if code == -1094995529 { + err = nil + } + } else if code == 183 { + err = nil + } + return +} diff --git a/dms/dlna/dms/html.go b/dms/dlna/dms/html.go new file mode 100644 index 000000000..24e7eb82d --- /dev/null +++ b/dms/dlna/dms/html.go @@ -0,0 +1,21 @@ +package dms + +import ( + "html/template" +) + +var ( + rootTmpl *template.Template +) + +func init() { + rootTmpl = template.Must(template.New("root").Parse( + ` + Path: + + `)) +} diff --git a/dms/dlna/dms/mimetype.go b/dms/dlna/dms/mimetype.go new file mode 100644 index 000000000..281e469c2 --- /dev/null +++ b/dms/dlna/dms/mimetype.go @@ -0,0 +1,90 @@ +package dms + +import ( + "log" + "mime" + "net/http" + "os" + "path" + "strings" +) + +func init() { + if err := mime.AddExtensionType(".rmvb", "application/vnd.rn-realmedia-vbr"); err != nil { + log.Printf("Could not register application/vnd.rn-realmedia-vbr MIME type: %s", err) + } + if err := mime.AddExtensionType(".ogv", "video/ogg"); err != nil { + log.Printf("Could not register video/ogg MIME type: %s", err) + } +} + +// Example: "video/mpeg" +type mimeType string + +// IsMedia returns true for media MIME-types +func (mt mimeType) IsMedia() bool { + return mt.IsVideo() || mt.IsAudio() || mt.IsImage() +} + +// IsVideo returns true for video MIME-types +func (mt mimeType) IsVideo() bool { + return strings.HasPrefix(string(mt), "video/") || mt == "application/vnd.rn-realmedia-vbr" +} + +// IsAudio returns true for audio MIME-types +func (mt mimeType) IsAudio() bool { + return strings.HasPrefix(string(mt), "audio/") +} + +// IsImage returns true for image MIME-types +func (mt mimeType) IsImage() bool { + return strings.HasPrefix(string(mt), "image/") +} + +// Returns the group "type", the part before the '/'. +func (mt mimeType) Type() string { + return strings.SplitN(string(mt), "/", 2)[0] +} + +// Returns the string representation of this MIME-type +func (mt mimeType) String() string { + return string(mt) +} + +// MimeTypeByPath determines the MIME-type of file at the given path +func MimeTypeByPath(filePath string) (ret mimeType, err error) { + ret = mimeTypeByBaseName(path.Base(filePath)) + if ret == "" { + ret, err = mimeTypeByContent(filePath) + } + if ret == "video/x-msvideo" { + ret = "video/avi" + } else if ret == "" { + ret = "application/octet-stream" + } + return +} + +// Guess MIME-type from the extension, ignoring ".part". +func mimeTypeByBaseName(name string) mimeType { + name = strings.TrimSuffix(name, ".part") + ext := path.Ext(name) + if ext != "" { + return mimeType(mime.TypeByExtension(ext)) + } + return mimeType("") +} + +// Guess the MIME-type by analysing the first 512 bytes of the file. +func mimeTypeByContent(path string) (ret mimeType, err error) { + file, err := os.Open(path) + if err != nil { + return + } + defer file.Close() + var data [512]byte + if n, err := file.Read(data[:]); err == nil { + ret = mimeType(http.DetectContentType(data[:n])) + } + return +} diff --git a/dms/dlna/dms/xbase_link.go b/dms/dlna/dms/xbase_link.go new file mode 100644 index 000000000..29a8d52b0 --- /dev/null +++ b/dms/dlna/dms/xbase_link.go @@ -0,0 +1,95 @@ +package dms + +import ( + "time" + + "gopkg.in/resty.v1" +) + +type XbaseBase struct { + Sites []string `json:"sites"` + Actors []string `json:"actors"` + Tags []string `json:"tags"` + ReleaseGroup []string `json:"release_group"` + Volumes []XbaseVolume `json:"volumes"` +} + +type XbaseScenes struct { + Results int `json:"results"` + Scenes []XbaseScene `json:"scenes"` +} + +type XbaseScene struct { + ID int `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + SceneID string `json:"scene_id"` + Title string `json:"title"` + SceneType string `json:"scene_type"` + Studio string `json:"studio"` + Site string `json:"site"` + Tags []struct { + ID int `json:"id"` + Scenes interface{} `json:"scenes"` + Name string `json:"name"` + Clean string `json:"clean"` + Count int `json:"count"` + } `json:"tags"` + Cast []struct { + ID int `json:"id"` + Name string `json:"name"` + Count int `json:"count"` + } `json:"cast"` + Filename []struct { + ID int `json:"id"` + Name string `json:"name"` + Scenes interface{} `json:"scenes"` + } `json:"filename"` + Images []struct { + URL string `json:"url"` + Type string `json:"type"` + Orientation string `json:"orientation"` + } `json:"images"` + File []struct { + ID int `json:"ID"` + CreatedAt time.Time `json:"CreatedAt"` + UpdatedAt time.Time `json:"UpdatedAt"` + DeletedAt interface{} `json:"DeletedAt"` + Path string `json:"path"` + Filename string `json:"filename"` + Size int64 `json:"size"` + CreatedTime time.Time `json:"created_time"` + UpdatedTime time.Time `json:"updated_time"` + VideoWidth int `json:"video_width"` + VideoHeight int `json:"video_height"` + VideoBitrate uint `json:"video_bitrate"` + } `json:"file"` + Duration int `json:"duration"` + Synopsis string `json:"synopsis"` + ReleaseDate time.Time `json:"release_date"` + ReleaseDateText string `json:"release_date_text"` + CoverURL string `json:"cover_url"` + SceneURL string `json:"scene_url"` + Rating int `json:"rating"` + Favourite bool `json:"favourite"` + Wishlist bool `json:"wishlist"` + Watchlist bool `json:"watchlist"` + IsAvailable bool `json:"is_available"` + IsAccessible bool `json:"is_accessible"` +} + +type XbaseVolume struct { + Path string `json:"Path"` +} + +func XbaseGet() XbaseBase { + var data XbaseBase + resty.R().SetResult(&data).Get("http://127.0.0.1:9999/api/dms/base") + return data +} + +func XbaseGetScene(id string) XbaseScene { + var data XbaseScene + resty.R().SetResult(&data).Get("http://127.0.0.1:9999/api/dms/scene?id="+id) + return data +} diff --git a/dms/main.go b/dms/main.go new file mode 100644 index 000000000..370093dda --- /dev/null +++ b/dms/main.go @@ -0,0 +1,265 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "io/ioutil" + "log" + "net" + "os" + "os/signal" + "os/user" + "path/filepath" + "runtime" + "sync" + "syscall" + "time" + + "github.com/cld9x/xbvr/dms/dlna/dms" + "github.com/cld9x/xbvr/dms/rrcache" +) + +type dmsConfig struct { + Path string + IfName string + Http string + FriendlyName string + LogHeaders bool + FFprobeCachePath string + NoTranscode bool + NoProbe bool + StallEventSubscribe bool + NotifyInterval time.Duration + IgnoreHidden bool + IgnoreUnreadable bool +} + +func (config *dmsConfig) load(configPath string) { + file, err := os.Open(configPath) + if err != nil { + log.Printf("config error (config file: '%s'): %v\n", configPath, err) + return + } + defer file.Close() + decoder := json.NewDecoder(file) + err = decoder.Decode(&config) + if err != nil { + log.Printf("config error: %v\n", err) + return + } +} + +//default config +var config = &dmsConfig{ + Path: "", + IfName: "", + Http: ":1338", + FriendlyName: "", + LogHeaders: false, + FFprobeCachePath: getDefaultFFprobeCachePath(), +} + +func getDefaultFFprobeCachePath() (path string) { + _user, err := user.Current() + if err != nil { + log.Print(err) + return + } + path = filepath.Join(_user.HomeDir, ".dms-ffprobe-cache") + return +} + +type fFprobeCache struct { + c *rrcache.RRCache + sync.Mutex +} + +func (fc *fFprobeCache) Get(key interface{}) (value interface{}, ok bool) { + fc.Lock() + defer fc.Unlock() + return fc.c.Get(key) +} + +func (fc *fFprobeCache) Set(key interface{}, value interface{}) { + fc.Lock() + defer fc.Unlock() + var size int64 + for _, v := range []interface{}{key, value} { + b, err := json.Marshal(v) + if err != nil { + log.Printf("Could not marshal %v: %s", v, err) + continue + } + size += int64(len(b)) + } + fc.c.Set(key, value, size) +} + +func main() { + log.SetFlags(log.Ltime | log.Lshortfile) + + path := flag.String("path", config.Path, "browse root path") + ifName := flag.String("ifname", config.IfName, "specific SSDP network interface") + http := flag.String("http", config.Http, "http server port") + friendlyName := flag.String("friendlyName", config.FriendlyName, "server friendly name") + logHeaders := flag.Bool("logHeaders", config.LogHeaders, "log HTTP headers") + fFprobeCachePath := flag.String("fFprobeCachePath", config.FFprobeCachePath, "path to FFprobe cache file") + configFilePath := flag.String("config", "", "json configuration file") + flag.BoolVar(&config.NoTranscode, "noTranscode", false, "disable transcoding") + flag.BoolVar(&config.NoProbe, "noProbe", false, "disable media probing with ffprobe") + flag.BoolVar(&config.StallEventSubscribe, "stallEventSubscribe", false, "workaround for some bad event subscribers") + flag.DurationVar(&config.NotifyInterval, "notifyInterval", 30*time.Second, "interval between SSPD announces") + flag.BoolVar(&config.IgnoreHidden, "ignoreHidden", false, "ignore hidden files and directories") + flag.BoolVar(&config.IgnoreUnreadable, "ignoreUnreadable", false, "ignore unreadable files and directories") + + flag.Parse() + if flag.NArg() != 0 { + flag.Usage() + log.Fatalf("%s: %s\n", "unexpected positional arguments", flag.Args()) + } + + config.Path = *path + config.IfName = *ifName + config.Http = *http + config.FriendlyName = *friendlyName + config.LogHeaders = *logHeaders + config.FFprobeCachePath = *fFprobeCachePath + + if len(*configFilePath) > 0 { + config.load(*configFilePath) + } + + cache := &fFprobeCache{ + c: rrcache.New(64 << 20), + } + if err := cache.load(config.FFprobeCachePath); err != nil { + log.Print(err) + } + + dmsServer := &dms.Server{ + Interfaces: func(ifName string) (ifs []net.Interface) { + var err error + if ifName == "" { + ifs, err = net.Interfaces() + } else { + var if_ *net.Interface + if_, err = net.InterfaceByName(ifName) + if if_ != nil { + ifs = append(ifs, *if_) + } + } + if err != nil { + log.Fatal(err) + } + var tmp []net.Interface + for _, if_ := range ifs { + if if_.Flags&net.FlagUp == 0 || if_.MTU <= 0 { + continue + } + tmp = append(tmp, if_) + } + ifs = tmp + return + }(config.IfName), + HTTPConn: func() net.Listener { + conn, err := net.Listen("tcp", config.Http) + if err != nil { + log.Fatal(err) + } + return conn + }(), + FriendlyName: config.FriendlyName, + RootObjectPath: filepath.Clean(config.Path), + FFProbeCache: cache, + LogHeaders: config.LogHeaders, + NoTranscode: config.NoTranscode, + NoProbe: config.NoProbe, + Icons: []dms.Icon{ + dms.Icon{ + Width: 48, + Height: 48, + Depth: 8, + Mimetype: "image/png", + ReadSeeker: bytes.NewReader(MustAsset("data/VGC Sonic.png")), + }, + dms.Icon{ + Width: 128, + Height: 128, + Depth: 8, + Mimetype: "image/png", + ReadSeeker: bytes.NewReader(MustAsset("data/VGC Sonic 128.png")), + }, + }, + StallEventSubscribe: config.StallEventSubscribe, + NotifyInterval: config.NotifyInterval, + IgnoreHidden: config.IgnoreHidden, + IgnoreUnreadable: config.IgnoreUnreadable, + } + go func() { + if err := dmsServer.Serve(); err != nil { + log.Fatal(err) + } + }() + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) + <-sigs + err := dmsServer.Close() + if err != nil { + log.Fatal(err) + } + if err := cache.save(config.FFprobeCachePath); err != nil { + log.Print(err) + } +} + +func (cache *fFprobeCache) load(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + dec := json.NewDecoder(f) + var items []dms.FfprobeCacheItem + err = dec.Decode(&items) + if err != nil { + return err + } + for _, item := range items { + cache.Set(item.Key, item.Value) + } + log.Printf("added %d items from cache", len(items)) + return nil +} + +func (cache *fFprobeCache) save(path string) error { + cache.Lock() + items := cache.c.Items() + cache.Unlock() + f, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path)) + if err != nil { + return err + } + enc := json.NewEncoder(f) + err = enc.Encode(items) + f.Close() + if err != nil { + os.Remove(f.Name()) + return err + } + if runtime.GOOS == "windows" { + err = os.Remove(path) + if err == os.ErrNotExist { + err = nil + } + } + if err == nil { + err = os.Rename(f.Name(), path) + } + if err == nil { + log.Printf("saved cache with %d items", len(items)) + } else { + os.Remove(f.Name()) + } + return err +} diff --git a/dms/rrcache/rrcache.go b/dms/rrcache/rrcache.go new file mode 100644 index 000000000..1dd6711c4 --- /dev/null +++ b/dms/rrcache/rrcache.go @@ -0,0 +1,79 @@ +// Package rrcache implements a random replacement cache. Items are set with +// an associated size. When the capacity is exceeded, items will be randomly +// evicted until it is not. +package rrcache + +import ( + "math/rand" +) + +type RRCache struct { + capacity int64 + size int64 + + keys []interface{} + table map[interface{}]*entry +} + +type entry struct { + size int64 + value interface{} +} + +func New(capacity int64) *RRCache { + return &RRCache{ + capacity: capacity, + table: make(map[interface{}]*entry), + } +} + +// Returns the sum size of all items currently in the cache. +func (c *RRCache) Size() int64 { + return c.size +} + +func (c *RRCache) Set(key interface{}, value interface{}, size int64) { + if size > c.capacity { + return + } + _entry := c.table[key] + if _entry == nil { + _entry = new(entry) + c.keys = append(c.keys, key) + c.table[key] = _entry + } + sizeDelta := size - _entry.size + _entry.value = value + _entry.size = size + c.size += sizeDelta + for c.size > c.capacity { + i := rand.Intn(len(c.keys)) + key := c.keys[i] + c.keys[i] = c.keys[len(c.keys)-1] + c.keys = c.keys[:len(c.keys)-1] + c.size -= c.table[key].size + delete(c.table, key) + } +} + +func (c *RRCache) Get(key interface{}) (value interface{}, ok bool) { + entry, ok := c.table[key] + if !ok { + return + } + value = entry.value + return +} + +type Item struct { + Key, Value interface{} +} + +// Return all items currently in the cache. This is made available for +// serialization purposes. +func (c *RRCache) Items() (itens []Item) { + for k, e := range c.table { + itens = append(itens, Item{k, e.value}) + } + return +} diff --git a/dms/soap/soap.go b/dms/soap/soap.go new file mode 100644 index 000000000..5424bc855 --- /dev/null +++ b/dms/soap/soap.go @@ -0,0 +1,68 @@ +package soap + +import ( + "encoding/xml" +) + +const ( + EncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/" + EnvelopeNS = "http://schemas.xmlsoap.org/soap/envelope/" +) + +type Arg struct { + XMLName xml.Name + Value string `xml:",chardata"` +} + +type Action struct { + XMLName xml.Name + Args []Arg +} + +type Body struct { + Action []byte `xml:",innerxml"` +} + +type UPnPError struct { + XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"` + Code uint `xml:"errorCode"` + Desc string `xml:"errorDescription"` +} + +type FaultDetail struct { + XMLName xml.Name `xml:"detail"` + Data interface{} +} + +type Fault struct { + XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"` + FaultCode string `xml:"faultcode"` + FaultString string `xml:"faultstring"` + Detail FaultDetail `xml:"detail"` +} + +func NewFault(s string, detail interface{}) *Fault { + return &Fault{ + FaultCode: EnvelopeNS + ":Client", + FaultString: s, + Detail: FaultDetail{ + Data: detail, + }, + } +} + +type Envelope struct { + XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` + EncodingStyle string `xml:"encodingStyle,attr"` + Body Body `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` +} + +/* XML marshalling of nested namespaces is broken. + +func NewEnvelope(action []byte) Envelope { + return Envelope{ + EncodingStyle: EncodingStyle, + Body: Body{action}, + } +} +*/ diff --git a/dms/ssdp/ssdp.go b/dms/ssdp/ssdp.go new file mode 100644 index 000000000..5b03c709f --- /dev/null +++ b/dms/ssdp/ssdp.go @@ -0,0 +1,330 @@ +package ssdp + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "math/rand" + "net" + "net/http" + "net/textproto" + "strconv" + "strings" + "time" + + "golang.org/x/net/ipv4" +) + +const ( + AddrString = "239.255.255.250:1900" + rootDevice = "upnp:rootdevice" + aliveNTS = "ssdp:alive" + byebyeNTS = "ssdp:byebye" +) + +var ( + NetAddr *net.UDPAddr +) + +func init() { + var err error + NetAddr, err = net.ResolveUDPAddr("udp4", AddrString) + if err != nil { + log.Panicf("Could not resolve %s: %s", AddrString, err) + } +} + +type badStringError struct { + what string + str string +} + +func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) } + +func ReadRequest(b *bufio.Reader) (req *http.Request, err error) { + tp := textproto.NewReader(b) + var s string + if s, err = tp.ReadLine(); err != nil { + return nil, err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + var f []string + // TODO a split that only allows N values? + if f = strings.SplitN(s, " ", 3); len(f) < 3 { + return nil, &badStringError{"malformed request line", s} + } + if f[1] != "*" { + return nil, &badStringError{"bad URL request", f[1]} + } + req = &http.Request{ + Method: f[0], + } + var ok bool + if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(strings.TrimSpace(f[2])); !ok { + return nil, &badStringError{"malformed HTTP version", f[2]} + } + + mimeHeader, err := tp.ReadMIMEHeader() + if err != nil { + return nil, err + } + req.Header = http.Header(mimeHeader) + return +} + +type Server struct { + conn *net.UDPConn + Interface net.Interface + Server string + Services []string + Devices []string + Location func(net.IP) string + UUID string + NotifyInterval time.Duration + closed chan struct{} +} + +func makeConn(ifi net.Interface) (ret *net.UDPConn, err error) { + ret, err = net.ListenMulticastUDP("udp", &ifi, NetAddr) + if err != nil { + return + } + p := ipv4.NewPacketConn(ret) + if err := p.SetMulticastTTL(2); err != nil { + log.Println(err) + } + if err := p.SetMulticastLoopback(true); err != nil { + log.Println(err) + } + return +} + +func (me *Server) serve() { + for { + b := make([]byte, me.Interface.MTU) + n, addr, err := me.conn.ReadFromUDP(b) + select { + case <-me.closed: + return + default: + } + if err != nil { + log.Printf("error reading from UDP socket: %s", err) + break + } + go me.handle(b[:n], addr) + } +} + +func (me *Server) Init() (err error) { + me.closed = make(chan struct{}) + me.conn, err = makeConn(me.Interface) + return +} + +func (me *Server) Close() { + close(me.closed) + me.sendByeBye() + me.conn.Close() +} + +func (me *Server) Serve() (err error) { + go me.serve() + for { + addrs, err := me.Interface.Addrs() + if err != nil { + return err + } + for _, addr := range addrs { + ip := func() net.IP { + switch val := addr.(type) { + case *net.IPNet: + return val.IP + case *net.IPAddr: + return val.IP + } + panic(fmt.Sprint("unexpected addr type:", addr)) + }() + extraHdrs := [][2]string{ + {"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)}, + {"LOCATION", me.Location(ip)}, + } + me.notifyAll(aliveNTS, extraHdrs) + } + time.Sleep(me.NotifyInterval) + } +} + +func (me *Server) usnFromTarget(target string) string { + if target == me.UUID { + return target + } + return me.UUID + "::" + target +} + +func (me *Server) makeNotifyMessage(target, nts string, extraHdrs [][2]string) []byte { + lines := [...][2]string{ + {"HOST", AddrString}, + {"NT", target}, + {"NTS", nts}, + {"SERVER", me.Server}, + {"USN", me.usnFromTarget(target)}, + } + buf := &bytes.Buffer{} + fmt.Fprint(buf, "NOTIFY * HTTP/1.1\r\n") + writeHdr := func(keyValue [2]string) { + fmt.Fprintf(buf, "%s: %s\r\n", keyValue[0], keyValue[1]) + } + for _, pair := range lines { + writeHdr(pair) + } + for _, pair := range extraHdrs { + writeHdr(pair) + } + fmt.Fprint(buf, "\r\n") + return buf.Bytes() +} + +func (me *Server) send(buf []byte, addr *net.UDPAddr) { + if n, err := me.conn.WriteToUDP(buf, addr); err != nil { + log.Printf("error writing to UDP socket: %s", err) + } else if n != len(buf) { + log.Printf("short write: %d/%d bytes", n, len(buf)) + } +} + +func (me *Server) delayedSend(delay time.Duration, buf []byte, addr *net.UDPAddr) { + go func() { + select { + case <-time.After(delay): + me.send(buf, addr) + case <-me.closed: + } + }() +} + +func (me *Server) log(args ...interface{}) { + args = append([]interface{}{me.Interface.Name + ":"}, args...) + log.Print(args...) +} + +func (me *Server) sendByeBye() { + for _, type_ := range me.allTypes() { + buf := me.makeNotifyMessage(type_, byebyeNTS, nil) + me.send(buf, NetAddr) + } +} + +func (me *Server) notifyAll(nts string, extraHdrs [][2]string) { + for _, type_ := range me.allTypes() { + buf := me.makeNotifyMessage(type_, nts, extraHdrs) + delay := time.Duration(rand.Int63n(int64(100 * time.Millisecond))) + me.delayedSend(delay, buf, NetAddr) + } +} + +func (me *Server) allTypes() (ret []string) { + for _, a := range [][]string{ + {rootDevice, me.UUID}, + me.Devices, + me.Services, + } { + ret = append(ret, a...) + } + return +} + +func (me *Server) handle(buf []byte, sender *net.UDPAddr) { + req, err := ReadRequest(bufio.NewReader(bytes.NewReader(buf))) + if err != nil { + log.Println(err) + return + } + if req.Method != "M-SEARCH" || req.Header.Get("man") != `"ssdp:discover"` { + return + } + var mx uint + if req.Header.Get("Host") == AddrString { + mxHeader := req.Header.Get("mx") + i, err := strconv.ParseUint(mxHeader, 0, 0) + if err != nil { + log.Printf("Invalid mx header %q: %s", mxHeader, err) + return + } + mx = uint(i) + } else { + mx = 1 + } + types := func(st string) []string { + if st == "ssdp:all" { + return me.allTypes() + } + for _, t := range me.allTypes() { + if t == st { + return []string{t} + } + } + return nil + }(req.Header.Get("st")) + for _, ip := range func() (ret []net.IP) { + addrs, err := me.Interface.Addrs() + if err != nil { + panic(err) + } + for _, addr := range addrs { + if ip, ok := func() (net.IP, bool) { + switch data := addr.(type) { + case *net.IPNet: + if data.Contains(sender.IP) { + return data.IP, true + } + return nil, false + case *net.IPAddr: + return data.IP, true + } + panic(addr) + }(); ok { + ret = append(ret, ip) + } + } + return + }() { + for _, type_ := range types { + resp := me.makeResponse(ip, type_, req) + delay := time.Duration(rand.Int63n(int64(time.Second) * int64(mx))) + me.delayedSend(delay, resp, sender) + } + } +} + +func (me *Server) makeResponse(ip net.IP, targ string, req *http.Request) (ret []byte) { + resp := &http.Response{ + StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Request: req, + } + for _, pair := range [...][2]string{ + {"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)}, + {"EXT", ""}, + {"LOCATION", me.Location(ip)}, + {"SERVER", me.Server}, + {"ST", targ}, + {"USN", me.usnFromTarget(targ)}, + } { + resp.Header.Set(pair[0], pair[1]) + } + buf := &bytes.Buffer{} + if err := resp.Write(buf); err != nil { + panic(err) + } + return buf.Bytes() +} diff --git a/dms/transcode/transcode.go b/dms/transcode/transcode.go new file mode 100644 index 000000000..9d24decf3 --- /dev/null +++ b/dms/transcode/transcode.go @@ -0,0 +1,154 @@ +// Package transcode implements routines for transcoding to various kinds of +// receiver. +package transcode + +import ( + "fmt" + "io" + "log" + "os/exec" + "runtime" + "strconv" + "strings" + "time" + + "github.com/anacrolix/ffprobe" +) + +// Invokes an external command and returns a reader from its stdout. The +// command is waited on asynchronously. +func transcodePipe(args []string, stderr io.Writer) (r io.ReadCloser, err error) { + log.Println("transcode command:", args) + cmd := exec.Command(args[0], args[1:]...) + cmd.Stderr = stderr + r, err = cmd.StdoutPipe() + if err != nil { + return + } + err = cmd.Start() + if err != nil { + return + } + go func() { + err := cmd.Wait() + if err != nil { + log.Printf("command %s failed: %s", args, err) + } + }() + return +} + +// Return a series of ffmpeg arguments that pick specific codecs for specific +// streams. This requires use of the -map flag. +func streamArgs(s map[string]interface{}) (ret []string) { + defer func() { + if len(ret) != 0 { + ret = append(ret, []string{ + "-map", "0:" + strconv.Itoa(int(s["index"].(float64))), + }...) + } + }() + switch s["codec_type"] { + case "video": + /* + if s["codec_name"] == "h264" { + if i, _ := strconv.ParseInt(s["is_avc"], 0, 0); i != 0 { + return []string{"-vcodec", "copy", "-sameq", "-vbsf", "h264_mp4toannexb"} + } + } + */ + return []string{"-target", "pal-dvd"} + case "audio": + if s["codec_name"] == "dca" { + return []string{"-acodec", "ac3", "-ab", "224k", "-ac", "2"} + } else { + return []string{"-acodec", "copy"} + } + case "subtitle": + return []string{"-scodec", "copy"} + } + return +} + +func FormatDurationSexagesimal(d time.Duration) string { + ns := d % time.Second + d /= time.Second + s := d % 60 + d /= 60 + m := d % 60 + d /= 60 + h := d + ret := fmt.Sprintf("%d:%02d:%02d.%09d", h, m, s, ns) + ret = strings.TrimRight(ret, "0") + ret = strings.TrimRight(ret, ".") + return ret +} + +// Streams the desired file in the MPEG_PS_PAL DLNA profile. +func Transcode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { + args := []string{ + "ffmpeg", + "-threads", strconv.FormatInt(int64(runtime.NumCPU()), 10), + "-async", "1", + "-ss", FormatDurationSexagesimal(start), + } + if length >= 0 { + args = append(args, []string{ + "-t", FormatDurationSexagesimal(length), + }...) + } + args = append(args, []string{ + "-i", path, + }...) + info, err := ffprobe.Run(path) + if err != nil { + return + } + for _, s := range info.Streams { + args = append(args, streamArgs(s)...) + } + args = append(args, []string{"-f", "mpegts", "pipe:"}...) + return transcodePipe(args, stderr) +} + +// Returns a stream of Chromecast supported VP8. +func VP8Transcode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { + args := []string{ + "avconv", + "-threads", strconv.FormatInt(int64(runtime.NumCPU()), 10), + "-async", "1", + "-ss", FormatDurationSexagesimal(start), + } + if length > 0 { + args = append(args, []string{ + "-t", FormatDurationSexagesimal(length), + }...) + } + args = append(args, []string{ + "-i", path, + // "-deadline", "good", + // "-c:v", "libvpx", "-crf", "10", + "-f", "webm", + "pipe:"}...) + return transcodePipe(args, stderr) +} + +// Returns a stream of Chromecast supported matroska. +func ChromecastTranscode(path string, start, length time.Duration, stderr io.Writer) (r io.ReadCloser, err error) { + args := []string{ + "ffmpeg", + "-ss", FormatDurationSexagesimal(start), + "-i", path, + "-c:v", "libx264", "-preset", "ultrafast", "-profile:v", "high", "-level", "5.0", + "-movflags", "+faststart", + } + if length > 0 { + args = append(args, []string{ + "-t", FormatDurationSexagesimal(length), + }...) + } + args = append(args, []string{ + "-f", "mp4", + "pipe:"}...) + return transcodePipe(args, stderr) +} diff --git a/dms/upnp/eventing.go b/dms/upnp/eventing.go new file mode 100644 index 000000000..11cd88df5 --- /dev/null +++ b/dms/upnp/eventing.go @@ -0,0 +1,91 @@ +package upnp + +import ( + "crypto/rand" + "encoding/xml" + "fmt" + "io" + "log" + "net/url" + "regexp" + "time" +) + +// TODO: Why use namespace prefixes in PropertySet et al? Because the spec +// uses them, and I believe the Golang standard library XML spec implementers +// incorrectly assume that you can get away with just xmlns="". + +// propertyset is the root element sent in an event callback. +type PropertySet struct { + XMLName struct{} `xml:"e:propertyset"` + Properties []Property + // This should be set to `"urn:schemas-upnp-org:event-1-0"`. + Space string `xml:"xmlns:e,attr"` +} + +// propertys provide namespacing to the contained variables. +type Property struct { + XMLName struct{} `xml:"e:property"` + Variable Variable +} + +// Represents an evented state variable that has sendEvents="yes" in its +// service spec. +type Variable struct { + XMLName xml.Name + Value string `xml:",chardata"` +} + +type subscriber struct { + sid string + nextSeq uint32 // 0 for initial event, wraps from Uint32Max to 1. + urls []*url.URL + expiry time.Time +} + +// Intended to eventually be an embeddable implementation for managing +// eventing for a service. Not complete. +type Eventing struct { + subscribers map[string]*subscriber +} + +func (me *Eventing) Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) { + var uuid [16]byte + io.ReadFull(rand.Reader, uuid[:]) + sid = FormatUUID(uuid[:]) + if _, ok := me.subscribers[sid]; ok { + err = fmt.Errorf("already subscribed: %s", sid) + return + } + ssr := &subscriber{ + sid: sid, + urls: callback, + expiry: time.Now().Add(time.Duration(timeoutSeconds) * time.Second), + } + if me.subscribers == nil { + me.subscribers = make(map[string]*subscriber) + } + me.subscribers[sid] = ssr + actualTimeout = int(ssr.expiry.Sub(time.Now()) / time.Second) + return +} + +func (me *Eventing) Unsubscribe(sid string) error { + return nil +} + +var callbackURLRegexp = regexp.MustCompile("<(.*?)>") + +// Parse the CALLBACK HTTP header in an event subscription request. See UPnP +// Device Architecture 4.1.2. +func ParseCallbackURLs(callback string) (ret []*url.URL) { + for _, match := range callbackURLRegexp.FindAllStringSubmatch(callback, -1) { + _url, err := url.Parse(match[1]) + if err != nil { + log.Printf("bad callback url: %q", match[1]) + continue + } + ret = append(ret, _url) + } + return +} diff --git a/dms/upnp/eventing_test.go b/dms/upnp/eventing_test.go new file mode 100644 index 000000000..9104a6cf0 --- /dev/null +++ b/dms/upnp/eventing_test.go @@ -0,0 +1,42 @@ +package upnp + +import ( + "encoding/xml" + "testing" +) + +// Visually verify that property sets are marshalled correctly. +func TestMarshalPropertySet(t *testing.T) { + b, err := xml.MarshalIndent(&PropertySet{ + Properties: []Property{ + Property{ + Variable: Variable{ + XMLName: xml.Name{ + Local: "SystemUpdateID", + }, + Value: "0", + }, + }, + Property{ + Variable: Variable{ + XMLName: xml.Name{ + Local: "answerToTheUniverse", + }, + Value: "42", + }, + }, + }, + Space: "urn:schemas-upnp-org:event-1-0", + }, "", " ") + t.Log("\n" + string(b)) + if err != nil { + t.Fatal(err) + } +} + +func TestParseCallbackURLs(t *testing.T) { + urls := ParseCallbackURLs(" ") + if len(urls) != 3 { + t.Fatal(len(urls)) + } +} diff --git a/dms/upnp/upnp.go b/dms/upnp/upnp.go new file mode 100644 index 000000000..86cd5d86d --- /dev/null +++ b/dms/upnp/upnp.go @@ -0,0 +1,159 @@ +package upnp + +import ( + "encoding/xml" + "errors" + "fmt" + "log" + "regexp" + "strconv" + "strings" +) + +var serviceURNRegexp *regexp.Regexp = regexp.MustCompile(`^urn:schemas-upnp-org:service:(\w+):(\d+)$`) + +type ServiceURN struct { + Type string + Version uint64 +} + +func (me ServiceURN) String() string { + return fmt.Sprintf("urn:schemas-upnp-org:service:%s:%d", me.Type, me.Version) +} + +func ParseServiceType(s string) (ret ServiceURN, err error) { + matches := serviceURNRegexp.FindStringSubmatch(s) + if matches == nil { + err = errors.New(s) + return + } + if len(matches) != 3 { + log.Panicf("Invalid serviceURNRegexp ?") + } + ret.Type = matches[1] + ret.Version, err = strconv.ParseUint(matches[2], 0, 0) + return +} + +type SoapAction struct { + ServiceURN + Action string +} + +func ParseActionHTTPHeader(s string) (ret SoapAction, err error) { + if s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + hashIndex := strings.LastIndex(s, "#") + if hashIndex == -1 { + return + } + ret.Action = s[hashIndex+1:] + ret.ServiceURN, err = ParseServiceType(s[:hashIndex]) + return +} + +type SpecVersion struct { + Major int `xml:"major"` + Minor int `xml:"minor"` +} + +type Icon struct { + Mimetype string `xml:"mimetype"` + Width int `xml:"width"` + Height int `xml:"height"` + Depth int `xml:"depth"` + URL string `xml:"url"` +} + +type Service struct { + XMLName xml.Name `xml:"service"` + ServiceType string `xml:"serviceType"` + ServiceId string `xml:"serviceId"` + SCPDURL string + ControlURL string `xml:"controlURL"` + EventSubURL string `xml:"eventSubURL"` +} + +type Device struct { + DeviceType string `xml:"deviceType"` + FriendlyName string `xml:"friendlyName"` + Manufacturer string `xml:"manufacturer"` + ModelName string `xml:"modelName"` + UDN string + IconList []Icon `xml:"iconList>icon"` + ServiceList []Service `xml:"serviceList>service"` +} + +type DeviceDesc struct { + XMLName xml.Name `xml:"urn:schemas-upnp-org:device-1-0 root"` + SpecVersion SpecVersion `xml:"specVersion"` + Device Device `xml:"device"` +} + +type Error struct { + XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"` + Code uint `xml:"errorCode"` + Desc string `xml:"errorDescription"` +} + +func (e *Error) Error() string { + return fmt.Sprintf("%d %s", e.Code, e.Desc) +} + +const ( + InvalidActionErrorCode = 401 + ActionFailedErrorCode = 501 + ArgumentValueInvalidErrorCode = 600 +) + +var ( + InvalidActionError = Errorf(401, "Invalid Action") + ArgumentValueInvalidError = Errorf(600, "The argument value is invalid") +) + +// Errorf creates an UPNP error from the given code and description +func Errorf(code uint, tpl string, args ...interface{}) *Error { + return &Error{Code: code, Desc: fmt.Sprintf(tpl, args...)} +} + +// ConvertError converts any error to an UPNP error +func ConvertError(err error) *Error { + if err == nil { + return nil + } + if e, ok := err.(*Error); ok { + return e + } + return Errorf(ActionFailedErrorCode, err.Error()) +} + +type Action struct { + Name string + Arguments []Argument +} + +type Argument struct { + Name string + Direction string + RelatedStateVar string +} + +type SCPD struct { + XMLName xml.Name `xml:"urn:schemas-upnp-org:service-1-0 scpd"` + SpecVersion SpecVersion `xml:"specVersion"` + ActionList []Action `xml:"actionList>action"` + ServiceStateTable []StateVariable `xml:"serviceStateTable>stateVariable"` +} + +type StateVariable struct { + SendEvents string `xml:"sendEvents,attr"` + Name string `xml:"name"` + DataType string `xml:"dataType"` + AllowedValues *[]string `xml:"allowedValueList>allowedValue,omitempty"` +} + +func FormatUUID(buf []byte) string { + return fmt.Sprintf("uuid:%x-%x-%x-%x-%x", buf[:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16]) +} diff --git a/dms/upnpav/upnpav.go b/dms/upnpav/upnpav.go new file mode 100644 index 000000000..2ba7f8ade --- /dev/null +++ b/dms/upnpav/upnpav.go @@ -0,0 +1,45 @@ +package upnpav + +import ( + "encoding/xml" +) + +const ( + NoSuchObjectErrorCode = 701 +) + +type Resource struct { + XMLName xml.Name `xml:"res"` + ProtocolInfo string `xml:"protocolInfo,attr"` + URL string `xml:",chardata"` + Size uint64 `xml:"size,attr,omitempty"` + Bitrate uint `xml:"bitrate,attr,omitempty"` + Duration string `xml:"duration,attr,omitempty"` + Resolution string `xml:"resolution,attr,omitempty"` +} + +type Container struct { + Object + XMLName xml.Name `xml:"container"` + ChildCount int `xml:"childCount,attr"` +} + +type Item struct { + Object + XMLName xml.Name `xml:"item"` + Res []Resource +} + +type Object struct { + ID string `xml:"id,attr"` + ParentID string `xml:"parentID,attr"` + Restricted int `xml:"restricted,attr"` // indicates whether the object is modifiable + Class string `xml:"upnp:class"` + Icon string `xml:"upnp:icon,omitempty"` + Title string `xml:"dc:title"` + Artist string `xml:"upnp:artist,omitempty"` + Album string `xml:"upnp:album,omitempty"` + Genre string `xml:"upnp:genre,omitempty"` + AlbumArtURI string `xml:"upnp:albumArtURI,omitempty"` + Searchable int `xml:"searchable,attr"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..63d61a682 --- /dev/null +++ b/go.mod @@ -0,0 +1,103 @@ +module github.com/cld9x/xbvr + +require ( + github.com/ProtonMail/go-appdir v1.0.0 + github.com/PuerkitoBio/goquery v1.5.0 + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/UnnoTed/fileb0x v1.1.3 // indirect + github.com/anacrolix/envpprof v1.0.0 // indirect + github.com/anacrolix/ffprobe v0.0.0-20190307025918-9b483c5f7751 + github.com/anacrolix/missinggo v1.1.0 // indirect + github.com/antchfx/htmlquery v1.0.0 // indirect + github.com/antchfx/xmlquery v1.0.0 // indirect + github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e // indirect + github.com/araddon/dateparse v0.0.0-20190426192744-0d74ffceef83 + github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect + github.com/creasty/defaults v1.3.0 + github.com/davecgh/go-spew v1.1.1 + github.com/djherbis/times v1.2.0 + github.com/emicklei/go-restful v2.9.3+incompatible + github.com/emicklei/go-restful-openapi v1.0.0 + github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect + github.com/getlantern/errors v0.0.0-20180829142810-e24b7f4ff7c7 // indirect + github.com/getlantern/golog v0.0.0-20170508214112-cca714f7feb5 // indirect + github.com/getlantern/hex v0.0.0-20160523043825-083fba3033ad // indirect + github.com/getlantern/hidden v0.0.0-20160523043807-d52a649ab33a // indirect + github.com/getlantern/ops v0.0.0-20170904182230-37353306c908 // indirect + github.com/getlantern/systray v0.0.0-20190131073753-26d5b920200d + github.com/go-bindata/go-bindata v3.1.1+incompatible // indirect + github.com/go-openapi/jsonpointer v0.19.0 // indirect + github.com/go-openapi/jsonreference v0.19.0 // indirect + github.com/go-openapi/spec v0.19.0 // indirect + github.com/go-openapi/swag v0.19.0 // indirect + github.com/go-test/deep v1.0.1 + github.com/gobwas/glob v0.2.3 // indirect + github.com/gocolly/colly v1.2.0 + github.com/golang/mock v1.3.0 // indirect + github.com/google/go-cmp v0.3.0 // indirect + github.com/google/martian v2.1.0+incompatible + github.com/google/pprof v0.0.0-20190502144155-8358a9778bd1 // indirect + github.com/googleapis/gax-go v2.0.2+incompatible // indirect + github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect + github.com/gorilla/mux v1.6.2 + github.com/gowww/log v0.0.0-20170712202755-af3772f4bb12 + github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc + github.com/imdario/mergo v0.3.7 // indirect + github.com/jinzhu/gorm v1.9.5 + github.com/json-iterator/go v1.1.6 // indirect + github.com/kamilsk/retry/v4 v4.1.0 // indirect + github.com/kennygrant/sanitize v1.2.4 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/kr/pty v1.1.4 // indirect + github.com/labstack/echo v3.3.10+incompatible + github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe // indirect + github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0 + github.com/mattn/go-colorable v0.1.1 // indirect + github.com/mattn/go-isatty v0.0.7 // indirect + github.com/mattn/go-zglob v0.0.1 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/mozillazg/go-slugify v0.2.0 + github.com/mozillazg/go-unidecode v0.1.0 // indirect + github.com/muesli/smartcrop v0.3.0 // indirect + github.com/nleeper/goment v0.0.0-20190304152151-62477c661bec + github.com/oxtoacart/bpool v0.0.0-20150712133111-4e1c5567d7c2 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible + github.com/pkg/errors v0.8.1 + github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d + github.com/rs/cors v1.6.0 + github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect + github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect + github.com/simplereach/timeutils v1.2.0 // indirect + github.com/sirupsen/logrus v1.4.0 + github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e + github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect + github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea // indirect + github.com/thoas/go-funk v0.4.0 + github.com/vansante/go-ffprobe v1.0.0 + github.com/x-cray/logrus-prefixed-formatter v0.5.2 + golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 // indirect + golang.org/x/exp v0.0.0-20190509005604-ec0fef3eb65a // indirect + golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec // indirect + golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect + golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 // indirect + golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c + golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect + golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c + golang.org/x/text v0.3.2 // indirect + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect + golang.org/x/tools v0.0.0-20190509001310-e31d36578abb // indirect + google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8 // indirect + google.golang.org/grpc v1.20.1 // indirect + gopkg.in/cheggaaa/pb.v1 v1.0.27 + gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect + gopkg.in/resty.v1 v1.12.0 + gopkg.in/sourcemap.v1 v1.0.5 // indirect + gopkg.in/urfave/cli.v1 v1.20.0 + honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a // indirect + willnorris.com/go/imageproxy v0.8.1-0.20190422234945-d4246a08fdec +) diff --git a/main.go b/main.go new file mode 100644 index 000000000..e13dd43cc --- /dev/null +++ b/main.go @@ -0,0 +1,35 @@ +package main + +//go:generate fileb0x .assets.toml + +import ( + "fmt" + "github.com/cld9x/xbvr/command" + "github.com/cld9x/xbvr/xbase" + "gopkg.in/urfave/cli.v1" + "os" +) + +var version = "CURRENT" +var commit = "HEAD" +var branch = "master" +var date = "moment ago" + +func main() { + app := cli.NewApp() + app.Name = "xbvr" + app.UsageText = "xbvr command [command options]" + app.Version = fmt.Sprintf("%s (%s)", version, branch) + app.Commands = command.GetCommands() + + app.Before = func(c *cli.Context) error { + return nil + } + + app.Action = func(c *cli.Context) error { + xbase.StartServer() + return nil + } + + app.Run(os.Args) +} diff --git a/ui/.gitignore b/ui/.gitignore new file mode 100644 index 000000000..849d10a90 --- /dev/null +++ b/ui/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +node_modules +public/bundle.* diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 000000000..6163b71e8 --- /dev/null +++ b/ui/package.json @@ -0,0 +1,30 @@ +{ + "name": "svelte-app", + "version": "1.0.0", + "devDependencies": { + "npm-run-all": "^4.1.5", + "rollup": "^1.10.1", + "rollup-plugin-commonjs": "^9.3.4", + "rollup-plugin-livereload": "^1.0.0", + "rollup-plugin-node-resolve": "^4.2.3", + "rollup-plugin-svelte": "^5.0.3", + "rollup-plugin-terser": "^4.0.4", + "sirv-cli": "^0.4.0", + "svelte": "^3.0.0" + }, + "scripts": { + "build": "rollup -c", + "autobuild": "rollup -c -w", + "dev": "run-p start:dev autobuild", + "start": "sirv public", + "start:dev": "sirv public --dev" + }, + "dependencies": { + "date-fns": "1.30.1", + "ky": "0.10.0", + "svelte-routing": "1.0.0", + "svero": "0.4.3", + "videojs-hotkeys": "0.2.25", + "videojs-vr": "1.5.0" + } +} diff --git a/ui/public/index.html b/ui/public/index.html new file mode 100644 index 000000000..3aaa2816f --- /dev/null +++ b/ui/public/index.html @@ -0,0 +1,18 @@ + + + + + + + XBVR + + + + + + + + + + + \ No newline at end of file diff --git a/ui/public/static/bulma.min.css b/ui/public/static/bulma.min.css new file mode 100644 index 000000000..70f6e4450 --- /dev/null +++ b/ui/public/static/bulma.min.css @@ -0,0 +1 @@ +/*! bulma.io v0.7.4 | MIT License | github.com/jgthms/bulma */@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.breadcrumb,.button,.delete,.file,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:not(.is-arrowless)::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:center;transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.list:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:290486px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.delete::after,.delete::before,.modal-close::after,.modal-close::before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.delete::before,.modal-close::before{height:2px;width:50%}.delete::after,.modal-close::after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.delete:active,.modal-close:active{background-color:rgba(10,10,10,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.control.is-loading::after,.loader,.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img,.is-overlay,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{outline:0}.button[disabled],.file-cta[disabled],.file-name[disabled],.input[disabled],.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .button,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .input,fieldset[disabled] .pagination-ellipsis,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-previous,fieldset[disabled] .select select,fieldset[disabled] .textarea{cursor:not-allowed}/*! minireset.css v0.0.4 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}embed,iframe,img,object,video{height:auto;max-width:100%}audio{max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1087px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1088px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1280px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1472px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1087px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1088px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1280px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1472px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1087px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1088px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1280px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1472px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1087px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1088px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1280px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1472px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1087px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1088px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1280px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1472px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-background-info{background-color:#209cee!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-background-success{background-color:#23d160!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-background-danger{background-color:#ff3860!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-family-primary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-secondary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-sans-serif{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-monospace{font-family:monospace!important}.is-family-code{font-family:monospace!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1087px){.is-block-touch{display:block!important}}@media screen and (min-width:1088px){.is-block-desktop{display:block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1280px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1472px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1087px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1088px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1280px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1472px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1087px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1088px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1280px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1472px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1087px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1088px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1280px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1472px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1087px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1088px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1280px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1472px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}.is-sr-only{border:none!important;clip:rect(0,0,0,0)!important;height:.01em!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:.01em!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1087px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1088px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1280px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1472px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1087px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1088px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1280px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1472px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.375em - 1px);padding-left:.75em;padding-right:.75em;padding-top:calc(.375em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled],fieldset[disabled] .button.is-dark{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled],fieldset[disabled] .button.is-primary{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],fieldset[disabled] .button.is-primary.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em / 2));top:calc(50% - (1em / 2));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){border-radius:2px;font-size:.75rem}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1}.buttons.is-centered{justify-content:center}.buttons.is-right{justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1088px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:64px;margin-right:64px;max-width:none;width:auto}}@media screen and (max-width:1279px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1471px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1280px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1472px){.container{max-width:1344px;width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol:not([type]).is-lower-alpha{list-style-type:lower-alpha}.content ol:not([type]).is-lower-roman{list-style-type:lower-roman}.content ol:not([type]).is-upper-alpha{list-style-type:upper-alpha}.content ol:not([type]).is-upper-roman{list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.textarea{background-color:#fff;border-color:#dbdbdb;color:#363636;box-shadow:inset 0 1px 2px rgba(10,10,10,.1);max-width:100%;width:100%}.input::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.textarea[disabled],fieldset[disabled] .input,fieldset[disabled] .textarea{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input[readonly],.textarea[readonly]{box-shadow:none}.input.is-white,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.input.is-black,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.input.is-light,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.input.is-dark,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.textarea.is-medium{font-size:1.25rem}.input.is-large,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.textarea.is-inline{display:inline;width:auto}.input.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled],fieldset[disabled] .checkbox,fieldset[disabled] .radio{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled],fieldset[disabled] .select select{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder{color:rgba(122,122,122,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover)::after{border-color:#3273dc}.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover)::after{border-color:#209cee}.select.is-info select{border-color:#209cee}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#118fe4}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success:not(:hover)::after{border-color:#23d160}.select.is-success select{border-color:#23d160}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#20bc56}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning:not(:hover)::after{border-color:#ffdd57}.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover)::after{border-color:#ff3860}.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ff1f4b}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:.625em;-webkit-transform:none;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(245,245,245,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:0;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]).is-hovered,.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .input:not([disabled]).is-hovered,.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]).is-hovered,.field.has-addons .control .select select:not([disabled]):hover{z-index:2}.field.has-addons .control .button:not([disabled]).is-active,.field.has-addons .control .button:not([disabled]).is-focused,.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .input:not([disabled]).is-active,.field.has-addons .control .input:not([disabled]).is-focused,.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control .select select:not([disabled]).is-active,.field.has-addons .control .select select:not([disabled]).is-focused,.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select:not([disabled]):focus{z-index:3}.field.has-addons .control .button:not([disabled]).is-active:hover,.field.has-addons .control .button:not([disabled]).is-focused:hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .input:not([disabled]).is-active:hover,.field.has-addons .control .input:not([disabled]).is-focused:hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control .select select:not([disabled]).is-active:hover,.field.has-addons .control .select select:not([disabled]).is-focused:hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select:not([disabled]):focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:left}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress:indeterminate{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:moveIndeterminate;animation-name:moveIndeterminate;-webkit-animation-timing-function:linear;animation-timing-function:linear;background-color:#dbdbdb;background-image:linear-gradient(to right,#4a4a4a 30%,#dbdbdb 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right,#fff 30%,#dbdbdb 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right,#0a0a0a 30%,#dbdbdb 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right,#f5f5f5 30%,#dbdbdb 30%)}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate{background-image:linear-gradient(to right,#363636 30%,#dbdbdb 30%)}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-primary:indeterminate{background-image:linear-gradient(to right,#00d1b2 30%,#dbdbdb 30%)}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-link:indeterminate{background-image:linear-gradient(to right,#3273dc 30%,#dbdbdb 30%)}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-info:indeterminate{background-image:linear-gradient(to right,#209cee 30%,#dbdbdb 30%)}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-success:indeterminate{background-image:linear-gradient(to right,#23d160 30%,#dbdbdb 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-warning:indeterminate{background-image:linear-gradient(to right,#ffdd57 30%,#dbdbdb 30%)}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-danger:indeterminate{background-image:linear-gradient(to right,#ff3860 30%,#dbdbdb 30%)}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@-webkit-keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:transparent}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot{background-color:transparent}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody{background-color:transparent}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-normal{font-size:.75rem}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::after,.tag:not(body).is-delete::before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:transparent;align-items:stretch;box-shadow:0 1px 2px rgba(10,10,10,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:transparent;padding:1.5rem}.card-footer{background-color:transparent;border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:left;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width:769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:flex}}.list{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1)}.list-item{display:block;padding:.5em 1em}.list-item:not(a){color:#4a4a4a}.list-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-item:last-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-item:not(:last-child){border-bottom:1px solid #dbdbdb}.list-item.is-active{background-color:#3273dc;color:#fff}a.list-item{background-color:#f5f5f5;cursor:pointer}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}@media screen and (max-width:768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px),print{.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width:1088px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-burger{color:#363636}@media screen and (min-width:1088px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-burger{color:#f5f5f5}@media screen and (min-width:1088px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1088px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width:1088px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#4a4a4a;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;-webkit-transform-origin:center;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,-webkit-transform;transition-property:background-color,opacity,transform;transition-property:background-color,opacity,transform,-webkit-transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:nth-child(1){-webkit-transform:translateY(5px) rotate(45deg);transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){-webkit-transform:translateY(-5px) rotate(-45deg);transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#3273dc;border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1087px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1088px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{-webkit-transform:rotate(135deg) translate(.25em,-.25em);transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;-webkit-transform:translateY(0);transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));-webkit-transform:translateY(-5px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-.75rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:hover),a.navbar-item.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;padding-left:.5em;padding-right:.5em;justify-content:center;margin:.25rem;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1087px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1088px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1280px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1472px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1088px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}@media screen and (max-width:768px){.columns.is-variable.is-0-mobile{--columnGap:0rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-0-tablet{--columnGap:0rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-0-tablet-only{--columnGap:0rem}}@media screen and (max-width:1087px){.columns.is-variable.is-0-touch{--columnGap:0rem}}@media screen and (min-width:1088px){.columns.is-variable.is-0-desktop{--columnGap:0rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-0-desktop-only{--columnGap:0rem}}@media screen and (min-width:1280px){.columns.is-variable.is-0-widescreen{--columnGap:0rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-0-widescreen-only{--columnGap:0rem}}@media screen and (min-width:1472px){.columns.is-variable.is-0-fullhd{--columnGap:0rem}}.columns.is-variable.is-1{--columnGap:0.25rem}@media screen and (max-width:768px){.columns.is-variable.is-1-mobile{--columnGap:0.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-1-tablet{--columnGap:0.25rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-1-tablet-only{--columnGap:0.25rem}}@media screen and (max-width:1087px){.columns.is-variable.is-1-touch{--columnGap:0.25rem}}@media screen and (min-width:1088px){.columns.is-variable.is-1-desktop{--columnGap:0.25rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-1-desktop-only{--columnGap:0.25rem}}@media screen and (min-width:1280px){.columns.is-variable.is-1-widescreen{--columnGap:0.25rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-1-widescreen-only{--columnGap:0.25rem}}@media screen and (min-width:1472px){.columns.is-variable.is-1-fullhd{--columnGap:0.25rem}}.columns.is-variable.is-2{--columnGap:0.5rem}@media screen and (max-width:768px){.columns.is-variable.is-2-mobile{--columnGap:0.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-2-tablet{--columnGap:0.5rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-2-tablet-only{--columnGap:0.5rem}}@media screen and (max-width:1087px){.columns.is-variable.is-2-touch{--columnGap:0.5rem}}@media screen and (min-width:1088px){.columns.is-variable.is-2-desktop{--columnGap:0.5rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-2-desktop-only{--columnGap:0.5rem}}@media screen and (min-width:1280px){.columns.is-variable.is-2-widescreen{--columnGap:0.5rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-2-widescreen-only{--columnGap:0.5rem}}@media screen and (min-width:1472px){.columns.is-variable.is-2-fullhd{--columnGap:0.5rem}}.columns.is-variable.is-3{--columnGap:0.75rem}@media screen and (max-width:768px){.columns.is-variable.is-3-mobile{--columnGap:0.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-3-tablet{--columnGap:0.75rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-3-tablet-only{--columnGap:0.75rem}}@media screen and (max-width:1087px){.columns.is-variable.is-3-touch{--columnGap:0.75rem}}@media screen and (min-width:1088px){.columns.is-variable.is-3-desktop{--columnGap:0.75rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-3-desktop-only{--columnGap:0.75rem}}@media screen and (min-width:1280px){.columns.is-variable.is-3-widescreen{--columnGap:0.75rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-3-widescreen-only{--columnGap:0.75rem}}@media screen and (min-width:1472px){.columns.is-variable.is-3-fullhd{--columnGap:0.75rem}}.columns.is-variable.is-4{--columnGap:1rem}@media screen and (max-width:768px){.columns.is-variable.is-4-mobile{--columnGap:1rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-4-tablet{--columnGap:1rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-4-tablet-only{--columnGap:1rem}}@media screen and (max-width:1087px){.columns.is-variable.is-4-touch{--columnGap:1rem}}@media screen and (min-width:1088px){.columns.is-variable.is-4-desktop{--columnGap:1rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-4-desktop-only{--columnGap:1rem}}@media screen and (min-width:1280px){.columns.is-variable.is-4-widescreen{--columnGap:1rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-4-widescreen-only{--columnGap:1rem}}@media screen and (min-width:1472px){.columns.is-variable.is-4-fullhd{--columnGap:1rem}}.columns.is-variable.is-5{--columnGap:1.25rem}@media screen and (max-width:768px){.columns.is-variable.is-5-mobile{--columnGap:1.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-5-tablet{--columnGap:1.25rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-5-tablet-only{--columnGap:1.25rem}}@media screen and (max-width:1087px){.columns.is-variable.is-5-touch{--columnGap:1.25rem}}@media screen and (min-width:1088px){.columns.is-variable.is-5-desktop{--columnGap:1.25rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-5-desktop-only{--columnGap:1.25rem}}@media screen and (min-width:1280px){.columns.is-variable.is-5-widescreen{--columnGap:1.25rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-5-widescreen-only{--columnGap:1.25rem}}@media screen and (min-width:1472px){.columns.is-variable.is-5-fullhd{--columnGap:1.25rem}}.columns.is-variable.is-6{--columnGap:1.5rem}@media screen and (max-width:768px){.columns.is-variable.is-6-mobile{--columnGap:1.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-6-tablet{--columnGap:1.5rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-6-tablet-only{--columnGap:1.5rem}}@media screen and (max-width:1087px){.columns.is-variable.is-6-touch{--columnGap:1.5rem}}@media screen and (min-width:1088px){.columns.is-variable.is-6-desktop{--columnGap:1.5rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-6-desktop-only{--columnGap:1.5rem}}@media screen and (min-width:1280px){.columns.is-variable.is-6-widescreen{--columnGap:1.5rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-6-widescreen-only{--columnGap:1.5rem}}@media screen and (min-width:1472px){.columns.is-variable.is-6-fullhd{--columnGap:1.5rem}}.columns.is-variable.is-7{--columnGap:1.75rem}@media screen and (max-width:768px){.columns.is-variable.is-7-mobile{--columnGap:1.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-7-tablet{--columnGap:1.75rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-7-tablet-only{--columnGap:1.75rem}}@media screen and (max-width:1087px){.columns.is-variable.is-7-touch{--columnGap:1.75rem}}@media screen and (min-width:1088px){.columns.is-variable.is-7-desktop{--columnGap:1.75rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-7-desktop-only{--columnGap:1.75rem}}@media screen and (min-width:1280px){.columns.is-variable.is-7-widescreen{--columnGap:1.75rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-7-widescreen-only{--columnGap:1.75rem}}@media screen and (min-width:1472px){.columns.is-variable.is-7-fullhd{--columnGap:1.75rem}}.columns.is-variable.is-8{--columnGap:2rem}@media screen and (max-width:768px){.columns.is-variable.is-8-mobile{--columnGap:2rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-8-tablet{--columnGap:2rem}}@media screen and (min-width:769px) and (max-width:1087px){.columns.is-variable.is-8-tablet-only{--columnGap:2rem}}@media screen and (max-width:1087px){.columns.is-variable.is-8-touch{--columnGap:2rem}}@media screen and (min-width:1088px){.columns.is-variable.is-8-desktop{--columnGap:2rem}}@media screen and (min-width:1088px) and (max-width:1279px){.columns.is-variable.is-8-desktop-only{--columnGap:2rem}}@media screen and (min-width:1280px){.columns.is-variable.is-8-widescreen{--columnGap:2rem}}@media screen and (min-width:1280px) and (max-width:1471px){.columns.is-variable.is-8-widescreen-only{--columnGap:2rem}}@media screen and (min-width:1472px){.columns.is-variable.is-8-fullhd{--columnGap:2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1087px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1087px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:rgba(245,245,245,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1087px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(245,245,245,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1087px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}.section{padding:3rem 1.5rem}@media screen and (min-width:1088px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem} \ No newline at end of file diff --git a/ui/public/static/video-js.css b/ui/public/static/video-js.css new file mode 100644 index 000000000..8aadce67b --- /dev/null +++ b/ui/public/static/video-js.css @@ -0,0 +1,1386 @@ +.video-js .vjs-big-play-button .vjs-icon-placeholder:before, .vjs-button > .vjs-icon-placeholder:before, .video-js .vjs-modal-dialog, .vjs-modal-dialog .vjs-modal-dialog-content { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } + +.video-js .vjs-big-play-button .vjs-icon-placeholder:before, .vjs-button > .vjs-icon-placeholder:before { + text-align: center; } + +@font-face { + font-family: VideoJS; + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAABBIAAsAAAAAGoQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPgAAAFZRiV3RY21hcAAAAYQAAADQAAADIjn098ZnbHlmAAACVAAACv4AABEIAwnSw2hlYWQAAA1UAAAAKwAAADYV1OgpaGhlYQAADYAAAAAbAAAAJA4DByFobXR4AAANnAAAAA8AAACE4AAAAGxvY2EAAA2sAAAARAAAAEQ9NEHGbWF4cAAADfAAAAAfAAAAIAEyAIFuYW1lAAAOEAAAASUAAAIK1cf1oHBvc3QAAA84AAABDwAAAZ5AAl/0eJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGQ7xTiBgZWBgaWQ5RkDA8MvCM0cwxDOeI6BgYmBlZkBKwhIc01hcPjI+FGBHcRdyA4RZgQRAC4HCwEAAHic7dFprsIgAEXhg8U61XmeWcBb1FuQP4w7ZQXK5boMm3yclFDSANAHmuKviBBeBPQ8ymyo8w3jOh/5r2ui5nN6v8sYNJb3WMdeWRvLji0DhozKdxM6psyYs2DJijUbtuzYc+DIiTMXrty4k8oGLb+n0xCe37ekM7Z66j1DbUy3l6PpHnLfdLO5NdSBoQ4NdWSoY9ON54mhdqa/y1NDnRnq3FAXhro01JWhrg11Y6hbQ90Z6t5QD4Z6NNSToZ4N9WKoV0O9GerdUJORPqkhTd54nJ1YDXBU1RV+576/JBs2bPYPkrDZt5vsJrv53V/I5mclhGDCTwgGBQQSTEji4hCkYIAGd4TGIWFAhV0RQTpWmQp1xv6hA4OTOlNr2zFANbHUYbq2OtNCpViRqsk+e+7bTQAhzti8vPfuPffcc88959zznbcMMPjHD/KDDGEY0ABpYX384NhlomIYlo4JISGEY9mMh2FSidYiqkEUphtNYDSY/dXg9023l4DdxlqUl0chuZRhncJKrsCQHIwcGuwfnhMIzBnuH4Sym+1D2zaGjheXlhYfD238z80mKYMmvJ5XeOTzd8z9eujbMxJNhu4C9xPE/bCMiDuSNIWgkTQwBE55hLSAE7ZwhrHLnAHZOGV/kmBGTiNjZxzI77Hb7Hqjz68TjT6vh+5JT/cCIkqS0D6CqPf5jX4Qjdx5j6vlDfZM4aZFdbVXIxtOlJaP/WottMnH6CJQ3bTiue3PrY23HjnChtuamxwvvzFjxkPrNj3z0tG9T561HDYf6OgmRWvlY3JQHoQb8ltV2Yet7YfWctEjR1AtxS/cSX6U4alf6NJEBQ7YKg9wrXQKd0IeZCb2ux75Uhh1Un+Nz+9LTOE7PK777nN5xqdTneTBhCbx446mZrhnUkrCz2YhA9dSMxaG0SYmT8hi9ZPu1E94PJYQSH6LRmhxec7Q7ZeXntgQuVpbh+a4qWNsckVyTdn0P7o7DpgPW84+uRcq0BITflBikGdUjAZ9wYBVI3mtrNvr9kpg1UsaK6t3690aoorC1lg0GpMH2HAMtkZjsSi5Ig9ESVosOh7GQfLjKNLvKpMKkLSKNFAka710GdgSi8oDMSoNhqjkKBXTgn3swtaxyzGkUzIzae9RtLdWkSlZ1KDX6EzgllzV4NV4SoDFSOGD4+HCeQUF8wrZ5Hs8zIb5EaVxy8DYFTbMCJPnLIWZxugZE2NlivC0gc1qEQUR8jEKgZcAXeH18BiCgl5nlHh0CrjB4Hb5fX4gb0J7c9PuHVsfgkx2n/vTY/JV8kn8PGxf7faOZ8qX8JVByuIf4whk9sqXli2hvPJV9hrp0hY7l8r2x37ydaVsb4xvXv/47v2NjfCl8m5oRDJclFMoE1yk0Uh1Te4/m8lFXe9qBZD0EkheicebXvzI2PLCuoKCukLuhPIeKwaHPEouxw3kMqaIUXDQ1p0mip+MyCORSCQaoUsnY1VZ38nUTrG21WvVo4f1OsEJFhvSfAFwGfT8VHRMeAVUpwLOoLzjT/REIj3O3FhuURE+nERF+0pTId5Fyxv5sfwGyg4O+my4vZv0sZm7oeQlFZORiB+tG0MweVNraeitl7yxiPIHTk4/diVxs94o5lEYishB2iAtkchEnsActoEpx44Fo8XnsQMaA22BlqC20RmhBKzYojZyYaxg+JggMc4HHY2m+L9EkWSYljirOisrO7d3VorxzyZ6Vc4lJqITAu1b2wOBdrLElAP+bFc2eGaZFVbkmJktv5uT6Jlz5D/MnBFor6ig/JPnRViBsV3LNKGGqB1ChJ0tgQywlVLFJIuQgTFttwkiKxhyQdAZMdMYtSaoAewqfvXVYPAbDT6/1mez85YS8FSDywQ6NfAnef6FNEGMilnppyvn5rB6tTyq1pOceRWnp2WJEZFXHeX5oyoem1nTTgdqc4heDY7bOeKz63vnz+/dRx+s31Ht2JGanQ5seirfWJL9tjozU/12TnEjn5oux9OzU3ckGbBzBwNOyk69JykKH0n/0LM9A72tuwM3zQpIRu4AxiToseEpgPOmbROyFe9/X2yeUvoUsCyEvjcgs7fpWP3/aKlFN0+6HFUe6D9HFz/XPwBlN9tTqNyZjFJ8UO2RUT5/h4CptCctEyeisnOyXjALEp7dXKaQKf6O7IMnGjNNACRMLxqdYJX8eMLvmmd68D+ayBLyKKYZwYxDt/GNhzETDJ05Qxlyi3pi3/Z93ndYVSumgj0V/KkIFlO6+1K3fF2+3g0q+YtuSIf0bvmLqV09nnobI6hwcjIP8aPCKayjsF5JBY3LaKAeRLSyYB1h81oTwe9SlPMkXB7G0mfL9q71gaqqwPqu67QRKS1+ObTx+sbQy9QV2OQHEScGkdFBeT7v7qisqqrs6N52i78/R+6S0qQONVj26agOVoswCyQWIV5D86vH53bxNUeXV0K+XZaHv/nm/KsHhOvylwsWnJX/HE8l/4WCv5x+l5n08z6UU8bUMa3MBpSmM7F63AxntdC9eBCKEZW9Hr+ABNqtxgAQrSbMtmrW7lKQuoSgBhSrTazWVU2QAKWY8wiiuhqFmQgWJBgoXiuWIm42N7hqZbBsgXz52O5P5uSvaNgFGnOuvsRw8I8Laha91wMvDuxqWFheN7/8GVtTltdS83DQsXRmqc5ZtcJXEVrlV2doTWk5+Yunm71dG5f55m/qY0MjI93vv9/NfpxXV9sUXrxy2fbNy1or65cOlDRnOoKFeeXcbw42H/bNDT5Qs3flgs31gWC1lD1nfUV/X7NdCnSUdHY2e8afzfKsqZ5ZljfDqjLOmk3UebNXB+aHArPYDRs+/HDDxeT5DiP+sFg7OpRaVQMGBV89PpeBdj22hCE0Uub0UqwLrNWsG0cuyadgLXTeR5rbO4+3c/vl15cur2nRq+TXCQDcS3SO+s6ak+e5/eMS+1dw3btu3YG2tvFL8XdIZvdjdW6TO/4B7IdrZWVPmctm5/59AgsPItTSbCiIBr2OqIGzmu20SMKAS7yqwGBUfGfgjDYlLLDeF0SfcLB2LSx8flT+08/kzz6yOj96rft4rpTjdPQcmLd47uKibbDq7ZSz/XtbH2nN717Nd62rU+c8Icevvv7I09wA6WvjVcafb+FsbNG+ZQ80Rn6ZZsvrP7teP2dzTdoETvNhjCmsr8FID2sJ69VYvdUcxk4AzYRlKcaE38eXNRlfW9H1as9i6acLHp1XpuNB5K7DIvkX08y1ZYvh3KfWaiCzH+ztrSDmD7LuX73x/mJelB8Yj39t8nhNQJJ2CAthpoFGLsGgtSOCJooCGoaJAMTjSWHVZ08YAa1Fg9lPI5U6DOsGVjDasJeZZ+YyhfCwfOzCxlBA69M9XLXtza7H/rav+9Tjq5xNi0wpKQIRNO4Lrzz7yp5QVYM6Jd/oc1Uvn/mQhhuWh6ENXoS2YTZ8QT42bF5d/559zp5r0Uff2VnR2tdf2/WCOd2cO0Mw6qpWPnvxpV0nrt5fZd2yItc199GWe8vlNfNDq+CH/7yAAnB9hn7T4QO4c1g9ScxsZgmzntnE/IDGndtHMw69lFwoCnYsMGx+rBp8JSBqdLzBr9QRPq/PbhWMWFtQZp1xguy/haw3TEHm3TWAnxFWQQWgt7M5OV0lCz1VRYucpWliy7z6Zd4urwPIyeZQqli2Lgg7szJV09PysATbOQtYIrB2YzbkJYkGgJ0m4AjPUap1pvYu1K9qr97z0Yl3p332b2LYB78ncYIlRkau/8GObSsOlZancACE5d5ily+c2+7h5Yj4lqhVmXXB+iXLfvdqSgqfKtQvfHDV0OnvQR1qhw42XS/vkvsh/hXcrDFP0a+SJNIomEfD1nsrYGO+1bgTOJhM8Hv6ek+7vVglxuSRwoKn17S937bm6YJCeSSG0Op1n+7tE37tcZ/p7dsTv4EUrGpDbWueKigsLHhqTVsoEj+JU0kaSjnj9tz8/gryQWwJ9BcJXBC/7smO+I/IFURJetFPrdt5WcoL6DbEJaygI8CTHfQTjf40ofD+DwalTqIAAHicY2BkYGAA4gDud4bx/DZfGbjZGUDg+q1z05BpdkawOAcDE4gCAB45CXEAeJxjYGRgYGcAARD5/z87IwMjAypQBAAtgwI4AHicY2BgYGAfYAwAOkQA4QAAAAAAAA4AaAB+AMwA4AECAUIBbAGYAcICGAJYArQC4AMwA7AD3gQwBJYE3AUkBWYFigYgBmYGtAbqB1gIEghYCG4IhHicY2BkYGBQZChlYGcAASYg5gJCBob/YD4DABfTAbQAeJxdkE1qg0AYhl8Tk9AIoVDaVSmzahcF87PMARLIMoFAl0ZHY1BHdBJIT9AT9AQ9RQ9Qeqy+yteNMzDzfM+88w0K4BY/cNAMB6N2bUaPPBLukybCLvleeAAPj8JD+hfhMV7hC3u4wxs7OO4NzQSZcI/8Ltwnfwi75E/hAR7wJTyk/xYeY49fYQ/PztM+jbTZ7LY6OWdBJdX/pqs6NYWa+zMxa13oKrA6Uoerqi/JwtpYxZXJ1coUVmeZUWVlTjq0/tHacjmdxuL90OR8O0UEDYMNdtiSEpz5XQGqzlm30kzUdAYFFOb8R7NOZk0q2lwAyz1i7oAr1xoXvrOgtYhZx8wY5KRV269JZ5yGpmzPTjQhvY9je6vEElPOuJP3mWKnP5M3V+YAAAB4nG2PyXLCMBBE3YCNDWEL2ffk7o8S8oCnkCVHC5C/jzBQlUP6IHVPzYyekl5y0iL5X5/ooY8BUmQYIkeBEca4wgRTzDDHAtdY4ga3uMM9HvCIJzzjBa94wzs+8ImvZNAq8TM+HqVkKxWlrQiOxjujQkNlEzyNzl6Z/cU2XF06at7U83VQyklLpEvSnuzsb+HAPnPfQVgaupa1Jlu4sPLsFblcitaz0dHU0ZF1qatjZ1+aTXYCmp6u0gSvWNPyHLtFZ+ZeXWVSaEkqs3T8S74WklbGbNNNq4LL4+CWKtZDv2cfX8l8aFbKFhEnJnJ+IULFpqwoQnNHlHaVQtPBl+ypmbSWdmyC61KS/AKZC3Y+AA==) format("woff"); + font-weight: normal; + font-style: normal; } + +.vjs-icon-play, .video-js .vjs-big-play-button .vjs-icon-placeholder:before, .video-js .vjs-play-control .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-play:before, .video-js .vjs-big-play-button .vjs-icon-placeholder:before, .video-js .vjs-play-control .vjs-icon-placeholder:before { + content: "\f101"; } + +.vjs-icon-play-circle { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-play-circle:before { + content: "\f102"; } + +.vjs-icon-pause, .video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-pause:before, .video-js .vjs-play-control.vjs-playing .vjs-icon-placeholder:before { + content: "\f103"; } + +.vjs-icon-volume-mute, .video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-volume-mute:before, .video-js .vjs-mute-control.vjs-vol-0 .vjs-icon-placeholder:before { + content: "\f104"; } + +.vjs-icon-volume-low, .video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-volume-low:before, .video-js .vjs-mute-control.vjs-vol-1 .vjs-icon-placeholder:before { + content: "\f105"; } + +.vjs-icon-volume-mid, .video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-volume-mid:before, .video-js .vjs-mute-control.vjs-vol-2 .vjs-icon-placeholder:before { + content: "\f106"; } + +.vjs-icon-volume-high, .video-js .vjs-mute-control .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-volume-high:before, .video-js .vjs-mute-control .vjs-icon-placeholder:before { + content: "\f107"; } + +.vjs-icon-fullscreen-enter, .video-js .vjs-fullscreen-control .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-fullscreen-enter:before, .video-js .vjs-fullscreen-control .vjs-icon-placeholder:before { + content: "\f108"; } + +.vjs-icon-fullscreen-exit, .video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-fullscreen-exit:before, .video-js.vjs-fullscreen .vjs-fullscreen-control .vjs-icon-placeholder:before { + content: "\f109"; } + +.vjs-icon-square { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-square:before { + content: "\f10a"; } + +.vjs-icon-spinner { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-spinner:before { + content: "\f10b"; } + +.vjs-icon-subtitles, .video-js .vjs-subtitles-button .vjs-icon-placeholder, .video-js .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-subtitles:before, .video-js .vjs-subtitles-button .vjs-icon-placeholder:before, .video-js .vjs-subs-caps-button .vjs-icon-placeholder:before, + .video-js.video-js:lang(en-GB) .vjs-subs-caps-button .vjs-icon-placeholder:before, + .video-js.video-js:lang(en-IE) .vjs-subs-caps-button .vjs-icon-placeholder:before, + .video-js.video-js:lang(en-AU) .vjs-subs-caps-button .vjs-icon-placeholder:before, + .video-js.video-js:lang(en-NZ) .vjs-subs-caps-button .vjs-icon-placeholder:before { + content: "\f10c"; } + +.vjs-icon-captions, .video-js .vjs-captions-button .vjs-icon-placeholder, .video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder, +.video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-captions:before, .video-js .vjs-captions-button .vjs-icon-placeholder:before, .video-js:lang(en) .vjs-subs-caps-button .vjs-icon-placeholder:before, + .video-js:lang(fr-CA) .vjs-subs-caps-button .vjs-icon-placeholder:before { + content: "\f10d"; } + +.vjs-icon-chapters, .video-js .vjs-chapters-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-chapters:before, .video-js .vjs-chapters-button .vjs-icon-placeholder:before { + content: "\f10e"; } + +.vjs-icon-share { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-share:before { + content: "\f10f"; } + +.vjs-icon-cog { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-cog:before { + content: "\f110"; } + +.vjs-icon-circle, .video-js .vjs-play-progress, .video-js .vjs-volume-level, .vjs-seek-to-live-control .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-circle:before, .video-js .vjs-play-progress:before, .video-js .vjs-volume-level:before, .vjs-seek-to-live-control .vjs-icon-placeholder:before { + content: "\f111"; } + +.vjs-icon-circle-outline { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-circle-outline:before { + content: "\f112"; } + +.vjs-icon-circle-inner-circle { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-circle-inner-circle:before { + content: "\f113"; } + +.vjs-icon-hd { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-hd:before { + content: "\f114"; } + +.vjs-icon-cancel, .video-js .vjs-control.vjs-close-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-cancel:before, .video-js .vjs-control.vjs-close-button .vjs-icon-placeholder:before { + content: "\f115"; } + +.vjs-icon-replay, .video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-replay:before, .video-js .vjs-play-control.vjs-ended .vjs-icon-placeholder:before { + content: "\f116"; } + +.vjs-icon-facebook { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-facebook:before { + content: "\f117"; } + +.vjs-icon-gplus { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-gplus:before { + content: "\f118"; } + +.vjs-icon-linkedin { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-linkedin:before { + content: "\f119"; } + +.vjs-icon-twitter { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-twitter:before { + content: "\f11a"; } + +.vjs-icon-tumblr { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-tumblr:before { + content: "\f11b"; } + +.vjs-icon-pinterest { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-pinterest:before { + content: "\f11c"; } + +.vjs-icon-audio-description, .video-js .vjs-descriptions-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-audio-description:before, .video-js .vjs-descriptions-button .vjs-icon-placeholder:before { + content: "\f11d"; } + +.vjs-icon-audio, .video-js .vjs-audio-button .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-audio:before, .video-js .vjs-audio-button .vjs-icon-placeholder:before { + content: "\f11e"; } + +.vjs-icon-next-item { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-next-item:before { + content: "\f11f"; } + +.vjs-icon-previous-item { + font-family: VideoJS; + font-weight: normal; + font-style: normal; } + .vjs-icon-previous-item:before { + content: "\f120"; } + +.video-js { + display: block; + vertical-align: top; + box-sizing: border-box; + color: #fff; + background-color: #000; + position: relative; + padding: 0; + font-size: 10px; + line-height: 1; + font-weight: normal; + font-style: normal; + font-family: Arial, Helvetica, sans-serif; + word-break: initial; } + .video-js:-moz-full-screen { + position: absolute; } + .video-js:-webkit-full-screen { + width: 100% !important; + height: 100% !important; } + +.video-js[tabindex="-1"] { + outline: none; } + +.video-js *, +.video-js *:before, +.video-js *:after { + box-sizing: inherit; } + +.video-js ul { + font-family: inherit; + font-size: inherit; + line-height: inherit; + list-style-position: outside; + margin-left: 0; + margin-right: 0; + margin-top: 0; + margin-bottom: 0; } + +.video-js.vjs-fluid, +.video-js.vjs-16-9, +.video-js.vjs-4-3 { + width: 100%; + max-width: 100%; + height: 0; } + +.video-js.vjs-16-9 { + padding-top: 56.25%; } + +.video-js.vjs-4-3 { + padding-top: 75%; } + +.video-js.vjs-fill { + width: 100%; + height: 100%; } + +.video-js .vjs-tech { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } + +body.vjs-full-window { + padding: 0; + margin: 0; + height: 100%; } + +.vjs-full-window .video-js.vjs-fullscreen { + position: fixed; + overflow: hidden; + z-index: 1000; + left: 0; + top: 0; + bottom: 0; + right: 0; } + +.video-js.vjs-fullscreen { + width: 100% !important; + height: 100% !important; + padding-top: 0 !important; } + +.video-js.vjs-fullscreen.vjs-user-inactive { + cursor: none; } + +.vjs-hidden { + display: none !important; } + +.vjs-disabled { + opacity: 0.5; + cursor: default; } + +.video-js .vjs-offscreen { + height: 1px; + left: -9999px; + position: absolute; + top: 0; + width: 1px; } + +.vjs-lock-showing { + display: block !important; + opacity: 1; + visibility: visible; } + +.vjs-no-js { + padding: 20px; + color: #fff; + background-color: #000; + font-size: 18px; + font-family: Arial, Helvetica, sans-serif; + text-align: center; + width: 300px; + height: 150px; + margin: 0px auto; } + +.vjs-no-js a, +.vjs-no-js a:visited { + color: #66A8CC; } + +.video-js .vjs-big-play-button { + font-size: 3em; + line-height: 1.5em; + height: 1.63332em; + width: 3em; + display: block; + position: absolute; + top: 10px; + left: 10px; + padding: 0; + cursor: pointer; + opacity: 1; + border: 0.06666em solid #fff; + background-color: #2B333F; + background-color: rgba(43, 51, 63, 0.7); + border-radius: 0.3em; + transition: all 0.4s; } + +.vjs-big-play-centered .vjs-big-play-button { + top: 50%; + left: 50%; + margin-top: -0.81666em; + margin-left: -1.5em; } + +.video-js:hover .vjs-big-play-button, +.video-js .vjs-big-play-button:focus { + border-color: #fff; + background-color: #73859f; + background-color: rgba(115, 133, 159, 0.5); + transition: all 0s; } + +.vjs-controls-disabled .vjs-big-play-button, +.vjs-has-started .vjs-big-play-button, +.vjs-using-native-controls .vjs-big-play-button, +.vjs-error .vjs-big-play-button { + display: none; } + +.vjs-has-started.vjs-paused.vjs-show-big-play-button-on-pause .vjs-big-play-button { + display: block; } + +.video-js button { + background: none; + border: none; + color: inherit; + display: inline-block; + font-size: inherit; + line-height: inherit; + text-transform: none; + text-decoration: none; + transition: none; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } + +.vjs-control .vjs-button { + width: 100%; + height: 100%; } + +.video-js .vjs-control.vjs-close-button { + cursor: pointer; + height: 3em; + position: absolute; + right: 0; + top: 0.5em; + z-index: 2; } + +.video-js .vjs-modal-dialog { + background: rgba(0, 0, 0, 0.8); + background: linear-gradient(180deg, rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0)); + overflow: auto; } + +.video-js .vjs-modal-dialog > * { + box-sizing: border-box; } + +.vjs-modal-dialog .vjs-modal-dialog-content { + font-size: 1.2em; + line-height: 1.5; + padding: 20px 24px; + z-index: 1; } + +.vjs-menu-button { + cursor: pointer; } + +.vjs-menu-button.vjs-disabled { + cursor: default; } + +.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu { + display: none; } + +.vjs-menu .vjs-menu-content { + display: block; + padding: 0; + margin: 0; + font-family: Arial, Helvetica, sans-serif; + overflow: auto; } + +.vjs-menu .vjs-menu-content > * { + box-sizing: border-box; } + +.vjs-scrubbing .vjs-control.vjs-menu-button:hover .vjs-menu { + display: none; } + +.vjs-menu li { + list-style: none; + margin: 0; + padding: 0.2em 0; + line-height: 1.4em; + font-size: 1.2em; + text-align: center; + text-transform: lowercase; } + +.vjs-menu li.vjs-menu-item:focus, +.vjs-menu li.vjs-menu-item:hover, +.js-focus-visible .vjs-menu li.vjs-menu-item:hover { + background-color: #73859f; + background-color: rgba(115, 133, 159, 0.5); } + +.vjs-menu li.vjs-selected, +.vjs-menu li.vjs-selected:focus, +.vjs-menu li.vjs-selected:hover, +.js-focus-visible .vjs-menu li.vjs-selected:hover { + background-color: #fff; + color: #2B333F; } + +.vjs-menu li.vjs-menu-title { + text-align: center; + text-transform: uppercase; + font-size: 1em; + line-height: 2em; + padding: 0; + margin: 0 0 0.3em 0; + font-weight: bold; + cursor: default; } + +.vjs-menu-button-popup .vjs-menu { + display: none; + position: absolute; + bottom: 0; + width: 10em; + left: -3em; + height: 0em; + margin-bottom: 1.5em; + border-top-color: rgba(43, 51, 63, 0.7); } + +.vjs-menu-button-popup .vjs-menu .vjs-menu-content { + background-color: #2B333F; + background-color: rgba(43, 51, 63, 0.7); + position: absolute; + width: 100%; + bottom: 1.5em; + max-height: 15em; } + +.vjs-layout-tiny .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-x-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 5em; } + +.vjs-layout-small .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 10em; } + +.vjs-layout-medium .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 14em; } + +.vjs-layout-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-x-large .vjs-menu-button-popup .vjs-menu .vjs-menu-content, +.vjs-layout-huge .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + max-height: 25em; } + +.vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu, +.vjs-menu-button-popup .vjs-menu.vjs-lock-showing { + display: block; } + +.video-js .vjs-menu-button-inline { + transition: all 0.4s; + overflow: hidden; } + +.video-js .vjs-menu-button-inline:before { + width: 2.222222222em; } + +.video-js .vjs-menu-button-inline:hover, +.video-js .vjs-menu-button-inline:focus, +.video-js .vjs-menu-button-inline.vjs-slider-active, +.video-js.vjs-no-flex .vjs-menu-button-inline { + width: 12em; } + +.vjs-menu-button-inline .vjs-menu { + opacity: 0; + height: 100%; + width: auto; + position: absolute; + left: 4em; + top: 0; + padding: 0; + margin: 0; + transition: all 0.4s; } + +.vjs-menu-button-inline:hover .vjs-menu, +.vjs-menu-button-inline:focus .vjs-menu, +.vjs-menu-button-inline.vjs-slider-active .vjs-menu { + display: block; + opacity: 1; } + +.vjs-no-flex .vjs-menu-button-inline .vjs-menu { + display: block; + opacity: 1; + position: relative; + width: auto; } + +.vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu, +.vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu, +.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu { + width: auto; } + +.vjs-menu-button-inline .vjs-menu-content { + width: auto; + height: 100%; + margin: 0; + overflow: hidden; } + +.video-js .vjs-control-bar { + display: none; + width: 100%; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3.0em; + background-color: #2B333F; + background-color: rgba(43, 51, 63, 0.7); } + +.vjs-has-started .vjs-control-bar { + display: flex; + visibility: visible; + opacity: 1; + transition: visibility 0.1s, opacity 0.1s; } + +.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar { + visibility: visible; + opacity: 0; + transition: visibility 1s, opacity 1s; } + +.vjs-controls-disabled .vjs-control-bar, +.vjs-using-native-controls .vjs-control-bar, +.vjs-error .vjs-control-bar { + display: none !important; } + +.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar { + opacity: 1; + visibility: visible; } + +.vjs-has-started.vjs-no-flex .vjs-control-bar { + display: table; } + +.video-js .vjs-control { + position: relative; + text-align: center; + margin: 0; + padding: 0; + height: 100%; + width: 4em; + flex: none; } + +.vjs-button > .vjs-icon-placeholder:before { + font-size: 1.8em; + line-height: 1.67; } + +.video-js .vjs-control:focus:before, +.video-js .vjs-control:hover:before, +.video-js .vjs-control:focus { + text-shadow: 0em 0em 1em white; } + +.video-js .vjs-control-text { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } + +.vjs-no-flex .vjs-control { + display: table-cell; + vertical-align: middle; } + +.video-js .vjs-custom-control-spacer { + display: none; } + +.video-js .vjs-progress-control { + cursor: pointer; + flex: auto; + display: flex; + align-items: center; + min-width: 4em; + touch-action: none; } + +.video-js .vjs-progress-control.disabled { + cursor: default; } + +.vjs-live .vjs-progress-control { + display: none; } + +.vjs-liveui .vjs-progress-control { + display: flex; + align-items: center; } + +.vjs-no-flex .vjs-progress-control { + width: auto; } + +.video-js .vjs-progress-holder { + flex: auto; + transition: all 0.2s; + height: 0.3em; } + +.video-js .vjs-progress-control .vjs-progress-holder { + margin: 0 10px; } + +.video-js .vjs-progress-control:hover .vjs-progress-holder { + font-size: 1.666666666666666666em; } + +.video-js .vjs-progress-control:hover .vjs-progress-holder.disabled { + font-size: 1em; } + +.video-js .vjs-progress-holder .vjs-play-progress, +.video-js .vjs-progress-holder .vjs-load-progress, +.video-js .vjs-progress-holder .vjs-load-progress div { + position: absolute; + display: block; + height: 100%; + margin: 0; + padding: 0; + width: 0; } + +.video-js .vjs-play-progress { + background-color: #fff; } + .video-js .vjs-play-progress:before { + font-size: 0.9em; + position: absolute; + right: -0.5em; + top: -0.333333333333333em; + z-index: 1; } + +.video-js .vjs-load-progress { + background: rgba(115, 133, 159, 0.5); } + +.video-js .vjs-load-progress div { + background: rgba(115, 133, 159, 0.75); } + +.video-js .vjs-time-tooltip { + background-color: #fff; + background-color: rgba(255, 255, 255, 0.8); + border-radius: 0.3em; + color: #000; + float: right; + font-family: Arial, Helvetica, sans-serif; + font-size: 1em; + padding: 6px 8px 8px 8px; + pointer-events: none; + position: absolute; + top: -3.4em; + visibility: hidden; + z-index: 1; } + +.video-js .vjs-progress-holder:focus .vjs-time-tooltip { + display: none; } + +.video-js .vjs-progress-control:hover .vjs-time-tooltip, +.video-js .vjs-progress-control:hover .vjs-progress-holder:focus .vjs-time-tooltip { + display: block; + font-size: 0.6em; + visibility: visible; } + +.video-js .vjs-progress-control.disabled:hover .vjs-time-tooltip { + font-size: 1em; } + +.video-js .vjs-progress-control .vjs-mouse-display { + display: none; + position: absolute; + width: 1px; + height: 100%; + background-color: #000; + z-index: 1; } + +.vjs-no-flex .vjs-progress-control .vjs-mouse-display { + z-index: 0; } + +.video-js .vjs-progress-control:hover .vjs-mouse-display { + display: block; } + +.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display { + visibility: hidden; + opacity: 0; + transition: visibility 1s, opacity 1s; } + +.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display { + display: none; } + +.vjs-mouse-display .vjs-time-tooltip { + color: #fff; + background-color: #000; + background-color: rgba(0, 0, 0, 0.8); } + +.video-js .vjs-slider { + position: relative; + cursor: pointer; + padding: 0; + margin: 0 0.45em 0 0.45em; + /* iOS Safari */ + -webkit-touch-callout: none; + /* Safari */ + -webkit-user-select: none; + /* Konqueror HTML */ + /* Firefox */ + -moz-user-select: none; + /* Internet Explorer/Edge */ + -ms-user-select: none; + /* Non-prefixed version, currently supported by Chrome and Opera */ + user-select: none; + background-color: #73859f; + background-color: rgba(115, 133, 159, 0.5); } + +.video-js .vjs-slider.disabled { + cursor: default; } + +.video-js .vjs-slider:focus { + text-shadow: 0em 0em 1em white; + box-shadow: 0 0 1em #fff; } + +.video-js .vjs-mute-control { + cursor: pointer; + flex: none; } + +.video-js .vjs-volume-control { + cursor: pointer; + margin-right: 1em; + display: flex; } + +.video-js .vjs-volume-control.vjs-volume-horizontal { + width: 5em; } + +.video-js .vjs-volume-panel .vjs-volume-control { + visibility: visible; + opacity: 0; + width: 1px; + height: 1px; + margin-left: -1px; } + +.video-js .vjs-volume-panel { + transition: width 1s; } + .video-js .vjs-volume-panel:hover .vjs-volume-control, + .video-js .vjs-volume-panel:active .vjs-volume-control, + .video-js .vjs-volume-panel:focus .vjs-volume-control, + .video-js .vjs-volume-panel .vjs-volume-control:hover, + .video-js .vjs-volume-panel .vjs-volume-control:active, + .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control, + .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active { + visibility: visible; + opacity: 1; + position: relative; + transition: visibility 0.1s, opacity 0.1s, height 0.1s, width 0.1s, left 0s, top 0s; } + .video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-horizontal, + .video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal, + .video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-horizontal, + .video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-horizontal, + .video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-horizontal, + .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-horizontal, + .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-horizontal { + width: 5em; + height: 3em; } + .video-js .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-vertical, + .video-js .vjs-volume-panel:active .vjs-volume-control.vjs-volume-vertical, + .video-js .vjs-volume-panel:focus .vjs-volume-control.vjs-volume-vertical, + .video-js .vjs-volume-panel .vjs-volume-control:hover.vjs-volume-vertical, + .video-js .vjs-volume-panel .vjs-volume-control:active.vjs-volume-vertical, + .video-js .vjs-volume-panel .vjs-mute-control:hover ~ .vjs-volume-control.vjs-volume-vertical, + .video-js .vjs-volume-panel .vjs-volume-control.vjs-slider-active.vjs-volume-vertical { + left: -3.5em; } + .video-js .vjs-volume-panel.vjs-volume-panel-horizontal:hover, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active { + width: 9em; + transition: width 0.1s; } + .video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only { + width: 4em; } + +.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical { + height: 8em; + width: 3em; + left: -3000em; + transition: visibility 1s, opacity 1s, height 1s 1s, width 1s 1s, left 1s 1s, top 1s 1s; } + +.video-js .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal { + transition: visibility 1s, opacity 1s, height 1s 1s, width 1s, left 1s 1s, top 1s 1s; } + +.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-horizontal { + width: 5em; + height: 3em; + visibility: visible; + opacity: 1; + position: relative; + transition: none; } + +.video-js.vjs-no-flex .vjs-volume-control.vjs-volume-vertical, +.video-js.vjs-no-flex .vjs-volume-panel .vjs-volume-control.vjs-volume-vertical { + position: absolute; + bottom: 3em; + left: 0.5em; } + +.video-js .vjs-volume-panel { + display: flex; } + +.video-js .vjs-volume-bar { + margin: 1.35em 0.45em; } + +.vjs-volume-bar.vjs-slider-horizontal { + width: 5em; + height: 0.3em; } + +.vjs-volume-bar.vjs-slider-vertical { + width: 0.3em; + height: 5em; + margin: 1.35em auto; } + +.video-js .vjs-volume-level { + position: absolute; + bottom: 0; + left: 0; + background-color: #fff; } + .video-js .vjs-volume-level:before { + position: absolute; + font-size: 0.9em; } + +.vjs-slider-vertical .vjs-volume-level { + width: 0.3em; } + .vjs-slider-vertical .vjs-volume-level:before { + top: -0.5em; + left: -0.3em; } + +.vjs-slider-horizontal .vjs-volume-level { + height: 0.3em; } + .vjs-slider-horizontal .vjs-volume-level:before { + top: -0.3em; + right: -0.5em; } + +.video-js .vjs-volume-panel.vjs-volume-panel-vertical { + width: 4em; } + +.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level { + height: 100%; } + +.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level { + width: 100%; } + +.video-js .vjs-volume-vertical { + width: 3em; + height: 8em; + bottom: 8em; + background-color: #2B333F; + background-color: rgba(43, 51, 63, 0.7); } + +.video-js .vjs-volume-horizontal .vjs-menu { + left: -2em; } + +.vjs-poster { + display: inline-block; + vertical-align: middle; + background-repeat: no-repeat; + background-position: 50% 50%; + background-size: contain; + background-color: #000000; + cursor: pointer; + margin: 0; + padding: 0; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 100%; } + +.vjs-has-started .vjs-poster { + display: none; } + +.vjs-audio.vjs-has-started .vjs-poster { + display: block; } + +.vjs-using-native-controls .vjs-poster { + display: none; } + +.video-js .vjs-live-control { + display: flex; + align-items: flex-start; + flex: auto; + font-size: 1em; + line-height: 3em; } + +.vjs-no-flex .vjs-live-control { + display: table-cell; + width: auto; + text-align: left; } + +.video-js:not(.vjs-live) .vjs-live-control, +.video-js.vjs-liveui .vjs-live-control { + display: none; } + +.video-js .vjs-seek-to-live-control { + cursor: pointer; + flex: none; + display: inline-flex; + height: 100%; + padding-left: 0.5em; + padding-right: 0.5em; + font-size: 1em; + line-height: 3em; + width: auto; + min-width: 4em; } + +.vjs-no-flex .vjs-seek-to-live-control { + display: table-cell; + width: auto; + text-align: left; } + +.video-js.vjs-live:not(.vjs-liveui) .vjs-seek-to-live-control, +.video-js:not(.vjs-live) .vjs-seek-to-live-control { + display: none; } + +.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge { + cursor: auto; } + +.vjs-seek-to-live-control .vjs-icon-placeholder { + margin-right: 0.5em; + color: #888; } + +.vjs-seek-to-live-control.vjs-control.vjs-at-live-edge .vjs-icon-placeholder { + color: red; } + +.video-js .vjs-time-control { + flex: none; + font-size: 1em; + line-height: 3em; + min-width: 2em; + width: auto; + padding-left: 1em; + padding-right: 1em; } + +.vjs-live .vjs-time-control { + display: none; } + +.video-js .vjs-current-time, +.vjs-no-flex .vjs-current-time { + display: none; } + +.video-js .vjs-duration, +.vjs-no-flex .vjs-duration { + display: none; } + +.vjs-time-divider { + display: none; + line-height: 3em; } + +.vjs-live .vjs-time-divider { + display: none; } + +.video-js .vjs-play-control { + cursor: pointer; } + +.video-js .vjs-play-control .vjs-icon-placeholder { + flex: none; } + +.vjs-text-track-display { + position: absolute; + bottom: 3em; + left: 0; + right: 0; + top: 0; + pointer-events: none; } + +.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display { + bottom: 1em; } + +.video-js .vjs-text-track { + font-size: 1.4em; + text-align: center; + margin-bottom: 0.1em; } + +.vjs-subtitles { + color: #fff; } + +.vjs-captions { + color: #fc6; } + +.vjs-tt-cue { + display: block; } + +video::-webkit-media-text-track-display { + -webkit-transform: translateY(-3em); + transform: translateY(-3em); } + +.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display { + -webkit-transform: translateY(-1.5em); + transform: translateY(-1.5em); } + +.video-js .vjs-fullscreen-control { + cursor: pointer; + flex: none; } + +.vjs-playback-rate > .vjs-menu-button, +.vjs-playback-rate .vjs-playback-rate-value { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } + +.vjs-playback-rate .vjs-playback-rate-value { + pointer-events: none; + font-size: 1.5em; + line-height: 2; + text-align: center; } + +.vjs-playback-rate .vjs-menu { + width: 4em; + left: 0em; } + +.vjs-error .vjs-error-display .vjs-modal-dialog-content { + font-size: 1.4em; + text-align: center; } + +.vjs-error .vjs-error-display:before { + color: #fff; + content: 'X'; + font-family: Arial, Helvetica, sans-serif; + font-size: 4em; + left: 0; + line-height: 1; + margin-top: -0.5em; + position: absolute; + text-shadow: 0.05em 0.05em 0.1em #000; + text-align: center; + top: 50%; + vertical-align: middle; + width: 100%; } + +.vjs-loading-spinner { + display: none; + position: absolute; + top: 50%; + left: 50%; + margin: -25px 0 0 -25px; + opacity: 0.85; + text-align: left; + border: 6px solid rgba(43, 51, 63, 0.7); + box-sizing: border-box; + background-clip: padding-box; + width: 50px; + height: 50px; + border-radius: 25px; + visibility: hidden; } + +.vjs-seeking .vjs-loading-spinner, +.vjs-waiting .vjs-loading-spinner { + display: block; + -webkit-animation: vjs-spinner-show 0s linear 0.3s forwards; + animation: vjs-spinner-show 0s linear 0.3s forwards; } + +.vjs-loading-spinner:before, +.vjs-loading-spinner:after { + content: ""; + position: absolute; + margin: -6px; + box-sizing: inherit; + width: inherit; + height: inherit; + border-radius: inherit; + opacity: 1; + border: inherit; + border-color: transparent; + border-top-color: white; } + +.vjs-seeking .vjs-loading-spinner:before, +.vjs-seeking .vjs-loading-spinner:after, +.vjs-waiting .vjs-loading-spinner:before, +.vjs-waiting .vjs-loading-spinner:after { + -webkit-animation: vjs-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, vjs-spinner-fade 1.1s linear infinite; + animation: vjs-spinner-spin 1.1s cubic-bezier(0.6, 0.2, 0, 0.8) infinite, vjs-spinner-fade 1.1s linear infinite; } + +.vjs-seeking .vjs-loading-spinner:before, +.vjs-waiting .vjs-loading-spinner:before { + border-top-color: white; } + +.vjs-seeking .vjs-loading-spinner:after, +.vjs-waiting .vjs-loading-spinner:after { + border-top-color: white; + -webkit-animation-delay: 0.44s; + animation-delay: 0.44s; } + +@keyframes vjs-spinner-show { + to { + visibility: visible; } } + +@-webkit-keyframes vjs-spinner-show { + to { + visibility: visible; } } + +@keyframes vjs-spinner-spin { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +@-webkit-keyframes vjs-spinner-spin { + 100% { + -webkit-transform: rotate(360deg); } } + +@keyframes vjs-spinner-fade { + 0% { + border-top-color: #73859f; } + 20% { + border-top-color: #73859f; } + 35% { + border-top-color: white; } + 60% { + border-top-color: #73859f; } + 100% { + border-top-color: #73859f; } } + +@-webkit-keyframes vjs-spinner-fade { + 0% { + border-top-color: #73859f; } + 20% { + border-top-color: #73859f; } + 35% { + border-top-color: white; } + 60% { + border-top-color: #73859f; } + 100% { + border-top-color: #73859f; } } + +.vjs-chapters-button .vjs-menu ul { + width: 24em; } + +.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder { + vertical-align: middle; + display: inline-block; + margin-bottom: -0.1em; } + +.video-js .vjs-subs-caps-button + .vjs-menu .vjs-captions-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before { + font-family: VideoJS; + content: "\f10d"; + font-size: 1.5em; + line-height: inherit; } + +.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder { + vertical-align: middle; + display: inline-block; + margin-bottom: -0.1em; } + +.video-js .vjs-audio-button + .vjs-menu .vjs-main-desc-menu-item .vjs-menu-item-text .vjs-icon-placeholder:before { + font-family: VideoJS; + content: " \f11d"; + font-size: 1.5em; + line-height: inherit; } + +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-current-time, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-time-divider, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-duration, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-remaining-time, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-playback-rate, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-chapters-button, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-descriptions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-captions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-subtitles-button, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-audio-button, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-control, .video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-current-time, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-time-divider, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-duration, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-remaining-time, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-playback-rate, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-chapters-button, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-descriptions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-captions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-subtitles-button, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-audio-button, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-control, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-current-time, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-time-divider, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-duration, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-remaining-time, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-playback-rate, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-chapters-button, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-descriptions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-captions-button, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subtitles-button, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-audio-button, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-control { + display: none; } + +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal:active, +.video-js:not(.vjs-fullscreen).vjs-layout-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, .video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:hover, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal:active, +.video-js:not(.vjs-fullscreen).vjs-layout-x-small .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:hover, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal:active, +.video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active { + width: auto; + width: initial; } + +.video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-liveui) .vjs-subs-caps-button, .video-js:not(.vjs-fullscreen).vjs-layout-x-small:not(.vjs-live) .vjs-subs-caps-button, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-subs-caps-button { + display: none; } + +.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-custom-control-spacer, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-custom-control-spacer { + flex: auto; + display: block; } + +.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui.vjs-no-flex .vjs-custom-control-spacer, .video-js:not(.vjs-fullscreen).vjs-layout-tiny.vjs-no-flex .vjs-custom-control-spacer { + width: auto; } + +.video-js:not(.vjs-fullscreen).vjs-layout-x-small.vjs-liveui .vjs-progress-control, .video-js:not(.vjs-fullscreen).vjs-layout-tiny .vjs-progress-control { + display: none; } + +.vjs-modal-dialog.vjs-text-track-settings { + background-color: #2B333F; + background-color: rgba(43, 51, 63, 0.75); + color: #fff; + height: 70%; } + +.vjs-text-track-settings .vjs-modal-dialog-content { + display: table; } + +.vjs-text-track-settings .vjs-track-settings-colors, +.vjs-text-track-settings .vjs-track-settings-font, +.vjs-text-track-settings .vjs-track-settings-controls { + display: table-cell; } + +.vjs-text-track-settings .vjs-track-settings-controls { + text-align: right; + vertical-align: bottom; } + +@supports (display: grid) { + .vjs-text-track-settings .vjs-modal-dialog-content { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-rows: 1fr; + padding: 20px 24px 0px 24px; } + .vjs-track-settings-controls .vjs-default-button { + margin-bottom: 20px; } + .vjs-text-track-settings .vjs-track-settings-controls { + grid-column: 1 / -1; } + .vjs-layout-small .vjs-text-track-settings .vjs-modal-dialog-content, + .vjs-layout-x-small .vjs-text-track-settings .vjs-modal-dialog-content, + .vjs-layout-tiny .vjs-text-track-settings .vjs-modal-dialog-content { + grid-template-columns: 1fr; } } + +.vjs-track-setting > select { + margin-right: 1em; + margin-bottom: 0.5em; } + +.vjs-text-track-settings fieldset { + margin: 5px; + padding: 3px; + border: none; } + +.vjs-text-track-settings fieldset span { + display: inline-block; } + +.vjs-text-track-settings fieldset span > select { + max-width: 7.3em; } + +.vjs-text-track-settings legend { + color: #fff; + margin: 0 0 5px 0; } + +.vjs-text-track-settings .vjs-label { + position: absolute; + clip: rect(1px 1px 1px 1px); + clip: rect(1px, 1px, 1px, 1px); + display: block; + margin: 0 0 5px 0; + padding: 0; + border: 0; + height: 1px; + width: 1px; + overflow: hidden; } + +.vjs-track-settings-controls button:focus, +.vjs-track-settings-controls button:active { + outline-style: solid; + outline-width: medium; + background-image: linear-gradient(0deg, #fff 88%, #73859f 100%); } + +.vjs-track-settings-controls button:hover { + color: rgba(43, 51, 63, 0.75); } + +.vjs-track-settings-controls button { + background-color: #fff; + background-image: linear-gradient(-180deg, #fff 88%, #73859f 100%); + color: #2B333F; + cursor: pointer; + border-radius: 2px; } + +.vjs-track-settings-controls .vjs-default-button { + margin-right: 1em; } + +@media print { + .video-js > *:not(.vjs-tech):not(.vjs-poster) { + visibility: hidden; } } + +.vjs-resize-manager { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: none; + z-index: -1000; } + +.js-focus-visible .video-js *:focus:not(.focus-visible) { + outline: none; + background: none; } + +.video-js *:focus:not(:focus-visible), +.video-js .vjs-menu *:focus:not(:focus-visible) { + outline: none; + background: none; } diff --git a/ui/public/static/videojs-vr.css b/ui/public/static/videojs-vr.css new file mode 100644 index 000000000..6e850c2f2 --- /dev/null +++ b/ui/public/static/videojs-vr.css @@ -0,0 +1 @@ +.video-js .vjs-big-vr-play-button{width:100px;height:100px;background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='360' height='360' viewBox='0 0 360 360'%3E%3Cpath fill='%23FFF' d='M334.883 275.78l-6.374-36.198-6.375-36.2-28.16 23.62-28.164 23.62 25.837 9.41C266.247 296.544 224 320.5 176.25 320.5c-77.47 0-140.5-63.03-140.5-140.5 0-77.472 63.03-140.5 140.5-140.5 53.428 0 99.98 29.978 123.733 73.993l13.304-6.923C287.025 57.76 235.45 24.5 176.25 24.5c-85.743 0-155.5 69.757-155.5 155.5 0 85.742 69.757 155.5 155.5 155.5 54.253 0 102.09-27.94 129.922-70.177l28.71 10.457z'/%3E%3Cpath fill='%23FFF' d='M314.492 175.167c-12.98 0-23.54-10.56-23.54-23.54s10.56-23.54 23.54-23.54c12.98 0 23.54 10.56 23.54 23.54s-10.56 23.54-23.54 23.54zm0-38.08c-8.018 0-14.54 6.522-14.54 14.54s6.522 14.54 14.54 14.54c8.017 0 14.54-6.522 14.54-14.54s-6.523-14.54-14.54-14.54z'/%3E%3Cg fill='%23FFF'%3E%3Cpath d='M88.76 173.102h9.395c4.74-.042 8.495-1.27 11.268-3.682 2.77-2.412 4.157-5.903 4.157-10.474 0-4.4-1.153-7.817-3.46-10.25-2.307-2.434-5.83-3.65-10.568-3.65-4.147 0-7.554 1.195-10.22 3.585-2.666 2.392-4 5.514-4 9.364H69.908c0-4.74 1.26-9.055 3.776-12.95 2.518-3.892 6.03-6.928 10.537-9.108 4.508-2.18 9.554-3.27 15.14-3.27 9.225 0 16.472 2.318 21.74 6.952 5.27 4.634 7.903 11.077 7.903 19.33 0 4.147-1.323 8.05-3.967 11.71-2.646 3.66-6.062 6.422-10.252 8.284 5.078 1.736 8.94 4.465 11.584 8.19s3.968 8.166 3.968 13.33c0 8.294-2.847 14.895-8.538 19.804s-13.17 7.363-22.438 7.363c-8.887 0-16.166-2.37-21.836-7.11-5.67-4.74-8.506-11.045-8.506-18.916h15.425c0 4.062 1.365 7.363 4.094 9.902 2.73 2.54 6.4 3.81 11.014 3.81 4.782 0 8.55-1.27 11.3-3.81s4.126-6.22 4.126-11.045c0-4.865-1.44-8.61-4.316-11.235-2.878-2.623-7.152-3.936-12.822-3.936H88.76V173.1zM187.598 133.493v12.76h-1.904c-8.633.126-15.53 2.497-20.693 7.108-5.162 4.614-8.23 11.152-9.203 19.615 4.95-5.205 11.277-7.808 18.98-7.808 8.166 0 14.608 2.878 19.328 8.633 4.718 5.755 7.077 13.182 7.077 22.28 0 9.395-2.76 17.002-8.284 22.82-5.52 5.818-12.77 8.73-21.74 8.73-9.226 0-16.705-3.407-22.44-10.222-5.733-6.812-8.6-15.742-8.6-26.787v-5.267c0-16.208 3.945-28.903 11.84-38.086 7.89-9.182 19.242-13.774 34.054-13.774h1.586zM171.03 177.61c-3.386 0-6.485.95-9.3 2.855-2.814 1.904-4.877 4.443-6.188 7.617v4.697c0 6.854 1.438 12.304 4.316 16.345 2.877 4.04 6.602 6.062 11.172 6.062s8.188-1.715 10.854-5.143 4-7.934 4-13.52-1.355-10.135-4.063-13.648c-2.708-3.51-6.304-5.267-10.79-5.267zM271.136 187.447c0 13.29-2.486 23.307-7.46 30.057s-12.535 10.125-22.69 10.125c-9.988 0-17.51-3.292-22.566-9.872-5.058-6.58-7.65-16.323-7.776-29.23V172.53c0-13.287 2.485-23.252 7.458-29.896 4.973-6.643 12.558-9.966 22.757-9.966 10.112 0 17.655 3.237 22.63 9.712 4.97 6.475 7.52 16.166 7.647 29.072v15.995zm-15.425-17.265c0-8.674-1.185-15.033-3.554-19.075-2.37-4.04-6.137-6.062-11.3-6.062-5.035 0-8.738 1.915-11.107 5.745-2.37 3.83-3.62 9.807-3.746 17.932v20.948c0 8.633 1.206 15.064 3.618 19.297s6.2 6.348 11.362 6.348c4.95 0 8.61-1.957 10.98-5.87 2.37-3.915 3.62-10.04 3.746-18.378v-20.885z'/%3E%3C/g%3E%3C/svg%3E");background-size:contain;background-color:rgba(0,0,0,0.5)}.video-js .vjs-big-vr-play-button .vjs-icon-placeholder{display:none}:hover.video-js .vjs-big-vr-play-button{-webkit-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-moz-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-ms-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;-o-transition:border-color 0.4s,outline 0.4s,background-color 0.4s;transition:border-color 0.4s,outline 0.4s,background-color 0.4s}.video-js .vjs-big-vr-play-button::before{content:''}.video-js canvas{cursor:move}.video-js .vjs-button-vr .vjs-icon-placeholder{height:30px;width:30px;display:inline-block;background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMjAuNzQgNkgzLjIxQzIuNTUgNiAyIDYuNTcgMiA3LjI4djEwLjQ0YzAgLjcuNTUgMS4yOCAxLjIzIDEuMjhoNC43OWMuNTIgMCAuOTYtLjMzIDEuMTQtLjc5bDEuNC0zLjQ4Yy4yMy0uNTkuNzktMS4wMSAxLjQ0LTEuMDFzMS4yMS40MiAxLjQ1IDEuMDFsMS4zOSAzLjQ4Yy4xOS40Ni42My43OSAxLjExLjc5aDQuNzljLjcxIDAgMS4yNi0uNTcgMS4yNi0xLjI4VjcuMjhjMC0uNy0uNTUtMS4yOC0xLjI2LTEuMjh6TTcuNSAxNC42MmMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTMgMS4xOCAwIDIuMTIuOTYgMi4xMiAyLjEzcy0uOTUgMi4xMi0yLjEyIDIuMTJ6bTkgMGMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTNzMi4xMi45NiAyLjEyIDIuMTMtLjk1IDIuMTItMi4xMiAyLjEyeiIvPgogICAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+Cjwvc3ZnPgo=) no-repeat left center} diff --git a/ui/rollup.config.js b/ui/rollup.config.js new file mode 100644 index 000000000..f060e1880 --- /dev/null +++ b/ui/rollup.config.js @@ -0,0 +1,44 @@ +import svelte from 'rollup-plugin-svelte'; +import resolve from 'rollup-plugin-node-resolve'; +import commonjs from 'rollup-plugin-commonjs'; +import livereload from 'rollup-plugin-livereload'; +import { terser } from 'rollup-plugin-terser'; + +const production = !process.env.ROLLUP_WATCH; + +export default { + input: 'src/main.js', + output: { + sourcemap: true, + format: 'iife', + name: 'app', + file: 'public/static/bundle.js' + }, + plugins: [ + svelte({ + // enable run-time checks when not in production + dev: !production, + // we'll extract any component CSS out into + // a separate file — better for performance + css: css => { + css.write('public/static/bundle.css'); + } + }), + + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration — + // consult the documentation for details: + // https://github.com/rollup/rollup-plugin-commonjs + resolve(), + commonjs(), + + // Watch the `public` directory and refresh the + // browser on changes when not in production + !production && livereload('public'), + + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser() + ] +}; diff --git a/ui/src/App.svelte b/ui/src/App.svelte new file mode 100644 index 000000000..0e134e1fa --- /dev/null +++ b/ui/src/App.svelte @@ -0,0 +1,13 @@ + + + + + + + diff --git a/ui/src/Navbar.svelte b/ui/src/Navbar.svelte new file mode 100644 index 000000000..fbc814ee3 --- /dev/null +++ b/ui/src/Navbar.svelte @@ -0,0 +1,34 @@ + + + + XBVR + + + + + + + Scenes + + + + Files + + + + Volumes + + + + Options + + + + + + \ No newline at end of file diff --git a/ui/src/main.js b/ui/src/main.js new file mode 100644 index 000000000..b116026a8 --- /dev/null +++ b/ui/src/main.js @@ -0,0 +1,7 @@ +import App from './App.svelte'; + +const app = new App({ + target: document.body, +}); + +export default app; \ No newline at end of file diff --git a/ui/src/scenes/Filters.svelte b/ui/src/scenes/Filters.svelte new file mode 100644 index 000000000..8f20aad0b --- /dev/null +++ b/ui/src/scenes/Filters.svelte @@ -0,0 +1,141 @@ + + + + + Cover size + + + + + Show info + + + Show info + + + + + + State + + + + Any + Available right now + Downloaded + Not downloaded + + + + + +Cast + + + + + + {#if filters.cast} + {#each filters.cast as t}{t}{/each} + {/if} + + + + + + + + + + + + +Site + + + + + + {#if filters.sites} + {#each filters.sites as t}{t}{/each} + {/if} + + + + + + + + + + + + +Tags + + + + + + {#if filters.tags} + {#each filters.tags as t}{t}{/each} + {/if} + + + + + + + + + + + diff --git a/ui/src/scenes/SceneList.svelte b/ui/src/scenes/SceneList.svelte new file mode 100644 index 000000000..bf4041a9a --- /dev/null +++ b/ui/src/scenes/SceneList.svelte @@ -0,0 +1,125 @@ + + +{#each items as item} + + + + + {#if item.is_available} + + + + {:else} + + + + {/if} + + + {#if $showInfo} + {format(parse(item.release_date), "YYYY-MM-DD")} + {/if} + + + +{/each} diff --git a/ui/src/scenes/Video.svelte b/ui/src/scenes/Video.svelte new file mode 100644 index 000000000..1ea6e3a4a --- /dev/null +++ b/ui/src/scenes/Video.svelte @@ -0,0 +1,46 @@ + + + + + + + + + + + diff --git a/ui/src/scenes/index.svelte b/ui/src/scenes/index.svelte new file mode 100644 index 000000000..0c28752b1 --- /dev/null +++ b/ui/src/scenes/index.svelte @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/ui/src/store/filters.js b/ui/src/store/filters.js new file mode 100644 index 000000000..0e1050bac --- /dev/null +++ b/ui/src/store/filters.js @@ -0,0 +1,10 @@ +import { writable } from 'svelte/store'; + +export const cardSize = writable(1); +export const showInfo = writable(false); + +export let tag = writable(""); +export let cast = writable(""); +export let site = writable(""); + +export let dlState = writable(""); diff --git a/ui/yarn.lock b/ui/yarn.lock new file mode 100644 index 000000000..6c036bb35 --- /dev/null +++ b/ui/yarn.lock @@ -0,0 +1,2293 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/runtime@^7.2.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" + integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg== + dependencies: + regenerator-runtime "^0.13.2" + +"@polka/url@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31" + integrity sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/node@*": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.0.tgz#d11813b9c0ff8aaca29f04cbc12817f4c7d656e5" + integrity sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg== + +"@types/node@^11.13.9": + version "11.13.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.10.tgz#4df59e5966b56f512bac98898bcbee5067411f0f" + integrity sha512-leUNzbFTMX94TWaIKz8N15Chu55F9QSH+INKayQr5xpkasBQBRF3qQXfo3/dOnMU/dEIit+Y/SU8HyOjq++GwA== + +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@videojs/http-streaming@1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-1.9.3.tgz#c971050495fb58d2b4c6ee0246bb03cc750635b1" + integrity sha512-gNdqyvhxTU67optzxiywHXi/z2+Ju0b6hNth0V7BsL7YAH+R1StIKmmp6SsfFZQfrNW5ykYFoR95M/AT5cg9Ug== + dependencies: + aes-decrypter "3.0.0" + global "^4.3.0" + m3u8-parser "4.3.0" + mpd-parser "0.7.0" + mux.js "5.1.1" + url-toolkit "^2.1.3" + video.js "^6.8.0 || ^7.0.0" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +acorn@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== + +aes-decrypter@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-3.0.0.tgz#7848a1c145b9fdbf57ae3e2b5b1bc7cf0644a8fb" + integrity sha1-eEihwUW5/b9Xrj4rWxvHzwZEqPs= + dependencies: + commander "^2.9.0" + global "^4.3.2" + pkcs7 "^1.0.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +builtin-modules@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" + integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cardboard-vr-display@1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/cardboard-vr-display/-/cardboard-vr-display-1.0.13.tgz#e192a220b2970002cfb5cc0f1b480ff5fd6748b6" + integrity sha512-/ovHu4nHfo3oaw2v20v7ErvDUmIBPih/e/6VG83XUUFs5vPQaGSM8hihqKClOlwy3Fz+52L0FrhcGSH3o+yn6w== + dependencies: + gl-preserve-state "^1.0.0" + nosleep.js "^0.7.0" + webvr-polyfill-dpdb "^1.0.7" + +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +commander@^2.19.0, commander@^2.9.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-clear@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/console-clear/-/console-clear-1.1.1.tgz#995e20cbfbf14dd792b672cde387bd128d674bf7" + integrity sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ== + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +date-fns@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== + +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +define-properties@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.4.3, es-abstract@^1.5.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +estree-walker@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.0.tgz#5d865327c44a618dde5699f763891ae31f257dae" + integrity sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + dependencies: + minipass "^2.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.0.0: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-port@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +gl-preserve-state@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gl-preserve-state/-/gl-preserve-state-1.0.0.tgz#4ef710d62873f1470ed015c6546c37dacddd4198" + integrity sha512-zQZ25l3haD4hvgJZ6C9+s0ebdkW9y+7U2qxvGu1uWOJh8a4RU+jURIKEQhf8elIlFpMH6CrAY2tH0mYrRjet3Q== + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@4.3.2, global@^4.3.0, global@^4.3.1, global@^4.3.2, global@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= + dependencies: + min-document "^2.19.0" + process "~0.5.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +individual@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/individual/-/individual-2.0.0.tgz#833b097dad23294e76117a98fb38e0d9ad61bb97" + integrity sha1-gzsJfa0jKU52EXqY+zjg2a1hu5c= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.3, is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-function@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" + integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +jest-worker@^24.0.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.6.0.tgz#7f81ceae34b7cde0c9827a6980c35b7cdc0161b3" + integrity sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ== + dependencies: + merge-stream "^1.0.1" + supports-color "^6.1.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +keycode@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" + integrity sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ= + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +kleur@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +ky@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.10.0.tgz#b736665a484962f02d8e2498ab27cfb316cfb7d7" + integrity sha512-2OiL0TJiex7tGTUvYOrijLbXKV9rb9IJcLvDh+m5+v87ZhBThIlXwEjaC8/H0e8zMW+hq4HCznVBIdbNMsSQKg== + +livereload@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.7.0.tgz#38238dd155ffb251191697f737b6b13f471da115" + integrity sha512-PHnIGczQEvmCctDvRTWylA+1wSwE0/eFm+LkNhlmlAFus/aCRlVE97UOLOf6TUGLmZyfg7z7twG37ZiOgNJAyQ== + dependencies: + chokidar "^1.7.0" + opts ">= 1.2.0" + ws "^1.1.5" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +local-access@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/local-access/-/local-access-1.0.1.tgz#5121258146d64e869046c642ea4f1dd39ff942bb" + integrity sha512-ykt2pgN0aqIy6KQC1CqdWTWkmUwNgaOS6dcpHVjyBJONA+Xi7AtSB1vuxC/U/0tjIP3wcRudwQk1YYzUvzk2bA== + +m3u8-parser@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.3.0.tgz#4b4e988f87b6d8b2401d209a1d17798285a9da04" + integrity sha512-bVbjuBMoVIgFL1vpXVIxjeaoB5TPDJRb0m5qiTdM738SGqv/LAmsnVVPlKjM4fulm/rr1XZsKM+owHm+zvqxYA== + dependencies: + global "^4.3.2" + +magic-string@^0.25.2: + version "0.25.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" + integrity sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg== + dependencies: + sourcemap-codec "^1.4.4" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +math-random@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" + integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime@^2.3.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.2.tgz#ce5229a5e99ffc313abac806b482c10e7ba6ac78" + integrity sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minipass@^2.2.1, minipass@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mpd-parser@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.7.0.tgz#d36e3322579fce23d657f71a3c2f3e6cc5ce4002" + integrity sha512-nkzVIkecaDz3q7p4ToN3GR0FV2Odbh0w2sJ8ijsyw79JcBrJoUD3KHIiI8gL0hEDlex7mrVpTxXBsRHowUBmPw== + dependencies: + global "^4.3.2" + url-toolkit "^2.1.1" + +mri@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" + integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +mux.js@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-5.1.1.tgz#0e95f048b4ac51d413c9ddc2d78e4cefad8d06de" + integrity sha512-Mf/UYmh5b8jvUP+jmrTbETnyFZprMdbT0RxKm/lJ/4d2Q3xdc5GaHaRPI1zVV5D3+6uxArVPm78QEb1RsrmaQw== + +nan@^2.12.1: + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +needle@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.1.tgz#d272f2f4034afb9c4c9ab1379aabc17fc85c9388" + integrity sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg== + dependencies: + debug "^4.1.0" + iconv-lite "^0.4.4" + sax "^1.2.4" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +nosleep.js@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/nosleep.js/-/nosleep.js-0.7.0.tgz#cfd919c25523ca0d0f4a69fb3305c083adaee289" + integrity sha1-z9kZwlUjyg0PSmn7MwXAg62u4ok= + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-all@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= + +"opts@>= 1.2.0": + version "1.2.6" + resolved "https://registry.yarnpkg.com/opts/-/opts-1.2.6.tgz#d185c0425cfdeb9da1d182908b65b5c0238febb3" + integrity sha1-0YXAQlz9652h0YKQi2W1wCOP67M= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-headers@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.2.tgz#9545e8a4c1ae5eaea7d24992bca890281ed26e34" + integrity sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== + dependencies: + for-each "^0.3.3" + string.prototype.trim "^1.1.2" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/path-parser/-/path-parser-4.2.0.tgz#2fbb3df2d49659a9a4cac88be7d8d5f4666814fb" + integrity sha512-MPPZiWTTp2I72VXmGQQfsn2ohrbd9QTbZSLYNS+HXsnQ37VbiLR/szO2R7DHaZA1V1scYxuxgyQerj+6kMTXtA== + dependencies: + search-params "2.1.3" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pidtree@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b" + integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pkcs7@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pkcs7/-/pkcs7-1.0.2.tgz#b6dba527528c2942bfc122ce2dafcdb5e59074e7" + integrity sha1-ttulJ1KMKUK/wSLOLa/NteWQdOc= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= + +randomatic@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" + integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +require-relative@^0.8.7: + version "0.8.7" + resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" + integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" + integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== + dependencies: + path-parse "^1.0.6" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rollup-plugin-commonjs@^9.3.4: + version "9.3.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.3.4.tgz#2b3dddbbbded83d45c36ff101cdd29e924fd23bc" + integrity sha512-DTZOvRoiVIHHLFBCL4pFxOaJt8pagxsVldEXBOn6wl3/V21wVaj17HFfyzTsQUuou3sZL3lEJZVWKPFblJfI6w== + dependencies: + estree-walker "^0.6.0" + magic-string "^0.25.2" + resolve "^1.10.0" + rollup-pluginutils "^2.6.0" + +rollup-plugin-livereload@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-1.0.0.tgz#7ebaeea382019037e21265cd49942122281d5646" + integrity sha512-73vtOWMcQEOKyk5jgDTEFYGf4d4GWraDt/onTiauE7WALAO+6nIWKD7p47yDiCiz/pTOKvh38SQ2HZ8KkpaNew== + dependencies: + livereload "^0.7.0" + +rollup-plugin-node-resolve@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.2.3.tgz#638a373a54287d19fcc088fdd1c6fd8a58e4d90a" + integrity sha512-r+WaesPzdGEynpLZLALFEDugA4ACa5zn7bc/+LVX4vAXQQ8IgDHv0xfsSvJ8tDXUtprfBtrDtRFg27ifKjcJTg== + dependencies: + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.10.0" + +rollup-plugin-svelte@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-svelte/-/rollup-plugin-svelte-5.0.3.tgz#1e4cce33a94b0b4b4831abc7d76eabdf333ab63d" + integrity sha512-3W/jbtBcsxohKQMI1Po2ZsUHRGUy3vEtgXLHvBin1+ms3wl2eomSyYBV7pwrkh6tWok9BVzdxGQgd7IqibG+Ew== + dependencies: + require-relative "^0.8.7" + rollup-pluginutils "^2.3.3" + sourcemap-codec "^1.4.4" + +rollup-plugin-terser@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-4.0.4.tgz#6f661ef284fa7c27963d242601691dc3d23f994e" + integrity sha512-wPANT5XKVJJ8RDUN0+wIr7UPd0lIXBo4UdJ59VmlPCtlFsE20AM+14pe+tk7YunCsWEiuzkDBY3QIkSCjtrPXg== + dependencies: + "@babel/code-frame" "^7.0.0" + jest-worker "^24.0.0" + serialize-javascript "^1.6.1" + terser "^3.14.1" + +rollup-pluginutils@^2.3.3, rollup-pluginutils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz#203706edd43dfafeaebc355d7351119402fc83ad" + integrity sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ== + dependencies: + estree-walker "^0.6.0" + micromatch "^3.1.10" + +rollup@^1.10.1: + version "1.11.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.11.3.tgz#6f436db2a2d6b63f808bf60ad01a177643dedb81" + integrity sha512-81MR7alHcFKxgWzGfG7jSdv+JQxSOIOD/Fa3iNUmpzbd7p+V19e1l9uffqT8/7YAHgGOzmoPGN3Fx3L2ptOf5g== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "^11.13.9" + acorn "^6.1.1" + +rust-result@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rust-result/-/rust-result-1.0.0.tgz#34c75b2e6dc39fe5875e5bdec85b5e0f91536f72" + integrity sha1-NMdbLm3Dn+WHXlveyFteD5FTb3I= + dependencies: + individual "^2.0.0" + +sade@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/sade/-/sade-1.4.2.tgz#b1946ef9ec62450b74e17d9fec30156c94f193a6" + integrity sha512-MTrQm+Nhl4m1mbssYDgAculC/HbShjj08QtHnA2GTpzivfU5aUp8EoHlECmrIHEaa8hZRZSp2Gygv8VMlpXEBw== + dependencies: + mri "^1.1.0" + +safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-json-parse@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz#7c0f578cfccd12d33a71c0e05413e2eca171eaac" + integrity sha1-fA9XjPzNEtM6ccDgVBPi7KFx6qw= + dependencies: + rust-result "^1.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +search-params@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/search-params/-/search-params-2.1.3.tgz#90b98964eaf3d0edef0f73e6e8307d1480020413" + integrity sha512-hHxU9ZGWpZ/lrFBIHndSnQae2in7ra+m+tBSoeAahSWDDgOgpZqs4bfaTZpljgNgAgTbjiQoJtZW6FKSsfEcDA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +serialize-javascript@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" + integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +sirv-cli@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-0.4.1.tgz#ce33c6ace002b821692a672b84500ca8fd88e2cc" + integrity sha512-dgI9CXtErbkYKZ1ZIQEdHpEMqlJAtXX1jq1OMXJkxLvWeDzvJbZFftrBIPe7Q0trSuiEFJRJ/GF1803n25mNDQ== + dependencies: + console-clear "^1.1.0" + get-port "^3.2.0" + kleur "^3.0.0" + local-access "^1.0.1" + sade "^1.4.0" + sirv "^0.4.1" + tinydate "^1.0.0" + +sirv@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-0.4.1.tgz#e4e5c5024abfade7f43d2edfd5d1e6f5a2d5a893" + integrity sha512-wjZMiB3jA/SbW6zWpLOAU3LGzCDHaVJsdaQwH9sTYFWJI6IdWe5Op1KhI8js+PXuNPFjNzlJCt233/R3+o/1YA== + dependencies: + "@polka/url" "^0.5.0" + mime "^2.3.1" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@~0.5.10: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" + integrity sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg== + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string.prototype.padend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" + integrity sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.4.3" + function-bind "^1.0.2" + +string.prototype.trim@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" + integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.0" + function-bind "^1.0.2" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +svelte-routing@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svelte-routing/-/svelte-routing-1.0.0.tgz#9132344964af8907a4a4d0d56a458a1a36937853" + integrity sha512-GI8FA/WABMbhL4+N7hK6/vs5f4TXEQQot7qQ7LT+lp8H4i9pqNFyOSdSZ58Kf0mDkykN30HE/A4vpwzjp2STdw== + +svelte@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.2.1.tgz#2bd89dbb49de623cea72730a4acaf92b4c1ec8e4" + integrity sha512-DH3WZHua5t87AjvCx8gU5cLQlhRF/KCyRWLpz4TXWwDVs1FEibIhXTXUWRd0EqU8/m/1e4stzL4MnboF8LxyHg== + +svero@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/svero/-/svero-0.4.3.tgz#93486d0073537b27948d6652209c0a2337c26ebd" + integrity sha512-8xkrxySsiDcGwUidDjqF4NgM5WdkMFndNWuGipSVt9yolGlrsnDe5LQ1uGEM3p0Ou0KMw+rH+usawdjqq27MTg== + dependencies: + path-parser "^4.2.0" + +tar@^4: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.4" + minizlib "^1.1.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +terser@^3.14.1: + version "3.17.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" + integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + +three@0.93.0: + version "0.93.0" + resolved "https://registry.yarnpkg.com/three/-/three-0.93.0.tgz#3fd6c367ef4554abbb6e16ad69936283e895c123" + integrity sha1-P9bDZ+9FVKu7bhataZNig+iVwSM= + +tinydate@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tinydate/-/tinydate-1.0.1.tgz#5e38797e2e3e79ce2300543f586c6caa2dfcf668" + integrity sha512-Imqa6iv3Ig5FmC3ESwmqczusIn1h8D5RqNbpatGc1eLHeoytuhodbsAPpSJ8iKiLhxBtLuRsrywWHlJM1bA3Rg== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tsml@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tsml/-/tsml-1.0.1.tgz#89f8218b9d9e257f47d7f6b56d01c5a4d2c68fc3" + integrity sha1-ifghi52eJX9H1/a1bQHFpNLGj8M= + +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-toolkit@^2.1.1, url-toolkit@^2.1.3: + version "2.1.6" + resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.1.6.tgz#6d03246499e519aad224c44044a4ae20544154f2" + integrity sha512-UaZ2+50am4HwrV2crR/JAf63Q4VvPYphe63WGeoJxeu8gmOm0qxPt+KsukfakPNrX9aymGNEkkaoICwn+OuvBw== + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +"video.js@^6 || ^7", "video.js@^6.8.0 || ^7.0.0": + version "7.5.4" + resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.5.4.tgz#7c76d44123be48f98333ab50b19d7683bfa8aba6" + integrity sha512-+U3FyLVFbnJdEC6TVMv8U75c8VM00vmVY8TSfFthnvo7/6rz3LFg2Pd3TTGNbV2pEmBhkLLYO+dvmqMNUyc2ZA== + dependencies: + "@babel/runtime" "^7.2.0" + "@videojs/http-streaming" "1.9.3" + global "4.3.2" + keycode "^2.2.0" + safe-json-parse "4.0.0" + tsml "1.0.1" + videojs-font "3.1.0" + videojs-vtt.js "0.14.1" + xhr "2.4.0" + +videojs-font@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-3.1.0.tgz#ac33be9b517fe19299f61cccd2b3c7d75a1c6960" + integrity sha512-rxB68SVgbHD+kSwoNWNCHicKJuR2ga3bGfvGxmB+8fupsiLbnyCwTBVtrZUq4bZnD64mrKP1DxHiutxwrs59pQ== + +videojs-hotkeys@0.2.25: + version "0.2.25" + resolved "https://registry.yarnpkg.com/videojs-hotkeys/-/videojs-hotkeys-0.2.25.tgz#b34b5816db1af747e41a90a3be268d51449b4cb0" + integrity sha512-XgMjWiqGlmAjuHtpP529A2voVh++z46FSD0XeSy+65yeuTZOd+w2CJmfrL4jPpGm+MME5l9lOLfVpoEeDaBa1Q== + +videojs-vr@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/videojs-vr/-/videojs-vr-1.5.0.tgz#507953228c626847a732ce651a6efede9f643dc1" + integrity sha512-S3/pnk0xP7tGuRX/qe9x4DDppyGGrOGho1IEaBuSyOBnxb1n75Qq+OT4Y7vf9YQp416/wfO6chml1tA5MhMhnw== + dependencies: + global "^4.3.2" + three "0.93.0" + video.js "^6 || ^7" + webvr-polyfill "0.10.6" + +videojs-vtt.js@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/videojs-vtt.js/-/videojs-vtt.js-0.14.1.tgz#da583eb1fc9c81c826a9432b706040e8dea49911" + integrity sha512-YxOiywx6N9t3J5nqsE5WN2Sw4CSqVe3zV+AZm2T4syOc2buNJaD6ZoexSdeszx2sHLU/RRo2r4BJAXFDQ7Qo2Q== + dependencies: + global "^4.3.1" + +webvr-polyfill-dpdb@^1.0.7: + version "1.0.16" + resolved "https://registry.yarnpkg.com/webvr-polyfill-dpdb/-/webvr-polyfill-dpdb-1.0.16.tgz#7f2cf88460c002a5f10ff69b2bf3a5663e0ad9dd" + integrity sha512-FCciTtPhwkYYHGL0K67G8k7ZnNVqnEx3u4NVHLJ0+Ya6f2VTZUl/GJpaEo1Id2pBP/2l0RYZ3IGZE4Pe8fRalg== + +webvr-polyfill@0.10.6: + version "0.10.6" + resolved "https://registry.yarnpkg.com/webvr-polyfill/-/webvr-polyfill-0.10.6.tgz#c1c5f5e3801784bc131a05aacfa2169872ce0b1c" + integrity sha512-H+2LI+rHMguGaUAg+1QuiIQ+1Y1wTSkkUXjQfF/GFD2wUL0AdiN8+uPGxnE7bHMBIG7NswN6kgkZhgy4WpwTlw== + dependencies: + cardboard-vr-display "1.0.13" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" + integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +xhr@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.0.tgz#e16e66a45f869861eeefab416d5eff722dc40993" + integrity sha1-4W5mpF+GmGHu76tBbV7/ci3ECZM= + dependencies: + global "~4.3.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== diff --git a/xbase/api_dms.go b/xbase/api_dms.go new file mode 100644 index 000000000..d6fbd9191 --- /dev/null +++ b/xbase/api_dms.go @@ -0,0 +1,145 @@ +package xbase + +import ( + "net/http" + "strconv" + + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" + "github.com/jinzhu/gorm" +) + +type DMSDataResponse struct { + Sites []string `json:"sites"` + Actors []string `json:"actors"` + Tags []string `json:"tags"` + ReleaseGroup []string `json:"release_group"` + Volumes []Volume `json:"volumes"` +} + +type DMSResource struct{} + +func (i DMSResource) WebService() *restful.WebService { + tags := []string{"DMS"} + + ws := new(restful.WebService) + + ws.Path("/api/dms"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/base").To(i.base). + Metadata(restfulspec.KeyOpenAPITags, tags)) + + ws.Route(ws.GET("/scene").To(i.sceneById). + Metadata(restfulspec.KeyOpenAPITags, tags)) + + ws.Route(ws.GET("/file/{file-id}").To(i.getFile). + Param(ws.PathParameter("file-id", "File ID").DataType("int")). + Metadata(restfulspec.KeyOpenAPITags, tags)) + + return ws +} + +func (i DMSResource) sceneById(req *restful.Request, resp *restful.Response) { + sceneId := req.QueryParameter("id") + + db, _ := GetDB() + defer db.Close() + + var scene Scene + db.Preload("Cast"). + Preload("Tags"). + Preload("Filenames"). + Preload("Images"). + Preload("Files"). + Where(&Scene{SceneID: sceneId}).FirstOrCreate(&scene) + + resp.WriteHeaderAndEntity(http.StatusOK, scene) +} + +func (i DMSResource) base(req *restful.Request, resp *restful.Response) { + db, _ := GetDB() + defer db.Close() + + // Get all accessible scenes + var scenes []Scene + tx := db. + Model(&scenes). + Preload("Cast"). + Preload("Tags"). + Preload("Filenames"). + Preload("Images"). + Preload("Files") + + tx = tx.Where("is_accessible = ?", 1) + + // Available sites + tx.Group("site").Find(&scenes) + var outSites []string + for i := range scenes { + if scenes[i].Site != "" { + outSites = append(outSites, scenes[i].Site) + } + } + + // Available release dates (YYYY-MM) + tx.Select("strftime('%Y-%m', release_date) as release_date_text"). + Group("strftime('%Y-%m', release_date)").Find(&scenes) + var outRelease []string + for i := range scenes { + outRelease = append(outRelease, scenes[i].ReleaseDateText) + } + + // Available tags + tx.Joins("left join scene_tags on scene_tags.scene_id=scenes.id"). + Joins("left join tags on tags.id=scene_tags.tag_id"). + Group("tags.name").Select("tags.name as release_date_text").Find(&scenes) + + var outTags []string + for i := range scenes { + if scenes[i].ReleaseDateText != "" { + outTags = append(outTags, scenes[i].ReleaseDateText) + } + } + + // Available actors + tx.Joins("left join scene_cast on scene_cast.scene_id=scenes.id"). + Joins("left join actors on actors.id=scene_cast.actor_id"). + Group("actors.name").Select("actors.name as release_date_text").Find(&scenes) + + var outCast []string + for i := range scenes { + if scenes[i].ReleaseDateText != "" { + outCast = append(outCast, scenes[i].ReleaseDateText) + } + } + + // Available volumes + var vol []Volume + db.Where("is_available = ?", true).Find(&vol) + + resp.WriteHeaderAndEntity(http.StatusOK, DMSDataResponse{Sites: outSites, Tags: outTags, Actors: outCast, Volumes: vol, ReleaseGroup: outRelease}) +} + +func (i DMSResource) getFile(req *restful.Request, resp *restful.Response) { + id, err := strconv.Atoi(req.PathParameter("file-id")) + if err != nil { + resp.WriteHeader(http.StatusBadRequest) + return + } + + // Check if scene exist + db, _ := GetDB() + defer db.Close() + + f := File{} + err = db.First(&f, id).Error + + if err == gorm.ErrRecordNotFound { + resp.WriteHeader(http.StatusNotFound) + return + } + + http.ServeFile(resp.ResponseWriter, req.Request, f.GetPath()) +} diff --git a/xbase/api_extension.go b/xbase/api_extension.go new file mode 100644 index 000000000..5fc30ac03 --- /dev/null +++ b/xbase/api_extension.go @@ -0,0 +1,202 @@ +package xbase + +import ( + "net/http" + "strings" + + "github.com/araddon/dateparse" + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" + "github.com/jinzhu/gorm" +) + +type ExtScene struct { + SceneID string `json:"_id"` + SiteID string `json:"scene_id"` + SceneType string `json:"scene_type"` + Title string `json:"title"` + Studio string `json:"studio"` + Site string `json:"site"` + Covers []string `json:"covers"` + Gallery []string `json:"gallery"` + Tags []string `json:"tags"` + Cast []string `json:"cast"` + Filenames []string `json:"filename"` + Duration int `json:"duration"` + Synopsis string `json:"synopsis"` + Released string `json:"released"` + HomepageURL string `json:"homepage_url"` +} + +type ExtSceneResponse struct { + Status string `json:"status"` + Scene interface{} `json:"scene"` +} + +type ExtResource struct{} + +func (i ExtResource) WebService() *restful.WebService { + tags := []string{"Ext"} + + ws := new(restful.WebService) + + ws.Path("/api/ext"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/status").To(i.status). + Metadata(restfulspec.KeyOpenAPITags, tags)) + + ws.Route(ws.GET("/scene/{scene-id}").To(i.checkScene). + Param(ws.PathParameter("scene-id", "Scene ID").DataType("string")). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes(ExtSceneResponse{})) + + ws.Route(ws.POST("/scene").To(i.createScene). + Metadata(restfulspec.KeyOpenAPITags, tags). + Reads(ExtScene{}). + Writes(ExtSceneResponse{}). + Returns(http.StatusCreated, "Created", Scene{}). + Returns(http.StatusConflict, "Already exist", ExtSceneResponse{})) + + return ws +} + +func (i ExtResource) status(req *restful.Request, resp *restful.Response) { + +} + +func (i ExtResource) checkScene(req *restful.Request, resp *restful.Response) { + id := req.PathParameter("scene-id") + + // Check if scene exist + db, _ := GetDB() + defer db.Close() + + localScene := Scene{} + err := localScene.GetIfExist(id) + + // Output + out := ExtScene{} + SceneToExt(localScene, &out) + + if err == gorm.ErrRecordNotFound { + resp.WriteHeaderAndEntity(http.StatusNotFound, ExtSceneResponse{Status: "not-found"}) + return + } + + resp.WriteHeaderAndEntity(http.StatusConflict, ExtSceneResponse{Status: "exist", Scene: out}) +} + +func (i ExtResource) createScene(req *restful.Request, resp *restful.Response) { + obj := ExtScene{} + err := req.ReadEntity(&obj) + if err != nil { + APIError(req, resp, http.StatusInternalServerError, err) + return + } + + // Check if scene exist + localScene := Scene{} + err = localScene.GetIfExist(obj.SceneID) + + // Output + // out := ExtScene{} + // SceneToExt(localScene, &out) + // if err == nil { + // resp.WriteHeaderAndEntity(http.StatusConflict, ExtSceneResponse{Status: "exist", Scene: out}) + // return + // } + + // Save + db, _ := GetDB() + defer db.Close() + + localScene = Scene{} + db.Where(&Scene{SceneID: obj.SceneID}).FirstOrCreate(&localScene) + + localScene.SceneID = obj.SceneID + localScene.Title = obj.Title + localScene.SceneType = obj.SceneType + localScene.Studio = obj.Studio + localScene.Site = obj.Site + localScene.Duration = obj.Duration + localScene.Synopsis = obj.Synopsis + localScene.ReleaseDateText = obj.Released + localScene.CoverURL = obj.Covers[0] + localScene.SceneURL = obj.HomepageURL + + if obj.Released != "" { + dateParsed, err := dateparse.ParseLocal(strings.Replace(obj.Released, ",", "", -1)) + if err == nil { + localScene.ReleaseDate = dateParsed + } + } + + db.Save(localScene) + + // Associate Tags + var tmpTag Tag + for _, name := range obj.Tags { + tagClean := convertTag(name) + if tagClean != "" { + tmpTag = Tag{} + db.Where(&Tag{Name: tagClean}).FirstOrCreate(&tmpTag) + db.Model(&localScene).Association("Tags").Append(tmpTag) + } + } + + // Associate Actors + var tmpActor Actor + for _, name := range obj.Cast { + tmpActor = Actor{} + db.Where(&Actor{Name: name}).FirstOrCreate(&tmpActor) + db.Model(&localScene).Association("Cast").Append(tmpActor) + } + + // Associate Filenames + var tmpSceneFilename PossibleFilename + for _, name := range obj.Filenames { + tmpSceneFilename = PossibleFilename{} + db.Where(&PossibleFilename{Name: name}).FirstOrCreate(&tmpSceneFilename) + db.Model(&localScene).Association("Filenames").Append(tmpSceneFilename) + } + + // Associate Images (but first remove old ones) + db.Unscoped().Where(&Image{SceneID: localScene.ID}).Delete(Image{}) + + for _, u := range obj.Covers { + tmpImage := Image{} + db.Where(&Image{URL: u}).FirstOrCreate(&tmpImage) + tmpImage.SceneID = localScene.ID + tmpImage.Type = "cover" + tmpImage.Save() + } + + for _, u := range obj.Gallery { + tmpImage := Image{} + db.Where(&Image{URL: u}).FirstOrCreate(&tmpImage) + tmpImage.SceneID = localScene.ID + tmpImage.Type = "gallery" + tmpImage.Save() + } + + resp.WriteHeader(http.StatusOK) +} + +func SceneToExt(in Scene, out *ExtScene) { + out.SceneID = in.SceneID + out.SiteID = "" + out.SceneType = in.SceneType + out.Title = in.Title + out.Studio = in.Studio + out.Site = in.Site + out.Duration = in.Duration + out.Synopsis = in.Synopsis + out.Released = in.ReleaseDateText // TODO: convert + out.HomepageURL = in.SceneURL + // out.Covers + // out.Tags + // out.Cast + // out.Filenames +} diff --git a/xbase/api_scenes.go b/xbase/api_scenes.go new file mode 100644 index 000000000..ba4b4e6b4 --- /dev/null +++ b/xbase/api_scenes.go @@ -0,0 +1,223 @@ +package xbase + +import ( + "net/http" + "strconv" + + "github.com/emicklei/go-restful" + restfulspec "github.com/emicklei/go-restful-openapi" +) + +// http.HandleFunc("/single_file.css", func(w http.ResponseWriter, r *http.Request) { +// http.ServeFile(w, r, "../foo/bar.css") +// }) + +type ResponseGetScenes struct { + Results int `json:"results"` + Scenes []Scene `json:"scenes"` +} + +type ResponseGetFilters struct { + Cast []string `json:"cast"` + Tags []string `json:"tags"` + Sites []string `json:"sites"` +} + +type SceneResource struct{} + +func (i SceneResource) WebService() *restful.WebService { + tags := []string{"Scene"} + + ws := new(restful.WebService) + + ws.Path("/api/scene"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/list").To(i.getScenes). + Param(ws.PathParameter("file-id", "File ID").DataType("int")). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes(ResponseGetScenes{})) + + ws.Route(ws.GET("/filters/all").To(i.getFiltersAll). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes(ResponseGetFilters{})) + + ws.Route(ws.GET("/filters/state").To(i.getFiltersForState). + Metadata(restfulspec.KeyOpenAPITags, tags). + Writes(ResponseGetFilters{})) + + return ws +} + +func (i SceneResource) getFiltersAll(req *restful.Request, resp *restful.Response) { + db, _ := GetDB() + defer db.Close() + + var tags []Tag + db.Model(&Tag{}).Order("name").Find(&tags) + + var outTags []string + for i := range tags { + outTags = append(outTags, tags[i].Name) + } + + var actors []Actor + db.Model(&Actor{}).Order("name").Find(&actors) + + var outCast []string + for i := range actors { + outCast = append(outCast, actors[i].Name) + } + + var scenes []Scene + db.Model(&Scene{}).Order("site").Group("site").Find(&scenes) + + var outSites []string + for i := range scenes { + if scenes[i].Site != "" { + outSites = append(outSites, scenes[i].Site) + } + } + + resp.WriteHeaderAndEntity(http.StatusOK, ResponseGetFilters{Tags: outTags, Cast: outCast, Sites: outSites}) +} + +func (i SceneResource) getFiltersForState(req *restful.Request, resp *restful.Response) { + db, _ := GetDB() + defer db.Close() + + // Get all accessible scenes + var scenes []Scene + tx := db. + Model(&scenes). + Preload("Cast"). + Preload("Tags"). + Preload("Filenames"). + Preload("Images"). + Preload("Files") + + if req.QueryParameter("is_available") != "" { + q_is_available, err := strconv.ParseBool(req.QueryParameter("is_available")) + if err == nil { + tx = tx.Where("is_available = ?", q_is_available) + } + } + + if req.QueryParameter("is_available") != "" { + q_is_accessible, err := strconv.ParseBool(req.QueryParameter("is_accessible")) + if err == nil { + tx = tx.Where("is_accessible = ?", q_is_accessible) + } + } + + // Available sites + tx.Group("site").Find(&scenes) + var outSites []string + for i := range scenes { + if scenes[i].Site != "" { + outSites = append(outSites, scenes[i].Site) + } + } + + // Available tags + tx.Joins("left join scene_tags on scene_tags.scene_id=scenes.id"). + Joins("left join tags on tags.id=scene_tags.tag_id"). + Group("tags.name").Select("tags.name as release_date_text").Find(&scenes) + + var outTags []string + for i := range scenes { + if scenes[i].ReleaseDateText != "" { + outTags = append(outTags, scenes[i].ReleaseDateText) + } + } + + // Available actors + tx.Joins("left join scene_cast on scene_cast.scene_id=scenes.id"). + Joins("left join actors on actors.id=scene_cast.actor_id"). + Group("actors.name").Select("actors.name as release_date_text").Find(&scenes) + + var outCast []string + for i := range scenes { + if scenes[i].ReleaseDateText != "" { + outCast = append(outCast, scenes[i].ReleaseDateText) + } + } + + resp.WriteHeaderAndEntity(http.StatusOK, ResponseGetFilters{Tags: outTags, Cast: outCast, Sites: outSites}) +} + +func (i SceneResource) getScenes(req *restful.Request, resp *restful.Response) { + var limit = 100 + var offset = 0 + var total = 0 + + q_limit, err := strconv.Atoi(req.QueryParameter("limit")) + if err == nil { + limit = q_limit + } + + q_offset, err := strconv.Atoi(req.QueryParameter("offset")) + if err == nil { + offset = q_offset + } + + db, _ := GetDB() + defer db.Close() + + var scenes []Scene + tx := db. + Model(&scenes). + Preload("Cast"). + Preload("Tags"). + Preload("Filenames"). + Preload("Images"). + Preload("Files") + + if req.QueryParameter("is_available") != "" { + q_is_available, err := strconv.ParseBool(req.QueryParameter("is_available")) + if err == nil { + tx = tx.Where("is_available = ?", q_is_available) + } + } + + if req.QueryParameter("is_available") != "" { + q_is_accessible, err := strconv.ParseBool(req.QueryParameter("is_accessible")) + if err == nil { + tx = tx.Where("is_accessible = ?", q_is_accessible) + } + } + + q_site := req.QueryParameter("site") + if q_site != "" { + tx = tx.Where("site = ?", q_site) + } + + q_tag := req.QueryParameter("tag") + if q_tag != "" { + tx = tx. + Joins("left join scene_tags on scene_tags.scene_id=scenes.id"). + Joins("left join tags on tags.id=scene_tags.tag_id"). + Where(&Tag{Name: q_tag}) + } + + q_cast := req.QueryParameter("cast") + if q_cast != "" { + tx = tx. + Joins("left join scene_cast on scene_cast.scene_id=scenes.id"). + Joins("left join actors on actors.id=scene_cast.actor_id"). + Where(&Actor{Name: q_cast}) + } + + // Count totals first + tx.Count(&total) + + // Get scenes + tx. + Order("release_date desc"). + Limit(limit). + Offset(offset). + Find(&scenes) + + resp.WriteHeaderAndEntity(http.StatusOK, ResponseGetScenes{Results: total, Scenes: scenes}) +} diff --git a/xbase/api_tasks.go b/xbase/api_tasks.go new file mode 100644 index 000000000..43fcfd105 --- /dev/null +++ b/xbase/api_tasks.go @@ -0,0 +1,27 @@ +package xbase + +import ( + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" +) + +type TaskResource struct{} + +func (i TaskResource) WebService() *restful.WebService { + tags := []string{"Task"} + + ws := new(restful.WebService) + + ws.Path("/api/task"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/rescan").To(i.rescan). + Metadata(restfulspec.KeyOpenAPITags, tags)) + + return ws +} + +func (i TaskResource) rescan(req *restful.Request, resp *restful.Response) { + RescanVolumes() +} diff --git a/xbase/bindata.go b/xbase/bindata.go new file mode 100644 index 000000000..d22b0914f --- /dev/null +++ b/xbase/bindata.go @@ -0,0 +1,260 @@ +// Code generated by go-bindata. +// sources: +// data/VGC Sonic 128.png +// data/VGC Sonic.png +// DO NOT EDIT! + +package xbase + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("Read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var _dataVgcSonic128Png = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xb7\x65\x50\x9c\x7d\xd2\x2f\x3c\x40\xd0\xe0\xee\xee\xee\xee\xee\x04\x77\x77\x77\x02\x04\x06\x77\x77\x77\x87\xe0\x9a\x10\x3c\xb8\xbb\xbb\x4b\x82\x0e\x32\x30\xf0\xd6\x7d\x76\xcf\xfb\x3c\xb5\x67\xbb\xaa\x3f\x5c\xdd\x3f\xb9\xba\xba\xbf\xfc\x63\xbe\xa8\xca\xa1\x20\x11\x22\x01\x00\x00\x14\x05\x79\x69\x0d\x00\x00\x10\xfc\x4f\x22\x40\x03\x00\x00\x65\xa3\xc7\x7c\x00\x00\x00\xe5\xa9\x21\x27\x09\x68\x98\x21\x3e\x07\x00\x00\x88\x6e\xf2\xfa\x9e\x00\xc0\x67\xac\x7f\x12\x0a\x90\x9b\x8f\x0f\x00\x40\x7b\xd8\x6b\xe9\x79\xe9\xa9\x28\x0b\x5a\xba\x3a\xb3\x9a\x5b\xb9\x5a\x58\xb3\xfa\x3a\xbb\x01\xfe\x09\x61\x5f\x41\x5f\x67\x37\x67\x6b\x2f\x73\x72\x5f\x67\x27\x17\x4f\x41\x5f\x11\xca\xff\x83\x10\x74\xf1\x14\xfc\xa7\xcc\x46\x49\xfe\x7f\x20\x5e\x8e\x22\x94\x7a\x2a\x5f\xc8\xa5\x5c\x3d\xac\xc9\x79\x58\xb9\x59\xd9\x29\x45\x91\xc8\xc9\xc9\x85\x3d\xac\x6c\x04\x35\xa4\x65\xff\x4d\xf7\xb0\xb2\x11\xa1\xb4\xf3\xf2\x72\x13\x64\x63\xfb\xfa\xf5\x2b\xeb\x57\x2e\x56\x57\x0f\x5b\x36\x0e\x01\x01\x01\x36\x76\x4e\x36\x4e\x4e\x16\x0f\x2b\x1b\x16\x4f\x3f\x17\x2f\x73\x5f\x16\x17\x4f\xaa\x7f\x89\xfc\x5f\x1d\x69\x6b\x4f\x4b\x0f\x7b\x37\x2f\x7b\x57\x17\xf2\x7f\xbe\xcd\x2d\x5c\xbd\xbd\x44\x28\x29\xff\x8d\xf9\x57\xfc\xcb\xc8\xcb\xde\xe6\x7f\x9c\x5c\x3c\xff\x3d\x96\xa5\xab\x33\xdb\x3f\x1d\x36\x0e\x56\x76\xb6\xff\x46\xf3\x75\x76\xfb\xef\x2c\x5f\x73\xb7\x7f\x91\x44\xff\x87\x25\xfc\x8f\x94\xa0\xbe\x86\xb5\xa7\xab\x93\xf7\x3f\x3f\x25\xca\xc7\x29\xcc\xf6\xff\x14\xff\x93\x20\xe5\xea\xec\xe6\x61\xed\xe9\xf9\x4f\x8f\xe7\xdf\xf8\xff\x5d\xfb\x4f\xbc\xde\x7f\x33\xd0\xfb\xef\x06\xbe\xce\x6e\x82\x52\x1e\xd6\xe6\x5e\xae\x1e\x5a\xae\xae\x4e\xa2\xb2\x4e\x7e\xf6\x2e\xb6\xe4\x2a\xd6\xe6\x5e\xe4\x12\x96\xae\x1e\x2e\xe4\x3c\xac\xbc\xac\xbc\xc2\x6c\xff\x09\xfc\x0f\x0d\x15\x57\x2b\x7b\x1b\x3f\x69\x73\x2f\x6b\x51\x4e\x76\x0e\x7e\x16\x76\x3e\x16\x0e\x4e\x2d\x0e\x4e\x41\x1e\x6e\x41\x4e\xee\x7f\xd1\xff\x17\xe6\xff\x2e\x89\xed\x3f\xb6\xf4\xaf\x13\x60\xfb\xf7\x0d\x88\x22\x09\xb3\xfd\xff\xf7\x24\x8a\x14\xae\x9b\x7b\x08\x00\xf0\xf9\x2b\x48\x4b\x68\xf9\x42\x4d\x17\x7c\x9b\x6a\x3f\xd7\x5e\xb9\x7e\xca\x6b\x5c\x14\xc0\xb1\xa9\x94\x12\x12\x89\xd7\xf3\x97\x67\x18\x2b\x21\xb1\xfb\x8c\x13\xbb\x47\xe1\x20\x8e\xf9\xce\x31\x28\x06\x4d\x18\x42\x69\x50\x7c\x40\x46\x99\xca\x1b\x2b\x1b\x13\x23\xab\x80\x7c\x5c\xcc\x9b\x8b\x5e\x86\x28\x1b\xaa\x07\x23\x8f\xa6\x51\xbc\x70\xef\x15\xa9\x51\xdc\xb6\x28\xe0\x65\x23\xc8\x55\xb0\x78\x7a\xfd\xb3\xa1\x7d\x05\xf8\xd8\xba\x7b\xce\xd0\xee\xfb\xb5\xef\xa9\xf7\xda\xdb\xf4\xd2\x60\xd5\xe0\x09\x22\xfc\xed\xfa\x6b\xdf\x3d\xe8\x6a\xa5\xb1\xa7\x8d\xb5\xbe\xb5\xa2\xf2\x5d\xb9\x87\x34\xeb\xed\x77\xea\x2a\x67\xcf\x5c\x93\x52\xd6\x4a\xc4\x9a\x33\x83\xe9\x37\x76\xb8\xc8\xce\x7e\x1b\x4b\x0d\xcd\x78\xdb\xba\x80\x69\x61\x61\x02\xea\x36\xfa\x9e\x0b\x84\x5c\xeb\xa6\x3f\x65\xaa\x8e\x2e\xdd\xf6\x79\x68\xb3\x42\xfd\x2e\xfe\xfd\x20\x3b\xf8\x35\x15\x22\x6f\xdf\x1d\x28\x9f\x45\x98\x06\x39\x98\x2e\xf9\x93\xe5\xd4\xa2\xaa\xf6\x2b\x46\xbc\xe0\x65\xa5\x4b\x8e\xe2\xa5\xa5\xc9\x5b\x4d\xd7\x35\xcb\x1e\x6d\xfc\x4f\x4a\xb3\xa9\x7c\x52\x3b\x27\xf0\x95\x34\x4f\x37\x4d\xdd\x37\x5c\x9b\xa8\x1a\x81\xfb\x07\x75\xc7\x3b\x1d\x7a\xdf\x94\xfc\x75\x84\xd2\x6b\xf6\x4f\xae\x8e\xf5\xfc\x2b\x4f\x8f\x08\x27\xc9\x58\xde\x29\x87\x4b\x65\x1f\xd2\x0f\xc9\xb9\xa1\x8d\xdd\x8d\xa1\x98\xf5\xda\x79\xcb\x66\x81\xb1\xd5\x58\xd3\x43\x75\x25\x9c\x6d\x9c\xbf\x59\x6d\xf2\x90\xae\x9b\x27\xa6\x83\xa3\x7b\x46\x91\x52\x19\x5b\xf7\xa2\xfd\x74\xbc\xc2\x26\x69\xae\xa6\x09\x94\x37\x7f\xdb\x77\x21\x23\x57\x4e\x3f\x7f\x48\xbd\xc9\x70\xd7\xc8\xaa\x92\x96\xd4\x64\xcd\xf0\x27\xf8\xfd\x25\xb5\x15\x6c\xfd\x79\xda\x56\xa1\x93\x80\x47\xdb\x0e\x33\x22\xe9\xf0\x93\x01\xca\xca\x53\xd3\x82\xf9\x2f\x4a\x1f\xef\xc8\x9b\x5d\x74\x3c\x05\x5e\x72\x5d\x38\x68\x6c\x52\xf5\xf9\xa7\x7a\x71\x25\x6c\x32\x2f\x4c\xa5\x4d\x86\x1f\x5c\x7c\x67\xf0\xb6\x97\x19\xd7\x1a\x94\xcf\x62\xc8\xb4\x19\xfc\x9a\x0a\x3c\x6f\x18\xa0\x0d\x8a\xbb\x40\x5a\x37\x8d\x34\x47\xf0\xbc\xc5\xc0\xe3\xaf\xf0\xf7\x8d\x31\xde\x29\xf2\x53\xdd\xe3\xb8\xac\x02\x2d\x66\x34\xad\xee\x19\xfe\xd7\xdc\xf7\xf4\xdd\x9b\x06\xef\x38\xf5\x76\xe6\x60\x5b\x81\x5c\xc5\xed\x48\xd3\x6c\x17\xe4\x32\xbe\x48\xfd\x9c\x81\xef\x85\xa8\x6e\x04\x6b\xc8\xaa\xe6\x96\x2f\xc8\x07\x6c\xc9\x44\xbf\x84\xf1\xe7\xb8\xbd\x87\x05\x96\xe2\x6b\x40\x80\x74\xbf\xda\x6b\xcc\xbd\x50\xd8\xe0\xad\x6d\xd7\xdb\x8d\xf2\x48\xc4\xa6\x7d\x5b\x4a\xd5\x46\x97\xed\xe6\xe8\xe4\x99\x9e\x5f\x53\x6c\xec\x58\x5c\xf2\x6a\x23\x0f\xa3\x6f\xbc\x08\xb2\x5c\x56\x62\xd9\x13\x9c\x08\x66\xcc\x41\xe1\xcf\x71\x42\x3f\x30\xaa\xea\x06\x4a\x39\x13\x7a\xf5\x52\x1f\xff\x0a\x26\xbb\xff\x55\x57\xfc\x55\x87\x65\xc9\x9c\x27\x82\xee\x4c\x57\x7f\x8a\x2a\x55\x4f\x80\x25\x9f\x95\xc2\xc7\x13\x86\x0f\xcb\x85\x99\x75\x65\x6a\x32\x92\x67\x62\x6d\xbd\x4d\x6c\x3d\xe9\xe5\x38\x3e\xb8\xf6\x75\xb1\x79\xf6\xdf\x33\x92\x98\xf1\x91\xac\x21\xb3\xd2\x1a\xba\xd9\x66\xb1\x16\x1c\x0b\x5e\x2b\x1e\x57\xfe\xd6\x50\x6c\x5c\x05\x54\xb0\xaa\x6a\x2b\xca\xcc\xc5\xc1\xa2\x25\x13\x58\x11\x9d\xc5\x8d\x2f\x97\x33\x42\x1b\xfd\xf4\x2a\x79\x81\x92\x93\x78\x01\xc3\xe9\x0d\x83\xf4\x90\x4b\xb1\x15\xd5\xde\xca\xd7\x79\xfd\xdb\x50\x12\x69\xec\x89\x20\x03\xb4\x9f\x0e\x29\x5b\xbc\x47\xaa\x9e\x7d\x03\x33\x6f\xf1\xd0\x62\x0a\x0b\xce\xef\x76\xd3\x6d\x3e\x71\xf4\x9b\x18\x66\x18\xd0\x0b\x64\x44\xbf\x0d\xf5\xdf\xc7\x47\x3c\xa6\x8b\x94\xc9\x12\xde\x7b\x9e\x6c\x6f\x18\x61\xe1\xdb\x26\x1d\x16\x8a\xf0\xea\xa1\x7a\x1c\xce\x3b\xdd\xd9\x6e\x65\x2e\x2c\xbe\xe5\x93\x0e\xd8\x5e\xa3\xdc\x5b\x20\x50\x87\xe1\xc6\xc1\x89\xa2\xa8\xf0\x7e\x97\xcb\x9e\x85\xde\x29\x82\xf1\x48\x2c\x57\xd8\x5f\x8f\x14\x98\x57\x2f\xfe\x4d\x29\xd2\x09\x7b\x3a\x9e\x7a\x11\xb1\xfd\xe4\x05\xe7\x4d\x3a\x9d\x26\x79\x26\xe9\x2e\x73\x32\xa5\x11\xec\xe4\x89\x00\xbb\xe6\x12\xf6\x12\xf8\x43\x1e\xc6\x8c\xd2\xce\x74\xf1\x7b\x9a\x39\xd4\x2f\x09\xe9\x55\xd2\xcb\xd8\x14\x20\xe9\xef\x85\x91\xaf\xb8\x2d\x8e\xc6\x22\xce\x7c\xd1\xf3\x4c\xcb\xb0\x4d\x61\x09\x87\xb6\xc4\x41\x1c\x74\xd8\xdb\xe1\xca\x5a\x16\xca\x09\xb2\xfa\xd0\xa3\xaa\x8c\x82\x4e\x7b\xdb\x74\x7d\x56\x89\x1c\x5a\xee\x4b\xbc\x56\x47\x1a\x2e\x93\x05\x22\x33\x16\x57\xf1\x5c\x63\xb2\xb1\x42\x86\x9f\x23\x32\x22\x9c\xcb\x2d\x86\x4a\x03\x2f\x8a\xa1\xad\xd0\x36\x0e\x86\x27\xe2\x9a\xed\x2d\x1c\xcd\x79\x84\x94\xa9\xf8\x21\xb6\x50\x41\x21\xf8\xc4\x39\x0f\xa9\xd9\x9d\xf4\x2b\x60\xfc\xce\x71\x42\xfc\xbd\x5b\xa8\x22\x1a\xff\xee\xc9\xbf\x24\x21\x7f\xc4\xc7\x7a\xc1\xf2\xb1\x74\x4d\x26\x3c\xb3\x18\x41\x48\xd8\xc0\x11\xa7\x48\x81\xb8\x84\x66\x01\xee\x40\x61\x60\x2d\x82\xa5\x8e\xda\x8b\x6d\x9e\x0a\x8d\x07\xc7\x4f\xb5\x53\xaf\xa4\x78\x6f\xff\x5f\x6f\x1f\x08\x8d\xb3\x50\xc2\xe8\xc6\x74\x9d\x96\xa9\xe3\xbf\x68\x2f\xe5\x09\x79\x47\x1f\xcf\xaa\x0f\x24\xdd\x69\xbe\x90\xfa\x9f\xd0\xc9\x2a\x65\x95\xb8\x42\x60\xdd\xa4\xc5\xa1\x17\x05\xed\x22\xf1\xa5\x82\x7b\x58\xdb\xd9\xed\x58\xbf\x5b\xe4\xec\xce\x40\x0b\xfd\xb4\x55\x89\x18\xd6\x23\x28\x2b\x82\x8a\x6b\x45\x91\x9d\xe4\x74\xbe\xdb\x26\x44\xab\x23\x75\xce\x12\xa5\xf2\xdd\x81\xfd\xf1\x68\xa3\x46\xb8\x25\xde\x4a\x96\x52\x87\xd9\x36\x5c\x73\x31\x8d\x0c\x43\xd8\x74\xee\xcd\x74\x03\xf5\xbb\x32\xf0\x60\xdc\x04\x39\xe2\xfa\x10\x8d\x0e\xcd\x3c\x3f\x76\x62\xbf\x27\x84\x91\xcf\xa5\x7a\xbd\x9c\x5b\xe1\x3e\xba\x10\xde\x3b\xdd\xb6\x5e\x9b\x9f\x45\x80\x35\x7a\x81\x2c\x7d\x12\x2d\x08\x21\x0d\x43\x93\x1d\x91\x5b\x58\x00\xeb\xc6\x29\x1a\x87\x0e\xd7\x3e\x9b\x87\x8b\x2b\x4f\xdb\x88\x93\x74\xb2\x2a\xf6\x14\x89\xbd\xe7\x33\xb6\xb5\xdb\x70\x25\xb8\x25\xfb\x46\x74\xa3\xd6\x69\xc7\xa9\xed\x87\xd6\xa9\x55\x4a\x1e\xac\x76\x85\xb4\xf4\xa2\xcb\xad\x3c\x62\x76\x94\x76\x1c\x41\x8e\x1f\x6a\x4d\x80\x1a\x61\x07\xa3\x75\x26\xbd\x8b\xb1\x27\x2c\x4a\x1e\x2f\x4e\x37\xc1\x11\xe6\xbb\xcf\x38\x17\xaf\x88\x0d\x11\xa1\x91\x74\x41\xd6\x74\x4b\x05\x78\x08\xfb\xf5\x3b\x92\xd9\x6c\x73\x40\xad\xef\x07\x23\xe5\x43\xa0\xbc\x37\xe3\x74\x5d\x99\x79\x89\x76\x8d\xaa\x42\xbb\x1b\x61\xbf\xdb\xf2\xfa\x35\xee\xee\x52\x2e\x21\xac\x7e\x04\xe0\x62\xea\x9c\x2a\xcb\x1f\x96\x20\xd8\x3f\xd3\xbf\xea\x76\xda\xc7\x02\x99\xc5\x84\x82\xba\x76\x46\xd1\x94\x2c\x2a\x04\x64\x37\x54\x56\x0b\xa2\xfb\x3c\xac\x01\x48\xf3\xb2\x93\x38\x5c\x56\x90\xd9\x5c\x99\xf1\x75\x24\x7e\xf3\xb9\xb1\x3f\xbc\x34\xfe\xe2\x21\x4f\x81\x89\x36\x99\x8c\x69\x0a\xd0\x6a\x4b\x56\xb4\xd7\x2c\x93\x7c\x45\xc4\xd6\xda\xc0\x3f\x84\xfa\x7c\x7d\x11\xf7\x5b\x8b\x53\xd0\xd3\xcf\xab\xe2\x20\xc4\x78\x78\xb0\xba\x5d\x6a\x92\x97\x1d\xad\xaf\x2b\x8c\x43\x3b\xde\xc0\xf1\xdd\x37\xf6\xc8\x49\xc4\xa1\xcc\x68\x8b\x7a\xb6\x37\x32\x0b\xc3\xaa\x72\x11\x4d\x54\x29\x82\x8b\xf9\x90\xc4\x8c\x0f\x62\x75\xf3\x4e\x9d\xdf\x7f\xeb\xb3\x40\x33\xcc\xa8\xfd\x19\x50\x70\x61\xc7\x3d\x67\xbb\x68\x37\x53\x10\x7b\x99\xa9\x58\xa3\xb6\xad\xf6\xbc\x5f\x5e\x31\x61\xe6\xd5\xb0\x9f\x84\x81\x89\x95\x3b\x2e\xa5\xd6\x1e\x31\x5d\xe4\x0d\x5a\x81\xd3\x50\xd5\x1f\x9e\xe0\x7f\x2c\x00\xc2\x43\xd7\x2a\x51\x9f\xf3\xa7\x4b\x93\x9a\xa2\x23\xf6\xfc\x11\x6a\x19\xc7\x9a\x09\x64\x9f\xbf\x3f\xdf\xf7\xfe\x4c\xe9\xa7\x4d\x1d\x81\xaa\x42\xd7\xca\x82\xc2\x54\x4c\x99\x75\x8a\xad\x2e\xf3\xe4\x4a\x78\x06\xe5\xea\x2a\x67\x8e\x19\x46\xa2\xb9\x0b\xcd\x1d\x34\x91\xeb\x7f\x6e\x0d\x40\x11\xfe\x45\x58\x96\x2f\x92\x31\x46\x18\x8b\x9f\xf7\xbb\x2b\x4a\x53\x29\x90\x64\xcc\x01\x0f\x7d\xdd\xb2\xda\x62\x89\x9c\x28\x67\x5a\x9c\x5d\xa2\x95\x80\x75\xf0\xca\x1d\x0b\x3b\x0d\x82\x83\xf1\x2d\x73\x61\x54\xf4\x10\x60\x51\xfe\x61\x75\xb3\x11\x2c\x10\x13\x3f\x49\x37\x2e\xbe\x05\x93\x11\xd6\x7a\x48\x18\xa2\xbe\x19\x1e\x38\x25\x5f\x40\x75\x35\x83\x3e\xe5\x14\x39\xe2\xa3\x5c\x11\x7c\x73\x4e\x25\x5b\xd8\x96\xa2\x5a\x68\x52\xf1\xb4\x10\x52\xa4\xed\x24\xda\xd1\x0e\xaf\x53\x2a\xbe\xdd\xc2\x51\x59\xa3\xb2\x40\x70\x8d\x7b\xfc\xc0\xf8\xf7\xcc\xf5\x56\x40\x76\xfd\xb5\xe0\x3d\x32\x9a\xfe\xb0\x9e\x66\x5f\x2f\x77\xa0\xff\x68\xef\x71\x7c\xaf\x1d\xaa\x94\xbc\xee\xd6\x5d\x80\x1b\x85\x2f\xd0\xc0\x55\x56\x5a\x57\x65\xa0\xdd\x16\x67\x10\x1e\xa9\x6a\x43\x77\x31\x7a\xe2\x34\x1d\xe8\xa3\x7e\x8a\xb1\x8b\x22\x56\x97\xab\xaf\x6c\xb6\x91\xca\xbd\xd8\x12\x81\xcd\x54\xef\x70\x81\x68\xb2\x6e\xfc\xc7\x7f\xc6\x43\x9c\xe8\x3d\xd5\xd9\x9c\x99\xf4\xa1\xb3\x09\x41\x7d\xcb\xcd\xbf\x8a\xc7\x6a\x06\xb1\xda\xb6\x51\xdf\x57\x96\x85\xae\x3a\x84\x0c\x25\x1b\xd9\x09\xce\xc6\x38\xaa\x91\x9a\x8a\x84\x70\x19\x36\xca\x29\x6c\x54\x71\x33\x76\x28\x77\xc1\x8e\x06\x9f\xac\x4e\x0b\xc9\x54\xa0\xe5\x08\x0a\x9f\x34\xc5\xa5\xab\xaa\xf7\x6d\x88\x0d\x1f\xe0\x4e\x20\x57\xf4\xb9\x9a\x0d\xa3\xd6\x47\x8b\x81\xea\x50\x89\xec\x7d\x8c\x6b\x78\xdf\x24\x5d\x8a\xc4\x26\x70\xab\x4a\x00\x44\xfc\xd9\x6b\x62\x05\xcc\x43\xb0\x8e\x5c\x90\x72\xb0\xc1\x96\x18\x8f\x45\x7f\x9e\xcd\x0c\x84\xce\x21\xb7\x1b\xe0\x42\xb1\xe9\x16\x6c\x22\x20\xee\x45\x9b\x7b\x19\xbe\x8a\xb4\x9d\x51\xea\x62\xbf\x9c\x97\x31\x8f\xef\x12\xfc\x42\x6a\xa5\x48\x56\xef\x56\x2d\x2b\x0c\xfa\x33\x26\xa5\xf9\xe4\x8e\x55\xb6\xef\x5f\xc8\x4e\x3a\x39\x8f\xab\xe0\xc1\x92\x5d\xe9\xb6\x9f\x85\xaf\x2b\x87\xe7\x92\x35\x1e\xe0\x1b\x9b\xad\x48\x72\x2e\x76\x42\x97\xa2\x09\xe1\xc8\x4c\x4d\x31\x2f\x97\xdf\xa6\xbe\xea\x92\xc8\xe7\x0d\x4b\x12\x23\x59\x43\x4e\xec\x45\xfb\xc2\x5b\x00\x80\xa1\x45\xcc\xb1\xf2\xfa\x73\x6a\x3d\xed\xd3\x71\x18\xda\x19\x1f\x0e\xa3\x69\xcc\x0b\xaf\x73\x73\x95\xd6\x68\xba\xf2\x8c\x8e\x17\xb8\x78\x48\xa9\xd7\x00\x73\x1f\x37\x30\xd9\xf0\xeb\xc6\xec\x39\xa6\x2a\x79\xff\x81\xa2\xda\xf2\x6f\xbb\xda\xdd\x6b\xfc\x6f\x0f\x59\xdf\xcb\x31\x99\x47\xe4\x0b\x76\xbf\xeb\x98\xdc\x2d\x0e\xca\xa0\xde\xd9\x68\xe0\x65\x3b\xd0\x16\x83\x4a\x98\x05\x74\x03\x51\xcc\xac\x24\x43\xce\x7c\x77\xa6\x1f\x9a\x7e\x82\x52\x4f\x93\x8c\x27\xd2\xd0\x12\x5f\xf9\xb4\x82\x36\x7e\x63\x46\xec\x09\x30\x78\xaa\x51\x6e\x6e\x73\x8e\xa4\x92\x7b\x5d\xb2\x9d\x00\x1e\xe3\x03\xfe\xce\x2c\x62\x4c\x5d\x6f\x70\xf7\x59\xc2\x92\x77\xe6\xbb\x4b\x0a\x65\xee\xbf\x8b\x52\x51\xc6\xa4\x01\xa0\x6b\xa1\x92\x7f\xd1\x8f\x6f\x4d\x7c\x82\xb9\xda\x4e\x54\x02\xd9\x7c\xe1\x1f\xf5\xd0\xd8\xa9\x5f\x49\xe2\xcd\xf4\xd0\xc4\x94\xe1\x1b\xe2\x19\x77\x8d\x95\x1d\x46\x6e\x5b\x32\x2b\x4c\xea\x45\x33\x8b\xe0\x99\xc0\x36\x09\xf0\x75\xb2\x24\x47\x0d\xee\x7a\xbe\x25\x28\xb7\xcd\x67\x0a\x6e\x07\x68\xa5\xab\x50\xc8\x87\x1d\x45\xfc\xce\x69\xfe\x29\xa5\x14\xcd\xbe\x37\xac\x95\x84\xcd\xc4\x4f\xc4\xe3\x44\xaa\xb0\x81\x6d\x41\xc4\x87\x8f\x33\x23\xec\x53\xcb\x81\x8c\x7f\xce\x63\xd6\xb6\x7c\x6d\xdf\x61\x0e\x40\x2e\x9f\x88\x91\x16\x6b\xdb\x2f\x32\x46\x23\xdf\xa7\xc5\xa5\x7f\xd5\x66\x9e\x45\x35\xe6\x2e\x1f\xdd\xd8\x8d\xd6\xe7\xb8\xc7\x0e\x25\xc5\x15\xf7\x33\x2b\x62\x18\xaf\xea\x85\xee\x56\xd6\x27\xa2\x0a\xeb\x82\x91\x2f\x4c\x46\x58\x3c\x62\x07\x64\x9b\xb0\xb3\xd4\xd2\x80\xb1\xa1\x81\x66\xcf\x2d\xa3\x82\x31\x23\x34\x2f\x08\x8b\xbc\x6b\xcd\xf1\xac\x3a\x88\x67\x22\xf4\xfd\xb4\xb1\x22\xdf\x05\x43\xa6\x99\x1b\x4f\xd8\x20\x02\xb2\x09\x70\x34\x80\xaa\x1d\x44\xd5\xf9\x66\x69\x9f\xe0\x45\xa1\xc3\x1c\x06\x23\x03\xb8\xf3\xfd\xfb\xcd\x2f\x45\xb4\xa9\xea\x84\x7f\x43\x18\x33\xe9\x2c\x8a\x0e\x6d\xba\x17\x5c\xdc\x17\x1c\xb6\x8f\xce\x19\x0b\x68\x50\xa0\xe1\x22\xf9\x70\xb3\xa7\xcf\x58\xdc\xdd\xf2\xbb\xea\x4e\x74\xbf\xb1\x03\x10\xa8\x7d\x41\x8a\x2b\x3f\xbc\x33\xa4\x28\xd4\x99\xbf\x0a\x4c\x69\xfa\x97\x2a\x4e\x60\x0d\xee\x62\xa2\xf5\xab\x5e\x34\x15\xe3\x02\x1d\x33\xdf\xd1\x49\xcf\xb3\x84\x17\xfd\xd9\xc1\xee\x3a\x12\x63\xaa\x8f\xfe\xda\xc9\xe5\x09\xc0\x46\xba\xed\x0b\x9c\xe5\xe7\xed\xc0\x98\x2e\x7f\x22\xc5\xc9\x1d\xc5\xa8\x1a\xeb\x0b\x61\xf6\x0b\xf9\x0d\xf4\xe1\x88\xa4\xed\x66\x7d\x9f\xed\x61\xb4\x05\xb1\x61\x0a\x42\xe4\x02\x65\x28\xcc\xe6\xc7\xbf\x6e\x45\xb0\x0c\x4d\x46\xdb\xc2\x29\x9f\xdb\xd5\x73\xf2\x69\x98\x64\x0c\xcc\x6c\x36\x0e\xf9\xef\x8e\x74\x9e\xe3\xd5\x40\x49\x3d\x24\x03\x58\x6f\xc5\xbb\x5e\x6b\x8c\x5a\x64\xa1\x9e\x20\x26\x9f\xce\xb1\xe8\x2c\xfc\xcd\x06\x8e\x19\x17\x43\xaa\x46\x28\x86\x92\x62\xd6\x52\xb6\xe6\x16\xc4\xc2\x9e\xf5\xfc\xd1\xb2\x6b\x18\x6a\x43\x31\x8f\x46\xc5\xe3\x80\x1c\x28\xd3\xf7\x2c\x3e\xb4\x62\x23\x6c\x0a\x02\x4f\x03\xfb\x0d\x20\x95\xfe\x60\x29\x7d\x9e\x98\x0b\x82\x68\x8f\xa4\x2f\xdd\x91\x4d\x6d\x2f\xd2\x21\x1b\x2c\xb9\xbd\xc0\xb6\x02\x6e\x18\xeb\x97\x5d\x93\x26\xea\x7c\x62\x58\x2f\x07\x62\x12\xa6\x01\x26\x64\xe6\xf3\xfc\xf6\xac\x29\x9b\x42\xa2\xc3\x1a\x20\x43\x11\x56\x07\x43\x01\xff\x94\xa5\x8b\x60\x3b\xfe\xc6\x77\xc3\xe4\xc3\x3a\x8d\x58\xec\xf0\x72\xbd\x70\x66\xde\x44\x3a\x72\x16\x44\xfa\x92\xb6\x78\x9e\x1b\x8e\x79\x46\x56\xd1\x56\x13\x9c\xc2\x9a\x54\x57\xc6\x46\xa2\x59\xfa\x11\x2d\xb9\x6c\x38\x0e\x51\x17\x6a\x7d\x47\x23\xc4\xe9\xdd\xf7\xe1\x48\x38\x99\xaf\x4a\x18\x51\x64\x04\xb5\x4e\xa4\xc2\xa0\xc6\x26\xe8\x86\x9e\x71\x46\xe4\x81\x88\xd1\x56\x62\xe9\xe1\x82\xee\x04\x85\x60\x84\xbc\xd8\x08\xa6\xca\x31\xb7\xa2\x2e\x22\x29\xba\x96\x6f\x28\x5f\x24\x34\x19\xaa\x64\x18\x1c\x2c\x7b\x7a\x28\xbf\xca\xdb\xf3\x90\x53\x0e\xb1\xee\xe6\xdb\x15\x31\x67\x60\xd4\x87\x2c\xa7\x61\x38\x5b\xa5\x86\x61\x59\x31\x70\xa6\xa2\xf2\x96\x96\x27\xb9\x08\x91\x21\x78\xbe\xc9\x0c\xaa\xdd\xdf\x4f\x49\x65\x38\x40\x04\xb1\x74\x28\xdf\x45\x3f\xb1\xdb\x66\x85\x45\x85\x84\xf8\x76\x80\x45\x86\x13\x4d\x11\x38\xec\xa6\x34\x8b\xc2\xe6\x8b\x7a\x18\xb2\xbf\x52\x2a\x61\xda\x17\x3a\x0f\x93\xe7\x8c\x38\x04\xa2\xf6\xeb\x23\x61\x35\xad\x72\x61\x45\x7c\x95\x2c\x1d\xdd\x74\xf4\x52\x47\x93\x6e\x52\x09\x53\xbc\xd1\x29\x96\x6c\x72\x60\x2b\xc4\x8a\xd3\xf7\x83\x52\xaa\xc8\xed\xd3\x40\xf2\x90\x12\x1a\xb2\x32\x24\x68\xa9\x41\xc5\x63\x93\xc7\xe9\x84\x3d\x5f\x5f\xc4\x16\x41\x46\xcc\x12\x41\x6d\xda\x37\x90\x93\xc9\xe4\x84\x9a\xaa\xe5\xe4\xb9\xbf\xb1\x64\x6e\x9c\xdf\x0d\x2d\xb3\x9a\x44\xd4\xee\xa0\x78\x63\xae\x11\xf5\xc7\xe8\x78\x90\x2e\xd5\x45\x66\x1b\x1b\xec\xaf\xb0\x2d\x66\xb1\xde\x40\x02\x40\x85\xc9\x94\xd3\x2e\x26\xd0\xe2\xb5\xbf\xd5\x46\x24\xe0\x18\x6c\x13\xe4\x2b\x67\xe2\x6c\x2c\x1c\x4f\xf4\xcf\x23\x6a\x85\xc3\xd1\x54\x07\x9c\x32\xc6\x7b\xea\xa8\x90\x0c\x0c\xcd\x2a\x0e\xb7\x2b\x61\x2a\x53\x0a\x7e\x9a\x26\xd3\x36\x84\x98\x2d\x58\x5d\xf9\x1f\xe3\x75\x7a\x4c\xd4\x6e\x6b\x99\x02\x84\x20\x1c\xbc\x41\x85\x65\xe1\xc5\xc8\xe9\xb9\x44\xfd\xb2\x2f\xce\x76\x36\x21\x8a\x9f\x78\x90\xda\x1a\xf5\xb7\x9f\x37\x3c\xa3\x11\x92\xd4\xcb\x03\xb9\xac\x09\xb3\x9b\x62\xcc\x1a\xa7\x28\x0d\x68\xd6\xa8\xb1\x3a\xc2\x13\xbf\xb5\x60\x8e\xc8\x49\x08\xef\x38\x69\xcf\x7d\xa7\xe5\xbf\x08\x2e\x4f\x1c\xf1\x8f\xc1\x40\x81\x18\x53\xa9\x78\x66\x6b\x44\xff\x58\xdc\xa0\x23\x82\xf1\x75\xa0\xfe\x65\x18\xaa\x98\xe7\x19\x07\xac\x29\x55\x87\xea\x4e\x07\xea\xfa\xcd\xd0\xbb\x95\x84\x1e\xad\xcf\xdb\x26\x71\xe4\xb1\x11\x99\x39\xd5\x12\x37\xc4\x3b\x72\x19\x59\xaf\x33\x48\x93\xe9\x45\xf0\xb5\xeb\xc9\x8f\x04\x54\xab\x51\xe0\xcb\xe7\x88\x2f\x1f\x8c\x49\x03\x10\x6c\xef\x8d\xa8\xaa\xdb\x44\xa6\x5d\xe5\x95\x3a\x95\x51\xa5\x31\x35\x05\x34\x16\x4d\xe0\x89\x67\xca\xad\x08\xfb\xa4\x61\x9a\x18\x26\x9e\xc0\x2c\x26\x21\x53\x41\x89\x40\xe9\x4c\x54\x09\xfc\x2a\xb1\x05\xd7\xe0\xf4\x57\x87\xe2\x22\x6e\x6d\xe2\xab\x73\x82\x44\x60\x11\xa3\xd9\xfe\xc0\xf1\x4d\x60\xb0\x5d\xaa\xf6\x63\xac\x82\x26\x7f\x78\x25\x61\xb9\x5e\x0f\xd3\x0f\xe8\x83\x9a\x0b\x7e\x7c\x76\xd2\x1a\xfb\xf2\xcd\x56\x6d\x1e\x18\x19\x82\x18\x0b\xf4\x6e\x66\x46\xdc\x59\x02\x76\x45\xcb\x5c\xc9\x36\x58\x3d\x54\x27\x3b\x42\x1a\x38\xc9\xcf\xde\x83\x64\x52\x8a\x3a\x55\xe5\xeb\xc1\x62\x0d\xef\xa8\xc9\xb5\x35\x01\x7a\xa5\x78\x4f\xce\xe9\x6b\xb3\xcb\xae\xc5\xa1\x13\xda\x2c\x60\xb1\xe3\x6b\x18\x2f\x77\xfe\xda\x23\xda\x65\x95\x8a\x2f\xb2\xc8\x88\x32\xe1\x9e\x4a\x3a\xe7\xda\x38\x07\x86\x92\x15\x18\xf5\x3b\x1b\x7b\xfa\x2a\xf6\xe2\x31\xb5\x55\xf1\xf6\xe3\xc2\xc2\x7b\x52\x23\xa4\x43\x41\xfc\x4d\x44\xc0\xa5\x05\x54\x3c\x62\xac\xcf\x54\x7e\x0a\x82\xf3\x45\x3d\xb3\xb0\x18\x22\xb0\x8b\x1d\x3a\x9a\xb0\xc2\xd6\x0a\x4e\xf1\x24\x15\xe8\x2c\xfc\x20\x91\x85\xbe\x1f\xd7\xe6\x49\x40\x63\xa8\xd8\x7d\xd2\x9f\xe5\x25\xf6\xf2\x24\xe9\x25\xa4\xa8\xf9\x92\x63\x3f\xea\x3a\x12\x3e\xa9\xbf\xa5\xe7\x81\x6a\xa2\x1b\x12\xc1\x2c\x55\x15\x5f\x55\x11\xc9\x77\x3d\xa4\x25\x8e\x7c\x12\x4a\xbb\x52\x7f\x4f\x87\x8c\x09\xdb\x9b\xc9\xe3\x52\x10\x43\x2a\xe7\x63\x52\x36\x56\x66\xcd\x1e\x66\x49\xab\x34\x86\xfa\x96\xd9\xb4\x4f\xd4\xdc\xd3\x75\x3b\x6f\x1c\x09\xbe\xb4\xb4\xee\xe9\x95\xfa\x6d\x4e\xce\x72\x52\x23\x6e\x28\x01\xf1\xc2\x11\xd4\xca\x99\x84\x5b\x51\xf6\xc3\x25\x13\x55\xa9\x2c\x01\x37\x6e\xb6\x8b\x92\xbc\xb3\xd0\x2d\x04\xe7\xed\x73\x0e\xd4\x1b\x7d\x2f\xf3\xad\xf7\x0c\xe7\xd0\x35\xdc\x94\x77\x09\x98\xef\xd2\xae\xc5\x50\x3e\x0c\xe5\xd6\xc1\x59\x57\xf1\xf5\xa1\x49\x73\xba\xbf\xf6\xae\x26\x4e\x63\xfa\x08\x8e\x5e\xfb\x0d\x57\x87\x3a\x73\x7e\x53\x68\xa3\x62\x9c\x07\x45\x5d\x73\xa5\x16\x67\x44\xf3\x12\xf6\xb7\x03\xbb\x03\x18\x77\x6d\xe9\x63\x17\x14\x5b\x32\xcc\x38\xa7\x77\xc7\xda\xde\xc1\xce\xd5\x9f\x5a\xce\x37\x35\x48\xab\x4a\x68\x9e\x07\x91\x52\x9a\x94\x27\xc6\xc2\x2b\xb9\xa2\x2d\x9e\x3e\xe9\x10\x8a\x87\x6b\xcd\x6c\x5e\xc3\xb4\x39\x94\x5d\x19\xe8\x32\xfd\x42\x43\x40\xdd\x40\xb5\x45\x8f\x84\xb2\x8b\xc2\x5e\x5f\xa5\x7f\x3f\xc9\xfa\x58\x5f\xe7\x31\xed\x71\x79\xe6\x45\xe9\xfc\x23\x25\xd6\x4b\xfa\x53\x25\x92\x31\x70\x85\x19\x01\x53\x72\x2e\x75\x95\x1a\xa1\x6a\x6a\x7c\xf5\x2d\x92\x2c\xa8\x68\x5f\x7b\x5d\x65\x4c\x7c\x50\x38\x63\x18\xd5\xdf\x47\x70\x5f\x2d\x54\x08\x02\x09\x04\x1c\x8f\xcc\xca\xe5\x9e\x1a\x59\x23\x3b\x69\x7e\x82\xb3\xa0\x73\x88\x9f\xea\x89\xd2\x5f\x54\xd8\x02\xa4\x07\x8b\x9c\xd3\xd6\x26\xdf\x7c\x8b\x12\x8e\x77\x9c\xba\xb0\x8f\xd6\x9c\x4d\x82\x81\x5a\x99\xb3\x4c\xf0\x20\x65\x21\x6f\x33\xc9\x34\xde\xaf\x5b\x34\xdb\x2c\x2f\x39\x45\x56\x05\x69\x96\xfd\xb6\x66\x69\x0e\x77\x0f\xbc\x55\x2a\x06\x94\x8f\x12\x52\x32\x7f\x87\xbd\x39\xfa\xd2\x79\x36\x7a\xe6\x5b\x92\xd0\xc8\x2f\xe5\x98\x35\xc4\x67\x21\x60\x57\x72\xa3\xe1\x2c\xc5\x90\x98\x57\x47\xac\xc1\x9e\x92\xe1\x88\xab\x12\xae\x7e\x6f\xa3\xe7\x00\x65\x65\xf3\x37\x13\xf3\xb7\xf8\xf7\xfe\x6c\x9b\xfc\xe5\x12\x0d\x97\x10\x8f\xc4\xd2\xa1\x45\xe3\xc2\x16\x81\xb3\x92\x85\x6c\x5e\x96\xb6\x07\x61\x72\x62\xda\x07\xa5\xac\x53\xac\x74\x89\xdf\x4d\xa9\xf1\x37\x9c\x33\xe7\x4e\xbd\x68\xf4\x10\xc4\x40\x62\x56\x0c\xa8\x23\xeb\x46\xca\xd8\x33\xb7\x4f\xde\x0e\x7e\x28\xfd\xe1\x89\x65\x94\x33\xf0\x06\x2b\x7c\xbb\x54\x91\xe9\x26\xde\x39\xd4\xfe\x44\xec\x37\xbb\xf0\x67\x98\x77\x32\x7c\xe1\x2d\xa7\xdc\xeb\x7d\x01\xb0\xbc\x29\xa6\xde\xfc\x61\xe5\x05\x70\x8c\xaf\xb9\x35\xe6\x8a\xbb\x97\x25\xf4\xe5\x66\xbd\x56\x72\x41\x33\x5f\x58\x9e\x34\x11\x68\xd6\x12\x6a\x20\xdc\x03\x95\x94\x9c\xb3\xc7\x8e\x90\x26\xf3\xea\xc9\x9e\x17\x9a\x59\x97\x33\x5d\xcf\x79\x48\xa9\xcf\x33\x29\x37\x3a\x6e\xc5\x4b\xd7\xb5\x25\x35\x12\x0e\x0f\x40\xb3\xe0\x3e\x10\x50\xc7\xfc\x43\x43\xc2\x47\xb8\xb0\xc7\x5e\x1f\x27\xc6\x72\xe4\xac\x76\xf9\x7a\xf1\x96\xc7\xcf\xd6\xae\x23\x5d\xcf\x2c\xea\x69\x31\x44\x4c\xe2\x5b\xc3\x30\xee\x9f\x9e\xf1\x38\x94\x00\x89\xa6\xab\x39\x5f\xed\x0e\x19\xc2\x8b\xc0\xc6\xd6\x93\x72\xad\x95\x69\x8b\x99\xbd\x1a\xa0\xf8\x86\x92\xae\xa2\x90\x15\xe4\x96\x04\x32\xce\xe7\xb4\xee\x3b\x42\x6e\x98\x35\x68\x95\xbf\xe3\x6d\xbb\xa6\x93\x90\x9e\xfb\xfe\x1a\xfc\x52\xda\x5f\xae\x7d\x1c\x43\x3d\x1e\x78\x88\xdb\x6f\x0a\xa3\xbd\xe7\xcf\x99\x73\x1e\x1b\xf2\xf3\xf9\xcb\xe2\x7d\x5f\x00\x99\xb8\x1b\x01\x8e\xce\x0f\x92\x44\x2b\xd3\x20\xbd\x00\xd2\x3f\x0c\xd1\xa8\x50\x7a\xc6\x64\x89\xc1\xe5\x16\x37\xa9\xf8\x53\x64\x90\x5c\x19\xb5\xf1\x9a\xaa\xfc\xc8\x85\x30\xe7\xb2\xdd\x3b\xe9\xd4\xca\x74\x99\xea\x1e\xb6\xb7\x33\xdb\xbd\x65\x7a\x28\x32\xc8\x06\xf0\x72\xb8\x19\xf4\xfa\x3b\x1b\x83\xf1\xf7\xa1\x6f\xf9\xee\x3e\xf6\x70\x38\x28\x10\xf0\x0e\x4d\x90\x2d\xe1\x03\x8c\xa7\xf5\x5a\x75\xd5\xce\x39\x4b\x26\x7d\xd0\xed\x69\xa2\x4e\xa3\x86\xe3\x18\xd8\xd3\xe5\x12\x9b\x8d\x75\x2e\x5b\xb8\xc9\x36\x19\x9f\x73\xf3\x81\x49\x56\x37\x6a\x56\x4f\x77\x28\x97\xb6\xe0\xe1\x51\xcc\x70\x88\x92\x3d\xd6\x8f\xb8\xc7\xec\x29\x9e\xbf\x1e\xaf\x79\x27\xbe\x5f\xf2\xaf\x23\x13\x93\x19\xdd\x3d\x7e\x6c\xfd\x24\x1f\xb4\x71\x36\xeb\x75\x21\x1a\x2c\x1a\x84\xc1\x05\x86\xd4\x45\x5f\x91\x8a\xcc\xbc\x3c\x6b\x1a\x5c\x92\x9b\x29\x8f\xbb\x2d\x72\x78\xc5\x63\x62\x58\xb9\xca\x96\x9e\x3e\x17\xab\x0d\xf0\x56\x29\x58\x38\x21\xc1\xe4\x8f\x33\xb6\x19\xe4\x8b\xdf\x69\x7e\x96\x6a\x91\xf7\x5e\x82\x3f\xe1\xa9\x4c\x96\x37\x89\x8a\x3b\xec\x40\x33\xeb\xc2\xde\xae\xc2\x84\x26\x95\x8a\x13\x18\xec\x00\x67\xd8\x48\x73\x53\x2d\x2e\x58\x42\x04\xd9\x01\x0b\x79\xfa\xf9\x2b\xad\xf5\xda\x46\x48\xd1\x66\x42\x50\x8d\xb3\x50\x48\x39\xf2\x50\x82\x6b\x73\xb1\x63\x15\xf0\xe1\x29\x3a\x34\x76\x20\x2c\x11\x56\x40\x3a\x57\x6d\x2e\x8b\xa6\xf5\x02\x6e\x74\x2a\xcb\xde\x0d\x95\xff\x33\x13\xcd\x90\x1c\x5c\x98\xf0\xa0\x5a\xa3\xb0\x5f\xad\x2e\x02\x1b\x97\x88\x0b\x74\xfc\xbb\xfe\xa0\xc3\x2e\x43\xb8\x59\x5e\x26\x23\x13\x1b\x84\x1e\x49\x5e\xc4\x2f\xd6\x0c\x30\x75\x27\xff\xa3\xa2\x8f\xe8\x85\xb1\x7a\xbf\xd9\x7c\xad\xe3\x08\xb5\xaa\x17\xad\xda\xe6\x43\x6a\x6f\xab\x5b\xf0\x97\x64\xee\x0c\x5c\x06\x33\x90\x52\x32\xf6\x1a\x31\x3c\x9c\x43\x1d\xe6\xb3\x61\xf2\xc4\xa7\xef\x74\x0a\xdd\xa2\x7e\x56\x74\xcc\x78\xb5\xc6\x9c\x48\x48\xfd\x5e\x0f\x32\x79\x1e\xbe\x79\xfb\xdc\xcd\x85\x98\x92\xe6\xbc\x09\xd1\x1b\xef\x57\x50\xd5\x5f\xb8\xb5\x1f\x0e\x95\xb0\xfd\xb5\xfd\x16\x31\xb7\x53\xf3\x84\x57\xf5\x9f\xcc\x35\x27\x4e\x46\xb5\x37\xcd\x18\x47\x90\x11\x59\xc2\x12\x36\x49\x14\xd1\x89\x21\x93\x5d\x57\xb6\x0d\xdf\x1f\xe5\x52\xbd\xf2\x64\x0f\x94\xeb\x0c\x84\x22\x25\x37\xe8\x35\x2e\x55\xef\x5a\xe0\xcf\xe9\x42\x48\x62\xf5\x7d\x08\x85\x71\x04\x9c\xe0\x10\xa8\x60\x2c\x69\x87\x77\xb6\x9d\x0a\x39\x8f\x24\x3c\xc9\x24\xc1\xe5\x47\x11\x17\xd2\x93\x83\xf6\x8e\xa3\x5d\xc1\x31\x70\xc1\x16\x5c\x41\x0b\xd8\x7f\x6a\xba\x59\x8a\x15\xfb\x38\xca\x82\xd6\xb0\x7f\xd7\xc8\x04\x21\xd1\xe8\xca\xe9\x26\x5f\x7d\x71\x58\xb6\x36\xdf\x0e\xca\x57\xd2\x23\x83\x8a\xe8\xfc\xe4\x8b\x26\xaa\xc3\xf0\x19\x4a\x59\x57\xfb\xaf\x94\x56\xa9\x0a\x65\x28\x79\xab\x5f\x1a\x66\xe2\x08\x0d\x0a\xfb\x94\x06\x02\xaf\xc7\x6f\x06\x87\x4c\x5f\x5e\xb1\x67\x81\x4f\x38\xdb\x21\xe9\x74\x03\xbc\xce\x7c\x05\x38\xc9\xfc\xb2\x38\xa2\x88\xf5\x21\x18\xca\xf6\x64\x1f\xaf\xc1\x25\x98\x0b\x69\xf3\x68\x4a\x9c\x8a\x8d\x98\x11\x7c\x31\x5d\xa3\x52\x3f\xf4\x3a\xf0\x53\x5d\x9d\x13\x1a\x01\x0a\x5f\x0b\x23\x08\x3a\x45\x99\xf7\xc3\x0d\xe4\x1b\x2d\x4a\x02\xdc\xce\x3f\x8d\x6f\x1a\xae\xc1\xe4\x18\x30\x1d\x63\xad\x61\xcb\x6c\xe9\xd6\xb5\xf1\x12\xf9\x56\x42\x6f\x96\x8a\xf0\x2a\x33\xe4\x4c\x37\x13\x99\xc5\x3d\x3c\x50\x34\xc5\xca\xc2\xf5\x9d\xc7\x0d\x18\xc6\x09\x0c\x4f\x3c\xca\xf5\x35\x85\xc7\x11\xcb\xfd\x96\x69\x22\x5e\x53\xe5\x02\x53\x21\x27\xcd\x8b\xe9\x53\xab\x20\xb6\xbe\xd3\x63\xfb\x75\x2a\xce\x2d\x7e\xbf\x30\x4e\x4e\x8d\xef\x72\x32\x9b\x31\x43\x32\xff\xe3\x25\x5c\x8e\xef\x46\x46\x9f\xa2\x9c\x60\xf4\x19\xf9\xe6\x93\x4d\x35\xb9\xda\x4b\xae\xf7\xb4\x0a\x61\xb5\xec\xc9\x92\x59\xfd\xb7\x9d\xee\xbf\xbe\x61\xbe\x39\xb1\x69\x3b\x6f\x2d\x6d\xa4\xea\x05\xf5\xb6\xc0\x49\xc1\x6f\xa1\xf1\x0e\x83\xe7\x04\x59\x42\x40\xbf\xc0\x2b\x48\x2d\xa1\x7e\xc5\xea\xa0\xc2\x10\xd5\x7a\xc5\x8a\xae\x70\xa0\xf1\xc7\xe9\x4a\xc6\xac\x7e\x96\x98\xb5\xb1\x0f\xc8\xff\x79\xe8\x72\x1f\xad\x01\xde\xc5\xf8\xe9\x17\x50\xe4\x0d\x5e\x54\xa3\x09\x83\x92\x73\x4a\xf9\xec\x4a\xc5\x7d\xc8\x0e\x07\x1f\xb6\x35\xbb\x1e\x38\x4e\x4b\x95\x45\x69\xcb\x6d\xd9\xef\x51\xc2\x23\x26\x15\x44\x66\x36\x32\x77\xdc\x0f\xdd\x9f\x26\xfa\xe5\xd8\x2d\x5f\xd6\x01\xbb\xc1\x99\xfa\x86\xb6\xb5\x2c\x73\x15\xa6\xb3\x11\x0f\x72\x26\x18\xf6\xd4\x25\xe2\xf1\x01\xce\x70\x35\x46\xdb\x71\xa9\x83\x1d\x18\xf2\xc4\xd9\xc0\xa5\x16\xb9\xcf\x5b\x6d\x35\x75\xbf\x3a\x5f\x97\xdb\x6d\x05\x5e\x8b\xe9\xe9\xe8\xc2\xbb\x45\x88\x86\x9c\xe2\x7a\xdd\x8e\x5f\xde\x9b\xb6\x8b\x3e\x0d\xcb\x4d\x66\x89\x19\x2f\x3a\xae\xfa\x6f\x98\x9a\x3e\xf5\x05\x42\xf6\xfb\x8c\x9e\x3e\xc8\xf4\x35\xbc\xf0\xb9\xf8\xb7\x2d\x2d\x6a\x44\xed\x75\x3e\xeb\xc9\x92\x4d\x2c\xb1\xac\x26\x33\xae\xd3\x69\xdf\x54\x7b\x95\xf0\xcb\xe2\xe9\x8d\x94\x46\xbd\xcb\xaa\x86\x59\x69\x58\x36\xe2\x8e\x88\xc6\xf3\x2c\xdb\xe0\x02\x2f\x31\xa9\x03\xa1\x6f\x03\x4e\x37\x57\x59\x3e\x74\x16\x8d\xbb\x7d\x97\xfd\xdf\x99\x18\x71\x3f\x50\x6a\x84\x13\x5f\x9a\x45\xf6\xc1\x20\xd8\x20\xe7\xd3\xaa\xf3\x0a\xb9\x15\x21\x93\xfa\xc2\x09\x1d\xe9\x4b\x31\xf8\xdc\x78\xd6\x7a\x47\xd3\x0e\x93\x9b\x8d\x40\xd0\xd7\xe7\x3a\x93\xa6\xde\xbd\xa2\x46\x22\xd3\x12\x36\xa4\x31\x14\x05\xa1\x77\x2d\x1b\x1f\x83\xe0\x13\x34\xfc\xc1\xb3\xe6\xd7\x4a\x51\xf3\x49\x07\xee\x43\xb3\xf7\xcf\x82\x24\x14\x77\x72\xbf\x9d\x41\x65\x8d\x3a\x9b\x3f\x06\x76\x2e\x13\x30\xe4\x5e\x06\xff\x92\xea\x75\x84\xb4\xf4\xde\xd3\xad\xf6\x74\x46\x5e\x55\xf7\xdd\x5e\xae\x68\x9a\x5c\x2d\x18\x06\xdf\xe0\x62\x59\x13\xe7\x9d\xf6\x5a\x18\xff\x02\x8f\xd1\xa4\x3d\x41\x96\xc0\x67\xf5\x19\x0e\x46\xe5\x13\x60\xb5\x79\xbc\x06\xbd\xa2\x8f\xce\x55\x4f\xd3\x56\x56\xe0\xcc\xf9\x41\xcc\xf1\xe0\xcf\x7d\xc3\x95\x45\x53\xfd\x0a\x5b\x3f\x53\x32\xca\x76\x03\xb4\x6e\x7f\x86\x88\x2c\x66\x38\xec\x53\xc3\x7b\xe5\xf3\x34\xb3\x0a\x85\x4a\x13\x8b\xc6\x88\x2f\x2f\xfe\x59\x9c\x19\x79\x7f\xe4\xc2\x46\x00\xb8\x26\x9d\xf1\xa2\xcc\x31\x09\xf3\xd9\xdf\x63\xf7\x17\xfc\x1f\xf6\x41\x97\x8d\xfa\x59\xa4\x19\x0f\xc6\xdf\x06\x76\x0d\x2e\xef\xa4\xc8\xfd\xda\xb3\x2f\x4d\xda\x5c\xde\x22\x49\x8c\x16\xbf\xed\xf6\xec\x3c\xb5\xe2\x18\x6f\xd0\x12\xa7\xdd\x6f\xa8\xec\x36\x17\x82\xb3\x66\x0c\x7d\x03\x36\xd3\x3e\x0e\x7e\xee\xb3\xe9\x9e\xa7\x48\x53\x29\xb6\xa5\xb0\xc1\x90\x70\x8a\x69\x75\x92\x5a\x8e\xeb\x8d\x0c\x1c\x49\x1b\x94\xab\xda\xc2\xc1\x59\x39\x5e\x22\xcd\xfd\x61\x9e\xc3\x12\x8c\x98\xb5\x04\x04\xce\xd3\x23\x3b\x9e\x4c\xab\xca\x4b\x34\x2a\x26\x1f\x0e\xb8\xad\x76\xaf\x74\x07\x65\x9e\x06\x76\x5f\x10\x5c\x0a\xbc\xa3\x32\x51\x30\x12\xec\x6c\x0a\x9b\x07\xee\x33\x3d\x99\x9d\x24\x89\xfc\xda\x79\xcf\x05\x9d\x95\xa5\x96\xdb\x9f\x0e\xa9\xf0\xba\x3b\x8b\x5e\x14\x83\xb9\x37\x12\x76\x7b\x1b\x7c\x57\x42\xc8\xee\x8c\x43\x35\x1b\x92\xd5\xce\xa9\xae\x92\x51\xa9\x70\x56\xef\x6c\x88\x7e\x96\x9f\xad\x12\x55\x95\x4b\xa8\x95\x35\x32\xda\x4d\x8d\x38\x4e\x50\x29\x29\x7f\x16\xcb\x33\xc7\xeb\x86\x5e\xf4\x24\xc9\x34\x22\x68\x50\xca\xde\x7d\xd9\xf0\x9e\xad\xa7\x15\xfc\x5b\x08\xd1\x5d\xc9\x98\xad\x16\xc9\xec\x46\x2c\x02\xd5\x5f\xd6\xda\x92\x1e\x0a\x77\xff\x69\xbf\xdc\x79\x22\xfd\x54\xc5\x6a\x08\x39\x7d\x0e\x30\xa0\xed\xf8\x43\x10\xb4\xa8\x7d\x7f\x2b\x99\xc1\xda\x1e\x6f\x2c\xa7\x6d\x7f\x91\xf6\x46\x22\x0f\x2e\x88\x30\x12\xde\x49\xd7\x02\x7d\x07\x7b\x6d\x3f\xb0\x46\x57\x12\x1e\x8b\x78\xf7\xb5\xc2\xf1\xf5\x55\xf2\x4a\x1b\xf7\x0c\x70\x16\x06\x6a\xbf\xfb\x94\x7f\xcd\xf8\xe7\x95\x6d\xbd\xf3\x2a\x0f\xa1\xba\x5c\x35\xed\x31\x79\xc7\xba\xbc\x6a\xd9\xe7\x3a\xc5\x5a\xc1\x0b\xfa\xf0\x87\x79\xa2\xe4\xfb\xd5\xe0\x77\xe2\xdf\xc1\x16\xc7\x09\x5a\x14\xb9\x36\xe8\x84\x74\x97\x15\x7f\x9f\xda\xb7\x5b\x3a\x14\x15\x03\xef\xfd\x84\x9c\xae\x92\x34\x9a\xa2\xbd\xef\xfb\x5f\xbd\x7b\xbf\x78\x5f\x4e\xd8\x36\xd0\x05\x41\x7e\xbe\x3b\x7a\x0f\x14\x74\x38\xf6\x3a\x19\xff\x92\xb8\xd6\xb4\xaf\x8d\xc4\xf8\xb9\x04\xbd\xac\xe9\x14\x38\x82\xe6\x69\x4e\xfb\x14\x1a\x37\xe0\x5b\xf8\x2b\xa2\xa5\xa8\x7e\xf2\x7d\x50\x45\xec\xfb\x57\x85\x12\xa4\x4e\x34\xd7\x21\x9c\x6e\x9d\xe9\x72\x63\xf8\xa2\xdb\x92\xf7\x48\x33\x52\x87\x7c\x0f\x8c\x2f\xcd\x27\x41\x7b\x43\xb5\xef\xa7\x81\x7e\xbf\xd0\x20\xcb\x6a\xa6\x4f\x90\xbf\x3f\x9f\x78\xcf\x4f\x88\xca\xf0\xde\xc7\xd6\x97\xf9\x48\x74\xb8\x0e\x4e\xee\x33\xc9\xf4\xd0\x76\x4a\x72\x0e\xb1\xfb\x24\x23\x3f\x9c\x8c\x9f\x06\xbd\xff\x64\xfd\x25\xe1\x0a\x7a\x08\x01\xad\x5c\x1b\x90\x81\x1b\x2f\x07\xd0\x8c\xfc\xaf\xca\x6c\x93\x02\x9d\x44\xde\x52\x40\xfd\xaf\xab\x24\x1f\x9e\xdb\xaf\x79\x97\x3f\xb3\xea\x3f\xab\x09\xbb\x0b\x1b\x92\xbd\x86\x79\xef\x0a\x5d\xbc\xa6\x96\xc3\x20\xa4\x2c\xc4\x20\x64\x51\x80\xaa\x4d\x26\xe5\x40\xeb\xa9\xb1\x25\xa7\xc3\x7b\x86\xc9\x26\xdf\xbd\x71\xee\x01\xe6\x5f\xc0\x3f\xd3\x2d\xce\xfb\x53\xa8\xf0\x3c\x39\xf6\x24\x16\xfb\x97\x6d\x93\xe5\x7b\x69\x9b\xc8\x6e\xfb\xee\xab\x56\xe5\x5c\x9a\x87\xdc\x9f\x64\x1a\xe7\x46\x29\x03\x8a\xde\x51\x6c\xf9\x02\x86\x6f\x73\x6d\x3b\xdf\xca\xff\x9a\xc5\x0e\x52\xa5\x32\x6e\x45\x7e\x10\x8b\xe5\xb1\xd6\x7f\x85\x9c\xfd\xbc\xee\xb9\x65\x09\x78\x68\x0d\x98\x35\x5c\x75\xfe\x78\x08\x98\xad\xeb\x45\xc7\x3b\xd4\x2c\x65\x9d\xa1\xd6\xf3\x67\xae\x99\xa4\x11\xae\xc0\x7f\x1f\xa8\x1e\xab\x2f\xf2\x65\xd0\xed\x81\x6b\xca\xb9\x6d\x2c\x60\x4f\x66\x15\x5d\x9e\x5d\x62\x2e\xbd\x81\x19\x15\x0e\x4f\x1a\xfe\x86\x68\x56\x39\x34\x2b\x54\xca\xd7\xe7\x2f\xf2\x38\x26\xba\xcf\x54\xf8\xab\xf0\xa3\x60\x23\x8c\x0d\xbc\x03\xc1\xbc\xef\xde\x2d\x41\xc3\x32\x04\x2a\xa2\xdf\x45\x23\x7f\x08\x14\xf2\xf6\x7c\x78\x5f\x0e\xf5\x95\x1a\xd1\x62\x74\x24\xe3\x4a\xfa\xba\x7c\x54\x78\x7f\x38\x9f\xfa\xde\x73\xd4\x3f\x0b\xe1\x55\x32\x36\x90\x2d\xec\x29\x4b\x11\x7d\xe3\xf7\x96\x36\x72\x19\x3b\x55\x57\xd2\xc3\xd7\x17\x53\x7a\x73\x4e\x45\xd5\xa2\x6f\x21\x3f\x15\x66\x56\xa0\x20\x22\xbf\x49\xf7\x2d\x63\x18\xb7\xb2\xff\x8c\xcf\xc8\x21\xc4\x9e\xd9\xed\xa2\x6f\x04\x5f\xf5\x47\x7f\xf7\x97\xcb\x7b\x0a\xe8\xf7\xe5\x13\xd3\x56\x00\xf0\x92\xe7\x92\xa3\xef\xb7\x5a\x74\x80\x4f\xd9\xa7\x14\x6c\x08\x6a\xcb\x57\x5b\xe0\xeb\x65\x9d\x46\x15\x98\x22\x7a\x75\xdb\x55\x70\xf4\x16\x31\x40\xfa\x51\x47\x9d\x21\xb7\xb4\x1c\xfa\x2f\x87\x2c\x5a\x36\x33\x4a\x5f\xad\x2b\xb2\xc9\x3e\x9a\x07\x18\x21\x72\xb3\x28\xbc\x47\x37\xd6\x8a\x72\xe5\x6d\x6e\xbb\xf7\x00\x64\xc6\xeb\x49\x49\x13\x5c\x6e\xf4\x53\x8b\xb1\x87\xa3\x79\x48\xfc\x2d\x1f\xb6\x3a\xf2\x83\x44\xac\xe7\x2f\x10\x2d\xb9\x6c\xa4\x91\xa9\xd0\xff\xed\x5b\x28\x73\xdf\xe5\xee\xf7\x6f\xc0\xd1\xd3\x99\x7b\x3b\x33\x84\xaf\xe5\xb4\x4c\xe7\x62\x8d\xee\x4f\x1f\x63\xdd\xf9\x63\x46\x50\xfc\x53\x0a\xa9\x1e\x24\x0b\xdd\x1f\x4f\x02\x33\x59\x81\xaa\xf5\x8c\xbf\xcf\x5c\xb3\x96\xe0\x49\x6a\x6d\xb2\x58\x6b\x6b\x5d\x18\x22\x5e\xb0\x22\xf0\xa1\xbb\x66\xc5\x3f\x7c\xbf\x2e\x69\x71\xae\xf3\xc1\x2e\x92\x19\xd1\x37\x45\x58\x17\x27\x17\xb6\x7a\xa5\x0b\x37\xbd\x93\x17\xe0\xab\x93\xeb\x71\x39\xbd\xf8\x51\x7d\xd0\xa2\x82\xc6\x13\xc6\x4a\x12\x7f\x76\x77\xbd\xbc\x7d\x9b\x3c\x93\xfa\x3a\x41\x16\x57\xeb\x76\x64\x90\x96\xf4\xe6\x13\x08\xc7\xac\x10\xfc\x53\xcf\x7d\x40\xf5\x75\xcc\xb5\x5e\x50\x82\x28\x75\xb2\x81\x5f\x8b\x1c\xac\xaf\xc5\xd0\x42\x80\x6c\xe4\xaf\x39\x31\x06\x58\x63\x08\x32\x6a\x23\x7c\x52\x69\x5c\x0d\x6e\xc2\x6e\x5f\xac\x36\x63\xc1\x76\xb8\x0d\xa5\x86\x3b\x7c\x7e\xe2\xd7\x04\x94\xb6\x84\x3e\xac\x2d\x5a\x75\xf2\xa3\xe4\x7e\xcc\x3c\x7d\xef\xfc\xd0\xa2\x22\x3d\x7d\x99\x8f\x7a\x98\x01\xcd\x83\x03\xdc\x45\xfb\xf8\x21\xc1\x61\x03\xf6\xdd\x1f\x4f\xf5\xeb\x8e\x29\x14\xe6\xb8\x62\x19\x16\xb7\xfb\xf0\xb8\xae\x09\x3e\x09\x9d\xb2\x14\x45\xbd\x1e\x4f\x35\x59\xaf\xb4\xbb\xeb\xf1\x24\x41\x1a\x91\xf4\xd4\x0c\x5a\xb7\x6e\x9f\x07\xfd\xcd\x14\x38\x8d\x99\x71\x7a\x46\x43\x25\xe6\x8f\x27\x8b\xab\x37\x3f\x83\x62\x9b\xbd\x11\xfd\xea\x96\x2e\xe0\x6e\x3a\x8a\x4c\xbe\xf6\x7c\xcb\xba\x9b\x7f\x20\xd9\x0a\xd8\xa1\x8b\x3b\xca\xcd\xbc\x0d\x11\xcb\xf2\x89\x42\x78\x49\xe9\xa3\x7b\xdb\xe0\x51\x2a\x23\x5b\x57\x9c\xae\x95\x67\x17\xa8\x0d\x7a\xf8\x9d\xb7\x3c\x99\xdf\x95\xd1\x4a\x0c\x61\x30\xbd\xa0\x2a\x6b\x37\xb7\xd2\x07\xdc\x73\x7d\x91\xce\xfc\x95\x4b\xda\x9b\xeb\x8f\x83\xe7\xdc\xaf\xe5\x07\x97\xd6\x44\x9f\x6f\x3e\xb0\x67\xd1\xd6\x83\x60\x14\x31\x41\x47\x41\x73\x48\x6e\x90\xd8\xcc\xff\x45\x2b\xf2\xa3\x59\x6e\x95\xc8\x57\xee\xa1\x09\x83\x6e\xbb\xc0\xd5\xf0\x1a\x68\xec\xec\xe7\x43\xc8\x06\x3d\x9e\x25\xe7\x71\xcf\xff\x86\xe8\x25\xd9\xda\x06\xf5\xa8\xe1\x83\x0d\xc8\xca\xdf\x42\x4e\x2d\x7e\xf5\xe4\x05\x20\x93\x55\xd3\x9a\x66\x85\xde\x54\x98\x3f\x38\x3d\xd3\x50\xe1\xd8\x1e\x79\xdc\xe6\x73\x52\x4a\x5d\x93\x60\xc6\xed\x34\xf9\x13\x31\x26\xbb\x77\x85\xc9\xda\xbe\x84\x4f\xfa\x31\xbe\x24\x0b\x46\x02\xa6\x4b\xd3\xa7\x0c\x9b\x0b\x0c\xf9\xab\x56\x83\xc9\x34\x69\x90\x5a\xbb\x77\xba\x45\x1f\x88\x38\x0b\x3b\x9e\xde\xb6\x2c\xa2\x62\x5f\xb2\xc9\xdc\x96\x86\xc8\x37\x54\x82\x26\xee\xc7\x60\x60\x69\x7f\x4a\xff\xa4\xf7\xa7\xda\x05\x5b\x6a\x05\xf9\xc3\xf6\x07\x48\xbc\x71\x2f\x8f\xc2\xd0\xa9\x4b\x50\x91\x5b\x74\xbc\x59\x9a\xb3\xbf\x81\x0b\x47\xbf\x21\xaa\x00\xec\xf8\x03\xc8\xd8\x3f\x34\x1e\xca\xcb\x43\x26\x73\x45\x87\xeb\xd9\x5b\x2e\x65\x1c\xd9\x53\x38\x7d\x87\x9b\xb6\xf4\x90\xa9\x8c\x96\x1d\x39\x7f\xdc\xd2\x5c\x8e\x34\x72\xff\x2d\x7f\x3d\xb8\xd6\xcb\x32\x66\x78\x7b\xe4\x78\x1b\x62\x7f\x7b\x1f\x9e\x42\xfc\xcc\x60\xfa\xf0\xc3\x72\x38\x0a\xf1\x97\x55\x1d\x9b\xc8\x10\xda\x5e\xdb\xf1\x9f\x46\x63\x54\xb0\x3b\x94\x12\xdb\xab\x60\x51\x11\x18\x99\x8b\x6a\x84\xbb\xf4\x90\x01\xa5\xb0\x15\x25\xea\x1a\xc6\x77\x33\x8c\x23\x7e\x9e\x71\xea\x4a\x8f\x97\x0a\x2f\xfc\x6b\x6a\xa7\xc8\x10\x7f\x62\x7b\xf3\x8c\x95\x45\x93\x43\x2d\x4a\x1f\x0b\x5e\xa3\x3e\x9b\x5f\x86\x55\xc1\x66\x03\x70\x03\x9c\x7a\xfd\x88\x28\xbf\xef\x46\x91\x80\x81\x2e\x82\x6f\x39\x2f\xfe\x56\x95\x80\xf6\x95\x9b\x81\xf8\xeb\xb7\x97\x2c\x44\x78\xaa\x98\x6b\xa0\x02\x4e\xed\x92\x2a\x43\xb9\x28\xab\xf5\xda\x91\xc5\x09\xcc\x87\xb2\x36\x65\x5f\x02\xb2\x28\xc3\xde\x0a\x9e\x34\xca\x83\xc4\x14\x1d\x74\x43\xba\xc9\x52\x5a\x1e\xfc\xb0\x8b\xc3\x12\xc2\x09\x4a\xb9\xee\x1b\xbb\xcd\xb2\xef\x31\x41\x64\xba\xcd\xb3\xa1\xbf\xe4\xdc\xee\x5b\xcf\x65\xed\xac\x52\xee\xe9\xd3\xec\x7b\xa5\xf7\xe1\x74\xc3\x7c\x79\xca\xdc\x74\x12\x7e\x28\x8d\xd1\xe3\x6b\x7a\x74\xe3\x0e\xed\x81\x16\x96\xdb\x23\x0c\x84\x33\xf7\x28\xf4\x5d\xda\x6a\xd7\x8b\x68\xeb\x28\x03\xa2\x83\xe1\x31\xa6\xe7\xf1\xf7\x5c\x82\x56\xe7\x79\xf8\x18\x0b\x39\xc8\x08\x7b\x09\x03\xb3\xa0\xb9\x01\xb0\xe1\x6b\x23\xb0\xf8\x20\xb3\x9f\x8c\x53\x89\x66\x3c\x78\x84\xf5\x72\x15\xae\x41\x28\x39\x4a\x15\xca\x9c\xb2\xa0\x34\xfb\x5c\x0b\xba\x12\x73\x65\x21\xab\x12\x32\xe4\xeb\x73\xa8\x08\xdd\x97\xeb\xcf\x0c\x42\x8a\xe5\x78\xd4\x6c\x99\x6c\xac\x27\x2c\xd9\x37\x82\x58\x8c\xbc\xb6\x78\x02\xe9\x26\x88\x3e\xb9\x48\x70\x6b\x9f\xeb\x7d\x34\xd1\x58\x68\xec\xdc\x7f\x3b\x55\xd4\xf3\xf1\xd7\x84\x27\x66\x30\xeb\x41\xa8\x93\x65\xcd\x74\xd8\xe0\xa9\xbd\x86\xe9\xc8\x3b\xb1\x5b\xef\xe9\x3f\xe3\x69\x2f\xb0\xd5\xfe\x81\x96\x85\x10\xe3\x10\x07\x80\x8b\xf8\x40\x54\x33\x60\x83\x48\xbc\x06\x94\x26\x12\xcd\x67\x01\x57\xaf\xaa\xe9\x63\xc5\x42\xf3\xc4\x1e\xc7\xd6\xd4\x4d\x5e\x65\x14\x55\x82\xa5\x4c\x28\xb7\x54\x8a\x1e\x7e\x79\xef\x1f\x65\x2d\x04\x70\x46\x03\xb7\xe2\xa8\xd7\x73\x2e\x6e\x02\x63\x98\x92\xcf\x2c\x1e\xbd\x46\xaf\xa3\x0f\xf0\x21\x21\xf6\x30\x13\x74\xbb\x9f\x9a\x70\xe0\xe3\x82\x9a\x4f\xed\xcd\x04\xaf\x5a\x27\xc8\xcd\xc8\xfc\x16\x5f\x58\xaf\x6a\xf8\xf9\x12\xdb\x09\xfe\xdc\x1d\x5c\x1b\xea\x6e\x17\x8b\x77\xfb\xcc\x5e\xff\x45\xce\x15\x3d\xcd\x5b\xd1\x34\xa1\xde\x26\x96\x94\x41\x0e\x62\x8a\x61\x97\xb9\xde\xad\xb9\xa6\x6f\xf0\xdf\xf0\x3f\xd0\x74\xf8\x7c\xcb\x0a\x99\x5f\xd1\x4a\x36\x7a\xb2\x16\x12\x53\x21\xfb\xd1\x48\xac\xc9\x3c\xec\x00\x95\xe6\x1a\x9f\x22\x21\xf6\xd6\x95\xa4\xee\x03\x7c\x8b\x5b\xa6\x82\xfc\x42\x70\x24\x9a\x37\x67\x08\x7e\x60\x37\x48\x51\x3e\x95\x9f\xae\xd3\x76\x2e\x5b\x43\xd6\x14\xee\x1e\x8b\x8e\x23\xc0\x67\x32\x31\x46\x91\x7b\x03\xb8\xf8\xaf\xfb\xcf\x14\xb5\x7f\xb8\x3f\xf5\x7e\x34\x03\xf7\x5f\x57\x86\x9e\xc8\x0c\x73\x0a\xea\xf1\x3e\x92\x5d\xa5\x7f\x64\x7b\xf4\xce\x1c\x66\xb5\xdc\x6f\x80\x4c\x29\x53\x34\x68\xda\x4f\x2f\xfb\xde\x65\x4e\x7d\x45\xbd\xf8\x62\xf4\xfa\x5a\x35\x35\x54\xd9\xd7\x2b\xd2\x6b\x66\x6f\xba\x85\x63\x33\xdc\xd6\x93\x94\xb5\xd1\x11\x6f\x30\x69\xab\x86\x70\xf6\xf8\xe8\x18\xf2\x3b\xf1\x28\xda\xc9\x6c\xc6\xf5\x46\xfc\xed\x6c\x07\x77\x70\x15\xb2\x86\x97\x90\x15\xf5\x0f\x1d\x63\x96\x5b\xbd\x5f\x4e\x1b\x1a\x45\xaf\x4f\xee\xe7\x02\x2f\x51\x3e\x9c\xb6\x9f\x4a\x40\x99\x41\x34\x1d\xc6\xdd\xe0\x11\x90\xf5\xae\xf1\x02\x4e\x5b\x52\xdc\x88\x10\x44\x48\x4d\xad\xe8\xe3\xcf\xe5\x41\x9a\x41\x5f\xdf\x69\x19\xd2\xe6\xc2\x0d\xe8\x14\xc4\x26\x3c\x9a\x26\x66\x98\x7b\x0d\xa3\xd5\xd5\x6b\xe1\x41\x97\x1d\x20\x97\x0a\x0f\x22\xd3\x8c\x24\xd3\x88\xd3\x12\x7b\x5e\xa2\x13\x9a\x27\xfb\x2d\x49\x92\xcc\x07\x93\x74\x1b\x43\x17\x8f\x1c\xe9\x0e\xd7\xf2\x1a\x42\xa3\xae\xe4\x78\x1d\xb5\xd5\xfa\xe2\xd8\x93\x9e\x37\x3b\xce\x96\xef\x1e\xb0\xd8\xa7\x47\xd6\xa3\xf9\x76\xfd\xa6\x76\x29\xf5\xbe\x48\x1f\xbf\xf9\xb8\xe5\x6a\xa0\x76\x4c\xa2\x7e\x27\x21\xfb\x39\x71\x4c\xb9\xf3\xf9\x4f\xb6\xd8\x74\xf7\x0b\x99\x9e\x54\xea\x1d\x2e\x1c\x79\x25\xd3\x4a\xd1\xf3\x80\xa7\x09\x00\xb0\xf5\x24\xa4\xfd\x69\x04\x87\x70\x97\x78\x85\x9a\x47\x24\x78\xad\x0c\xdd\xaf\x41\x9b\x31\xcd\x44\xa3\x07\xc9\xc3\xe5\xe4\x9d\xd8\xec\x81\x02\xcb\x14\xbe\x7d\x53\xa5\x8a\xb2\xb4\x74\x71\x06\x7b\xf2\xd1\x3d\xd9\x8e\xa7\x5f\x26\x35\x6a\x5b\xec\x43\xad\xe1\xf4\x9d\x60\xea\xf5\x31\x64\xd7\xe8\xfa\x8d\x6d\x78\xb6\x3b\xd1\x14\x22\x77\xfa\x01\x3c\xa0\x09\xaa\xda\xc1\x90\x71\x38\x6f\x2e\x5f\xa0\x5a\x7c\xf9\xc3\x60\xda\x6c\xf2\xf0\xb4\x92\x01\xb1\x40\x17\x0e\x6d\x0c\x1c\xf0\x1f\x4f\x6a\xa4\x2b\x0a\x50\x22\xcc\xdc\xdc\xb5\x83\xe4\x0d\x6a\xeb\x13\x63\x2b\x2f\x54\x31\x53\x2a\x6f\x3b\x4d\x71\xd7\x61\xf0\x1c\x22\x5f\xb8\xce\x3f\x54\xc5\xb0\x26\xf4\x4a\xac\xd4\xcb\xbc\x9e\x9c\xa6\x83\x95\x77\x07\x37\xb3\xc5\xbe\x4b\x73\xa8\x04\xeb\xf4\xe3\x5b\x1a\xf9\x15\x32\x84\x79\x3c\x00\x92\x98\x1a\x8d\x4d\xef\x61\x3c\x76\x79\x80\xc7\x2f\x93\x1f\x73\xcb\x6a\x0f\xad\xde\xa7\xce\x97\x9d\x40\x9b\xc0\x17\xd3\x6d\x28\x3c\xbe\x52\xbc\xc0\x59\xff\xa1\xa4\xf1\x44\xb3\x83\x0b\x70\x81\xac\xfc\x25\xfe\x90\x8b\xdb\x22\xdf\x77\xdb\x5f\x01\xdb\x2f\x9b\x97\xef\x68\x67\x78\xd2\xba\x5b\x4d\x85\x0f\xa3\x2b\x69\x85\xed\x52\x24\x95\x01\xf6\x01\x78\xab\x16\xaa\x26\x1e\x97\x61\x32\x46\x94\x4c\x3f\xfb\xcc\x75\xf5\xf3\x9b\xe2\xb5\x22\xf9\xb2\x8c\xc9\xea\xdd\xac\x7d\x33\x0a\x4f\x97\xd0\x21\xf3\xbd\x1f\xd7\x30\x5a\x83\x37\x07\xfc\xcd\x44\xc6\xaa\x66\xb2\xb1\xa3\xca\x9e\x8d\x4b\x92\xb9\x7e\x66\x13\x8f\xec\xbe\xeb\x87\x5b\x01\x3b\x77\x1b\xde\x4f\xaf\x05\xe2\xc4\x24\x10\xbe\x20\x1b\xc8\x6d\xa3\xe1\xaa\x48\xdd\x3a\xf8\xe5\xc7\x6e\x97\x6f\xfd\x7b\x18\xdf\x6b\x57\x4a\xf6\x1f\x05\x59\x87\x5b\x48\x57\x42\x92\x01\xdf\x8d\xcb\xe9\x2f\x3e\xe1\x2a\x60\x67\xaf\x6a\x1f\x22\x5d\x92\xb0\xc7\x3c\x7a\x5c\xe5\xd0\x5f\xa4\x33\x50\x9a\xfb\x81\x3f\x40\xb9\x13\xcf\xf0\xa8\x9a\x2c\x64\xd1\x92\xb5\xbd\xc3\x7e\x22\xa9\xfc\xe9\x5b\x57\xf8\x40\x75\x48\x8c\x0d\x39\x35\xd1\x65\x4f\x01\x07\xcd\x1e\x86\x70\x3e\xe1\x03\xf7\x32\x46\x6a\x67\x2a\x9b\xe9\x2c\x64\x31\xd1\x6f\x6b\xdb\x7d\x12\x8c\xd2\x7b\x05\xaa\xc1\x68\x79\x79\xb9\xd0\xcb\x92\xa1\x4f\x34\x36\xb9\x20\xc2\xa8\xed\x78\x7b\xa6\x9d\xb6\xbc\xaa\x12\x3b\x44\x03\xb8\xf8\x15\x6c\x95\x89\x41\x94\x71\xb6\x29\xe6\x93\xc1\xc1\x4a\x04\x1c\x4a\xd9\x58\x25\x68\x1a\x7f\x01\x53\xb4\xbf\x38\xe6\x1b\x51\xe8\x33\x7d\x2b\x91\x24\x5a\x79\x8f\x79\x65\x04\xe8\xf4\xd2\x30\x51\xe1\x1c\xf0\x02\xcf\x99\xf0\x1f\xdb\x0b\x9f\x7f\x5c\x1e\xef\x36\x2e\x1b\x97\xf5\x36\x7e\x34\x83\xe8\x83\x2c\x1d\xf1\x8f\xe2\xcf\xb0\x8f\x17\xc0\x7b\x9b\x2b\x9b\x41\x2e\xa7\x25\xe7\xcb\x6a\xc6\x7c\x41\x6a\x03\xd8\x7c\x60\x11\xb3\x80\x8d\xbf\x01\x55\xab\xaa\x78\xa4\xbb\x77\xcd\x42\xab\x79\x1f\x0d\x26\x51\xaa\x2f\x15\xcd\x8e\x94\x03\x22\x89\xf1\xf3\x41\xf4\xe9\x8d\x00\xdf\xd0\x23\xbf\xad\x71\x8c\xee\x46\x40\x2b\x32\xbd\xd1\xc0\x71\x3b\xa5\x60\x31\x7a\x5c\xd9\x08\x65\xbc\xbe\x95\x32\x45\x8b\x27\x83\x05\x5e\xc4\x85\xa9\x5f\x01\xa0\xfb\x71\xe5\xa3\x4b\xf4\xa6\xa3\xb6\x18\xc1\x6c\x72\x58\xc9\x75\x97\xef\xe3\xd2\x7b\x17\xfe\xab\x6a\xae\xe8\x35\xd1\xea\xe2\xf3\x22\xce\xa4\xcf\x11\xa5\x00\xfc\xcf\x95\x7a\xb1\xbe\x9d\x67\x7e\xbc\xc5\x1f\xcf\x49\xdb\xed\xc0\x3f\x19\x97\x3b\xbb\x42\x5a\x48\xf8\xea\x5a\x45\x0f\x0a\x02\x23\xa2\x1c\x06\x09\x40\xb4\x0e\xd0\x40\x16\x38\x3c\x24\x61\x7c\x67\xcc\x02\x12\xf8\x59\xb0\xb2\x6c\x69\x40\x25\xf5\x30\x89\x66\x20\x96\x49\xdb\x6d\x1f\xbb\x26\x2e\x15\x6a\xec\x34\x60\xdb\x7f\xa7\x28\xf5\xc1\xb8\xfb\x09\x6d\x95\x08\x28\xc9\xeb\xe7\xed\xc5\xbf\xaf\xcb\xb5\x2f\xf5\xc6\xf7\xe9\xe3\xf7\x62\xda\xdf\xba\x2e\x0c\x63\xe3\x57\xad\xf5\xdc\xa3\x4f\xf4\x30\xdf\x6e\xe2\x76\x41\x88\xbd\x81\x71\x87\xd7\x90\x27\x50\xdc\x6e\xc7\xdf\xde\x0a\x4c\x05\x8c\x48\xd3\x18\x57\x1f\x8b\x74\xdd\x16\xd6\x86\x07\xa1\xcf\x2f\x3f\x27\x08\xbd\x02\xf5\xac\x53\x74\x22\x26\x9e\xa0\x10\xd9\xc9\xdc\x25\x88\x25\x0f\xae\xa6\xf9\x5b\xc2\x37\xf0\xa4\x74\xce\x2f\xe7\x7d\x87\x6f\x84\x8a\x1a\xc5\xae\x4f\x86\x2e\xd8\x20\x87\xa0\xb9\x30\x63\x89\x6f\xce\x8b\x73\xaa\xcf\x34\x7b\x81\x7d\x1d\x2e\x2f\xb0\x23\xaf\x3e\xd3\x9a\xab\xba\xc0\xa4\xf8\x1a\x8c\x9b\x37\x1b\x9b\xa4\x56\x64\xf6\xce\x8f\x47\xb0\xeb\xee\x8f\xf7\xd5\x5d\xd3\xbe\x8f\xba\xd3\x5f\xa3\x81\xcf\xbd\xde\x97\x41\x26\x8d\x1f\x44\x97\x09\x62\xbf\x44\x1f\x31\xdc\x17\x96\x7e\xb8\x69\x93\x05\x85\xff\x35\xc0\x29\xce\xca\xef\x8d\x8b\x63\x93\xe5\x3a\xeb\xfd\x92\x44\x8d\xdf\xbe\xaf\x8c\x60\xe2\x1f\x74\x56\x01\x5e\x98\x6e\x7e\x3b\xa2\xca\x7e\x92\xfc\x79\x2c\x3e\xc1\xba\x1c\x5b\xe9\xb7\x3d\x0a\x1d\x36\xd2\x96\xf6\x91\x3c\xc1\x4b\x71\xe7\xa2\x2c\x0c\x9b\xbe\xca\x6b\x74\xc5\x1c\x89\x2e\x8c\x20\x93\x14\xf7\x62\x5d\xfe\x02\xb9\x35\x1f\x9f\xa7\xb9\x16\x1e\x4c\xb5\x4d\xa5\x0a\x06\x99\xfe\xb2\x08\x9c\x51\xb9\xac\xad\x6a\x98\x6c\x34\x79\xda\xed\x25\x7d\x9e\xf3\xdf\x0b\x03\x13\xf9\x23\x5e\xf2\x51\x59\xf0\xab\x05\x1d\xfd\xb1\xe2\x3a\x2e\x11\x07\x2e\x2c\x09\x6f\x84\xe0\x77\x82\xe9\x24\xd0\x58\x0f\xaa\xbe\xd6\x54\x26\xd3\x75\x87\x27\x7d\x65\xa6\xbc\x62\xf2\x29\x71\x5a\x25\x32\xf4\x1b\x0b\x4f\x61\x37\x07\x0c\xe9\x51\xb7\x38\x68\xb3\x1b\xa5\x66\xdc\x0f\x04\xab\x6b\xe5\x8d\x70\x87\x77\x1d\x56\x6d\x5a\xe4\x76\xf2\x9e\xaa\xb3\x8a\xa2\x08\xd3\xa9\xa9\x34\xd3\x80\x7f\x7b\x1f\x79\xb0\xe1\x84\xc6\xe0\xd6\xbd\x90\x01\xdd\x6e\xf9\x08\x84\x5b\xda\x3f\x69\x57\x0a\x4f\xa9\x91\x3f\x30\x9d\xe9\x88\x2d\xfe\x6e\xf7\x62\x9b\xd0\xf0\x6e\x12\x62\xaa\xd8\x0d\x43\x88\xf4\xab\x13\x83\x2e\xa5\x06\x5f\xee\x7e\xc6\xd4\x6f\xdd\xcb\x21\x14\xb0\xab\xb3\xd7\xdf\xcf\x30\x11\x92\x57\xbc\xc0\x9c\xcf\xe1\xcd\x78\xa4\x5a\x85\xdf\x3b\x3a\x94\xec\x67\xc3\x18\x65\x71\x6f\x56\x8f\xde\xd7\x8d\xbc\x2a\xe2\xa2\xc5\xd2\xc1\x9e\xb9\xac\xcd\x75\xa4\xe3\x17\x73\x9e\x12\xf7\xe4\xd3\xd1\xa8\xf4\x45\xa4\x87\xaf\xaf\xe0\x76\x92\xca\xd8\x1c\xb9\xd9\x6c\x37\x43\xb2\x7a\xbf\xb9\x4a\xe7\xcb\x11\x56\x6e\x5a\x45\x2b\xc1\x25\x8e\xd8\x4f\xfc\xb8\x71\x95\x49\x56\x4e\xc7\x98\xda\x97\x4f\x6a\xdc\x4f\x67\x65\x92\x2e\x28\xb0\x0a\x3e\xa3\x6e\xa4\x01\x6e\xe0\x80\x9d\x75\x42\x87\xe7\x65\x94\xad\xf9\xb2\x94\x20\x52\x8c\x77\x6c\x29\x84\xfd\x73\xb9\xf5\x5b\x6f\xcb\xb7\x3d\x66\x1d\x50\xc2\x95\x4e\x91\x9d\x02\x8b\x98\xe7\xd8\xa5\x45\xb5\x0a\x0e\xae\x62\x53\x0b\xd7\x6e\xe7\x38\x43\x88\x82\xf3\x2b\x49\xe3\x6f\xf3\x52\xe4\xa8\x12\xe7\xa3\x5a\x5f\xb7\xc1\x51\x47\x9e\xa2\xec\xdf\x8e\xe5\xc5\x32\x11\x56\xc4\x3f\x1d\xac\xf2\xfa\xce\xa4\xf1\xf5\x2f\x5b\x95\xb6\xb5\x97\xd2\xf2\xfd\x29\x18\xf5\x68\xc8\xd4\x4c\x16\xa6\xa2\xef\xc1\x18\x89\x93\x69\x15\x3e\xc8\xc4\xb4\xcb\xa7\x6f\x67\xd2\x10\x9d\x06\xf2\x0c\xb1\x36\xa8\x6c\xcc\xf9\x9d\xc0\xb2\xa9\x59\xdc\x35\xcb\xc0\xb6\x7f\x22\xe0\x05\xdf\x2a\x93\x93\x50\x95\xbc\xe6\x46\x6b\x3d\xcd\xc8\x1f\x70\x7b\xd6\x94\xaf\x9b\x79\x9f\xac\x6a\x00\xe6\x77\xd9\x42\xef\xdc\x0f\x09\x80\x03\xa5\x7d\x53\xfe\x2d\x1b\xad\x6f\xbc\xa1\x1c\xe4\x86\xe0\xdd\xcf\xb1\x2e\x2c\x4d\x4b\x9f\x64\xaa\x94\x63\x57\x33\xed\xd0\x39\x37\x93\x19\x15\xeb\x98\xb0\x61\x0d\x26\x2a\xb6\x3f\x4c\xd0\x23\xc3\x97\x70\xf6\x96\x6f\xb8\x1a\x2e\x63\x7e\xe4\xae\x1e\x19\xcb\x21\xc2\x58\xb2\xdc\x1d\xba\x57\x7b\x55\xf3\x80\xac\x1d\xd4\x2a\x8f\x9d\x9a\xf0\xdd\x5a\x80\x11\x3e\xb8\xde\xfa\x7c\x26\xe2\x01\x1e\x4a\x4e\x10\xe5\xe1\x5f\xcb\xae\xca\xe7\x78\x46\x16\x83\x6a\x63\xf3\x8d\x67\xa8\x7b\x20\x46\x83\x91\x32\xd3\x5e\x56\x99\x07\x4f\x58\x3b\x86\x8a\xd1\xf4\x4c\xc8\x98\xf9\xad\xff\x7a\xc4\x03\x5c\xc5\x84\xe5\x70\xd2\x5a\xf2\x52\xca\x38\x8d\xf6\xa4\x22\xfb\x42\x7b\xbb\x2c\x27\x3d\x55\x66\xfc\xf6\xa6\x14\xb9\x4e\xa7\xf3\xb3\x0a\x39\x1e\x3a\xcd\xb5\x11\xc4\x67\x86\x82\x1e\x72\xac\xbf\xd9\x7e\x3c\xab\xf8\x20\xdd\x77\xd4\x9c\xc5\x90\xc0\x17\xbd\xec\x54\x9e\x0e\x92\x29\x0e\xa0\x22\x15\x28\xeb\x4b\x9b\x2a\xc0\xa5\x18\x0a\x1a\x63\x04\xb3\x94\x9a\xc0\x1c\x65\xbc\xca\xca\x80\x8c\x17\x04\x6d\xbe\x79\xe6\xee\x29\x81\xce\x4f\x91\xda\x19\x65\x14\xd5\x51\xa3\x66\xf6\xeb\xd0\x13\xa3\xce\xe6\xd3\x85\xcf\x2f\xbb\x9a\xf7\xd2\x07\xf3\xe9\x25\xf9\x58\xf6\x5b\xb4\x72\x0c\xd9\x65\xb5\x1d\xca\x4c\x33\xb7\x16\xbf\x82\x0b\x72\x43\x73\x17\x49\xe1\x3d\x2a\xe7\xd5\xd8\x7e\x33\xbf\x3a\x34\x45\x09\xbe\x44\x46\x49\xb0\xa6\xcc\xbb\xee\x10\x1f\x94\xd3\xa6\xd2\x5a\xba\xa1\xaf\xe0\xa1\x8c\xf8\xe3\xa5\x13\xd7\x6a\xe4\x9d\xb9\xc5\xfa\xbc\x7f\xf8\x4e\x47\x43\x59\xf1\x16\xdd\x72\x27\xeb\x57\xe1\x05\x47\xcc\xe8\xd0\x84\xd5\xf6\x00\xe8\x2d\xc6\x50\x3e\xef\xef\xe5\xd4\x56\xc2\xcc\x35\xf9\xac\xda\xfb\xcf\x16\xa3\x05\xd5\x7c\x4f\x40\x06\x00\x0d\xea\xbd\x2a\x68\x2d\xd2\x11\x9a\xed\x83\x61\xe8\x6b\xba\x0c\xc3\x94\xc8\x8d\xfc\x08\x3a\x17\x1b\xed\x4d\x09\x24\x5c\xaa\x4a\x12\xf0\xfd\x73\x31\xdb\xde\x87\x9e\xa0\x0c\x10\xe6\x42\x40\x2e\x61\x41\xe6\x80\xf3\xb5\x6e\x37\x42\x22\x9e\x8c\x3b\x8b\xb7\xb7\x0d\xb3\xc8\xfe\x89\xf9\x88\xb4\x1c\x32\x78\x9d\xc6\xfd\x8a\xd1\x80\x27\x52\x06\x4c\x76\xd7\x0b\x94\x8e\x92\xb2\xa7\x0f\x6b\xf5\xb1\x1a\x0c\x18\x63\x07\x83\xc6\x69\xdc\x1d\x5c\xb8\x8d\x5c\xa5\x77\x51\x2a\xbc\x58\xf7\x3b\x50\xc2\xce\xa4\xf2\x51\xe1\xf4\x8c\xa3\xd4\xda\x6f\xe2\x14\x01\x3e\xe8\x0d\xab\xa6\xc8\x95\x5a\xb6\xd6\xa9\xca\x1c\x11\x82\xb3\x2d\xaf\x1f\xec\x44\xd5\xda\x59\xad\xf6\x2c\xd9\xf4\xbf\x42\xbe\x5f\xc3\xa0\x43\xe1\xee\x3e\x1f\xe6\x2c\xe2\x30\xc8\x1c\x3d\xe0\x9f\x32\x6a\x72\xe6\xc9\xd3\x23\x52\xd9\x18\xf8\x9a\xe1\x48\xb1\x6a\xd8\x37\xb3\xc1\x50\x2a\xa4\x3f\x9c\x2b\xde\x61\xd2\xaa\x38\xb6\x2b\xc6\xb0\xa1\xd5\xbf\xa2\x4d\x1f\x9d\xca\xfe\xbd\x4d\x55\xc2\x53\x02\xb7\xe6\x4f\x64\x93\x61\x81\x38\xc3\x60\xb8\xfb\x34\x54\xbd\x42\xe2\xc4\x76\xce\x39\xa0\x15\x60\x8f\xb0\x7f\xad\xa6\x17\xec\x7e\x09\x8d\x06\x12\x51\x33\x80\xa5\x12\xdd\xcb\x4c\x9d\x0e\x38\xc2\xc6\xa5\x1b\x56\x83\x45\xa1\x0d\x16\xf9\x8b\xcd\x58\x8c\x69\x6e\xb4\x06\xa6\x30\x63\xc4\x4b\x8a\xc7\x9b\xbf\x55\xfe\x08\xc7\x8a\xa9\xb1\xca\xa1\x78\x83\xe9\x47\x28\xc0\x17\x96\x37\x7b\xae\x5b\x17\x63\xe4\x96\xce\x1e\xe2\x38\xe1\x20\xc8\x24\xd5\x25\xc0\x76\x92\x26\xdb\xe8\x67\xf4\x17\xa6\x0b\x96\x27\x29\x20\xa6\x62\x5b\x07\xd5\x6c\x0c\x26\xe0\x88\xee\xe0\x90\x30\x81\x59\x98\x41\x12\x39\x70\x6d\x3e\x66\x8c\xa4\x54\xed\x13\x5d\x58\x50\x11\x5a\xce\x00\x15\x9e\x58\xc2\x32\x0a\x15\xc2\x3b\xd4\xc1\x42\x86\xcc\x41\xb2\x8f\x77\x39\x9f\xf5\x25\x3d\xe6\x99\x0e\xb3\x74\xc5\xcd\x08\xfb\x0c\x9a\x9f\x2f\x54\x22\xc3\x1a\x0f\x3e\x7b\x04\x40\x92\x50\x5a\xe6\xf7\x42\xa6\x9f\xb0\x59\x22\x89\xbc\x27\x67\x66\xab\x1a\xa8\x58\xb3\xb8\x58\x04\xe1\x93\xbd\x9c\x2c\x62\xf0\x98\x5b\x19\x0e\x38\x06\xf5\x1d\xfb\xae\x64\xf5\xd8\xf5\xf0\xa7\x17\x64\x4f\x0b\xe3\x69\xef\xdb\x73\x46\x91\xcd\x2f\xc4\x1c\x4b\x43\xd6\x1b\xc6\x5f\x3d\xe6\x93\x42\xa1\xcc\x92\x21\x59\x52\x0d\x91\xdf\x73\xa1\x30\xa6\x2a\x39\x68\x0d\xf7\x5f\x6b\x35\x64\x5a\x16\xa1\x40\xae\x40\xad\x14\x95\x04\xcc\xbf\x2d\x4b\xc3\xee\xda\x50\xba\xb6\x93\x8d\x2a\x9c\x49\x6d\x02\xb8\x6a\x01\x45\x21\xea\x42\xbf\x1d\xb8\xc2\xc5\x78\x14\x29\xa3\x34\xed\x0d\xfa\x31\x71\x7f\x05\x5a\xdf\x86\x68\xa7\x63\x41\x30\x2d\x81\x66\x0c\x63\x67\xa3\x2c\x45\xdf\x5a\xa6\x0f\x99\xf0\xee\x9f\xc4\xf4\x33\x2d\x04\x28\x56\x24\xa5\xd9\x92\x0f\xc7\x07\xd1\x94\x52\x8e\x09\xcd\xbf\xa8\x6c\xc3\x58\x4e\x31\x92\x54\xfb\xd1\xcf\xba\x8c\xe5\xd2\xfe\xa2\x83\xdf\xdc\x4a\x24\xe6\x13\x90\xa6\x29\xbf\xe3\x11\x53\x45\x32\x27\x70\xa6\x5f\x11\x3f\x33\x57\x65\x46\x11\x86\xc5\x50\xc2\xb1\x28\xeb\x4b\x55\x3c\x37\xf7\xfa\x1c\x44\xa3\xdd\xb1\x21\x6f\x53\x6c\x26\x74\x96\x45\x5e\x84\xc8\x84\x46\x01\x18\xbf\xc7\xa4\x92\x79\x5a\x86\xab\x72\x7a\x9b\x3b\xfd\x10\x13\x27\xaf\xb7\x78\x90\xb7\x4c\x99\xa3\xcc\x35\x6d\xb7\xac\xf3\xdd\xf7\x96\x3e\xd3\x37\x6b\x3e\xf2\xcf\xb7\x2c\x3c\x1c\x72\x80\x3e\x9c\xdc\x8c\xff\xf3\x41\xe6\x2a\x09\xb8\x49\xa5\x94\x65\x72\x6a\xa1\xe7\x2f\x78\x30\x8b\x19\x6c\xd6\xe6\x94\xb5\xd2\x76\x7a\x99\xce\x10\xd7\x66\x88\x17\x6c\x2a\x88\x98\x89\x72\x5d\x8f\x9c\x8b\x29\x22\x23\x8d\x91\x2f\xca\xde\x5f\xd2\xc0\x9b\xa1\x44\x03\x1f\xdb\x3b\xbd\xd8\x70\x39\xa5\x36\xfa\x05\x78\x71\x79\xe1\xcb\x25\xdc\x69\xe1\x10\xfb\x91\xbc\x83\xbc\xf8\xa9\x74\x55\x1f\x75\xb4\xd2\x38\x4f\x09\xf8\x0a\xbd\x30\x10\x29\xd0\x2d\x8d\x3e\xb9\xf0\x10\x64\xd1\x70\xa2\xd8\xd8\xf9\x5a\x33\xb9\x76\xcf\xc5\x49\x10\x1f\xa8\x25\x0c\x36\x7e\x11\xd0\x19\x42\x17\x9f\x7b\x98\x9e\xfd\x19\x09\xe1\x1d\x77\x73\x5f\x90\xbe\x34\x09\x73\x2a\x2f\x5f\xc3\x86\xd0\x96\x2c\x0e\x1d\x9a\x2c\x6b\x8d\x8e\x55\xa5\x66\x2c\x88\x11\x0a\x02\x1f\x46\x45\xc0\x79\x12\xde\x0c\x98\x69\x8e\x9d\x4a\x75\xe4\xf3\x8d\xd7\x30\xd1\x29\x15\xf2\x51\x29\x4f\x1a\x16\xdc\xd1\xa2\xf0\xd2\xbd\x3b\x26\x7f\x42\x08\xb0\x67\xb3\x03\xae\x32\x95\x00\xe8\x36\x9d\x4e\x14\xb5\x5a\x1d\x0f\x5a\x9f\xd8\x77\x0e\xb8\xe5\x2a\xcc\x4f\xa4\x57\xfd\xae\xb2\xc0\x5a\x21\x7f\x66\xd7\xd5\x8d\x73\x55\xa2\xfa\x99\x2d\x0e\x47\x71\x92\xf6\x1e\x2a\x6b\x9c\xc9\xf5\x0e\x6c\x00\x49\x46\x9e\xf1\xf8\xd2\x3e\xbe\xf1\x71\x4e\x32\x9d\x8a\xc7\x97\x46\xc4\x72\xcd\x95\x24\xe7\xa3\xb9\xdf\xc1\xda\x28\x61\x46\x53\xb3\xad\x1a\x5c\x62\xdb\x74\xcf\xa2\x00\xe9\x96\xe1\x61\x0b\x50\xbe\x44\xb9\x23\xee\x8e\x96\x80\x66\xd9\x19\xea\xa6\x59\x63\x35\xb5\x70\x58\x4d\xa2\xae\xa2\x5b\x85\xb9\xa4\xe4\x61\x43\xa5\xfc\xc5\xf0\xca\x9b\x01\x8f\xb8\x0c\x62\xe5\x97\xc1\x7e\xac\xaa\xef\xe1\x3e\x57\xad\x70\x25\xd1\x72\x62\x69\x28\x7d\xf8\xc4\xea\x1a\x39\xd9\x99\xc0\x87\xc9\xa3\x4c\xbd\xfd\x51\xf2\xa6\x9a\x33\x9c\x8d\xd7\xce\x6d\x63\x06\x35\x58\x5f\x94\xb9\xdf\x55\xf6\x0d\x5f\x9c\xbb\xfc\x3c\x75\x13\x14\x63\x10\xce\xe1\x8a\x7a\xf6\xbd\x82\x75\x7d\x77\x23\x3c\xe5\xd3\x32\x00\x23\xb3\x5a\xb9\x53\x2e\xdf\xb6\xb5\x09\xe8\x4a\x87\x56\x7a\x24\xfd\x53\xb9\xee\xc8\xe7\x87\xfa\x82\x3a\x0b\xda\x11\x50\xc5\x42\x48\xd5\xfa\x18\x53\x3f\x0d\x15\xd6\x20\x4a\xd5\x25\xa9\xe2\x26\xff\x89\x06\x6a\xaf\x9b\x30\x4f\x2c\xc8\xa4\x03\xcb\x48\x3a\x41\x54\xe3\x39\xb4\x9f\x19\x38\x82\x40\x27\x5f\xdd\x30\x28\x68\xd9\x97\x3d\x7b\xd1\x92\x93\x18\x1c\xe6\x19\xbf\xee\xc2\x23\x90\x4d\xd8\x79\xc4\xe3\xef\x63\x1a\xe7\x13\x0d\x11\x6f\x02\x5a\x08\x86\xae\xbf\x7b\x58\x1b\xe6\x15\x0c\xef\xeb\x30\x7a\xfe\x79\x0d\xb7\x18\xff\x38\x4d\xc4\x87\xc3\x6b\x02\xc9\x13\x31\xfd\xe5\xf9\x22\xcb\x33\x7f\x1e\x87\x79\x78\xee\xa1\x72\xa4\x35\x41\x33\xf9\x65\x34\xcc\x0b\xb3\xed\x36\xb6\xb0\xa2\xf4\xa1\x61\xa9\xe0\xfc\x0b\x64\x7e\x58\xbc\xdf\x29\x4f\x41\x31\xe8\xbe\x35\x3d\x70\xe1\x23\x28\x18\xc1\x5f\x02\xdb\xa7\x2e\x20\x3b\x07\x79\x58\xbc\xdf\xc0\x70\x0e\x43\x5d\x44\x6e\x24\xfa\xe7\x5b\x46\xaa\xe5\x9e\x34\x45\xed\xa7\xf5\x80\xd3\xcc\x00\xad\x68\xd1\x42\x81\xa4\x06\xed\x5b\xa5\x5c\xa8\x07\x61\x0d\x86\x68\x9c\xc9\x37\xb8\xa5\x27\x28\xfc\xec\x85\x89\x75\x01\x85\x8e\x14\x01\xf3\xef\x27\x9c\x38\x0d\x49\x29\x89\x92\xfc\x05\xbe\x9c\xa0\x17\x79\x07\xd1\x13\xc5\xad\x3c\xc6\xef\x23\x67\x74\x73\x67\x74\x5e\x7f\x92\xb9\x33\x3e\x4d\x62\x15\x0a\x24\x9d\xad\xf6\x63\x17\x47\x0f\xb2\x8c\xd9\xc1\x07\x4b\x2e\x3d\xa2\xd3\x4f\x5f\x22\xb7\x62\xfb\xc3\xa2\x2e\xc0\xf0\x97\xaf\x51\x52\x06\xf7\xe6\x31\xf6\x6b\xb1\xe2\xd6\xe3\xc0\x63\x52\x1d\x91\x7e\x3e\x8c\x52\xc1\xa9\x23\x08\xd7\x30\xfe\x9e\xe1\xce\x79\xae\xad\xab\x57\x6e\x1c\x7a\xd0\x4c\x7d\x9f\x15\x47\xd2\x62\x6c\xe1\x18\x54\x08\xbb\x17\x02\x44\xa3\x4f\x78\xc8\x5b\x34\xe7\xd1\xf2\x7a\xb5\xdb\xf2\xd5\x2f\x15\x23\x88\x51\x22\x57\x27\xc8\x27\x87\x7f\xb3\x29\x4c\x78\x6a\xa4\xcc\x4d\x3d\xad\x4d\x77\xdf\x48\x42\x87\x4d\xa6\x92\xaa\x97\x0b\xf1\x96\x6f\xab\x38\xc4\xd6\x00\x1f\x99\xf9\x19\x23\x64\xb1\x05\x23\x30\x2a\xf3\xce\xb6\xa9\x6a\x91\xe4\x6f\x12\x72\x54\xee\x6b\xcb\xd6\x21\x65\x24\x4e\x36\x03\xcd\xa0\xea\xb8\x65\x2b\xb3\x1a\x53\x28\x4a\xd4\x7e\x2c\x66\x69\xbd\x8a\x16\x02\x71\x4b\x18\x3d\xb2\x72\x19\xf4\xe5\x15\xfc\xc9\xa0\x39\x84\x0a\xe8\xa1\x92\x22\x32\xcb\xb1\x1b\xb4\xf7\x9f\xba\x76\xe1\xf7\xfa\x3d\x48\xc7\xa5\x0d\x37\xd3\xa3\xd2\xe3\x11\xf5\x24\xb6\x4a\x63\xe4\x3d\xda\xac\x25\x63\x7f\x73\xaf\x11\xd8\xf0\xbc\x09\x72\x06\xc6\x50\x57\xc0\xf4\x53\x2b\x50\xfd\xad\x11\x75\x1a\x42\xe3\x37\x19\xbe\x86\x15\x14\x79\x7a\x22\xd8\x1f\x05\x78\x38\x60\xc3\xc5\x23\x93\x97\x44\x5c\x14\x17\x6d\xc2\xf8\x02\x8c\xf9\x87\x41\x7e\x8c\xbb\x3b\xe7\xc9\x73\x43\x45\xca\x51\x46\xb2\xc0\xad\xa8\x22\x52\x65\xc8\x07\x7c\x74\x81\x8c\xd3\xe3\x1e\xe1\xea\xd7\xbc\xd1\x5d\x78\x3b\x4b\x95\x12\x17\x4e\x36\x34\x02\x2d\x11\xae\x72\xc1\x58\xdb\x6d\xe4\xd8\xae\x17\xc8\x4c\x41\xdf\xf4\xf2\x64\x79\x9b\x41\x2c\xe5\xa9\x08\x66\xd9\x4e\x0c\x7e\x20\xcb\x8a\x21\x6a\xd9\x51\x29\x6e\x3b\x6e\xe5\xeb\xc2\x48\x69\xee\xf3\x6b\x70\x34\x46\x98\x6e\xc1\x46\x33\x10\xef\xf2\x21\xec\xc1\xa4\xef\x85\x89\xc7\xf3\x4f\xdf\x6c\x53\x36\xf8\x8b\x06\x98\xf1\xac\x06\xc8\x58\xd3\x72\xe4\x5c\x3d\x03\x3d\x63\x1c\x6d\x55\xb1\x7d\x9b\x40\xc4\xc8\xc6\xd8\xad\x20\x6b\x79\x9a\x16\xe8\xd3\x55\x76\x0d\xf3\xf3\x08\x35\x8d\xd1\x90\xac\xb9\xe3\x6d\x8d\x59\xf5\x88\xbb\x32\xca\xb0\x1e\x64\x47\xa7\xd1\xfd\x5e\x4c\x54\x1e\x4f\x8b\x3c\x24\x11\xc4\x72\xbf\x04\x26\x99\xd7\x93\x7b\x64\xb0\xf1\xf3\x18\x9f\x71\xc7\x1d\x3b\x73\x1b\x1c\x4c\xe3\xac\xe0\x93\x81\x4a\x6f\x7a\x3e\xcd\x45\xdf\x4a\xbd\x0b\x04\xb4\x1a\x51\xd6\x26\x05\x3f\x80\xe2\xe0\x28\xf8\x68\x96\x1d\x83\xcb\xbd\x8c\x27\x58\xde\x59\x48\x32\xcd\xb4\x98\x85\xa2\x64\x4c\xb6\xb1\x87\x24\xcf\xd5\x19\xac\xe3\x07\xe7\x74\x1b\x00\x23\x38\xfd\x75\x1b\x63\xfd\x95\x2b\x7a\x16\x53\xfd\x18\x21\x37\x73\x35\xe3\xe4\xb7\xc8\xc1\x5c\xaf\xee\x80\x67\x91\x88\xd4\x6b\x28\xff\x9b\x29\x9b\x2c\xa6\x3a\x0c\xa7\xa7\xe4\x23\xad\xf3\x74\x27\xc2\x67\xfe\xc8\xa2\x0c\xf0\x77\x97\x77\x2d\x59\x6f\xf3\x0b\x11\x7a\xdd\xb4\xf2\xc2\x1f\x57\x5a\xb2\xe4\x88\xb6\xe8\xe8\x41\xe8\x8e\x75\x5a\x9c\x33\x21\xd6\xd8\x1a\x44\xd6\xd7\x5f\x39\x11\xfd\xe8\x4b\xf3\x97\x88\x9b\x7d\x10\xaa\x14\x47\xb8\x5b\x7c\x3e\x8b\xb5\x62\x1f\x37\xb5\x31\xe0\xe2\xaf\x95\x8b\x96\x3f\x8a\x64\x1e\x8a\xfc\xd5\x4e\x3e\x53\xff\xbd\x42\xe6\x67\xd3\xec\x5d\xd5\xe7\x58\x6b\xd0\xcd\xaa\xde\x58\x2c\xd0\x34\x3a\x9f\xe1\xc1\x47\x8d\x81\x3a\xa4\xda\x57\x39\x1a\x85\xa6\x44\x33\x18\x6a\x5f\xf1\x75\x68\xf6\xf9\x26\xc3\xaa\xa1\xa0\x96\xb3\xc9\x69\x28\xd9\xa0\x99\x32\x1b\xdb\xc1\xc5\x27\xa2\x99\x7c\xc4\xe0\x53\x51\xc0\xc3\xf7\x4a\x91\x6b\x52\x64\xc0\xb8\x03\xb0\x99\x27\x15\xc3\xb2\x1e\xee\x04\x46\xe9\x44\x20\xd0\x9b\x71\xfa\x7a\x86\x7d\xfc\x16\xf6\x54\xcf\x82\x5d\x4c\x76\x19\x83\x51\x16\xee\x0c\xd1\x47\x50\x48\x29\x92\xe5\x86\x6e\x4b\x22\xe8\x95\xb1\x41\x18\x55\xe5\x2e\x7b\xfd\xab\x87\x79\xd5\x67\x5b\x08\x85\x96\x98\x80\xc1\xf8\xfe\x2b\xf2\x26\x93\x4a\x03\xe0\x87\x90\x6a\x03\xaa\xcc\xe4\x52\x60\x44\x33\x0a\x8c\xbc\xd5\x79\xb1\xfe\xe7\x4c\xe5\xe5\x6f\x0a\x34\xf4\xa1\x82\x49\x82\x31\x91\x87\xed\x2f\x73\xd7\x17\xd8\x09\x16\xb5\x02\xa5\x6f\xf6\x36\x29\xd4\x51\x9a\xd7\x8a\xd6\x3c\xf9\x25\x05\x6e\x77\xbc\x52\x76\xfd\x7c\x2a\x18\x68\x7d\x3a\xca\x63\xf7\xf4\xbb\xe7\xb6\x1f\xe9\x36\xa9\x16\x7b\xb4\x4a\x7c\xda\x73\x9c\x54\x1a\x47\x88\x4e\x10\x9a\x35\xb0\xf6\x4e\x78\x5c\x58\x8f\x47\x81\x3b\xe1\x03\x2b\x02\x37\x0b\x1d\xed\x8f\x0a\xb2\x8c\x17\x51\xf2\xb7\xc1\xbc\xd0\x8f\x42\x71\x32\xf7\x98\xbc\x4e\x56\xdc\x64\x92\x03\x12\xd8\x3e\x30\x99\x30\x6a\xec\xde\x13\x41\x7d\x5f\x61\xfd\x34\x53\x35\xe0\xe8\x5e\xc9\x8b\x70\x28\x59\x3c\xdc\xc8\xf1\xa1\x63\xfa\xdb\x44\xbf\xec\xc7\xc9\x2b\x80\xcb\x5e\x80\x66\xd2\xe6\xcb\xe5\xcd\x7d\x3e\xc5\xec\x42\x5f\xe0\x51\xb8\xf7\x76\x3b\x4a\x66\x75\x68\x7e\x40\x11\x06\x30\xb3\x96\x76\xda\x74\x19\x8b\x83\x56\xec\xb8\xb4\x26\x46\x40\xbf\xec\xe0\x3e\x10\xa0\x24\xe1\x28\x76\x7b\xeb\x72\x56\x31\x52\xde\x9e\xae\x15\xa7\xe3\x9b\xb1\x03\x94\x38\x66\x6e\x42\xed\x04\x93\x57\x50\xe4\xfb\x13\x89\x2b\x05\x8a\xe4\xab\x0e\xd3\xa1\x38\x7c\xaa\x0a\x9d\x90\x79\xe3\xe1\x24\xe0\x22\xd1\x7b\x3a\x40\x6d\x79\x41\x64\x84\x02\xee\xd1\x34\x7d\xd3\xac\xd6\xcc\x91\xae\xa5\xd9\xff\xce\x09\x1a\x1e\xa4\x0a\xfe\x3e\x31\xa8\xb6\xb8\x6e\x07\xe9\xe1\x7b\x9e\x8c\xce\xfa\x16\x3c\x9f\xb4\xbc\xb7\x80\x16\xe3\x51\x66\x9e\xe4\x6a\xe7\x0c\x55\xce\x24\x2e\x57\x96\x17\x9d\xec\x82\x9b\xcc\x30\x90\xae\xec\xc5\x3b\x22\x05\x42\xaa\x24\x80\x9e\xdb\xa1\xdd\x9c\x51\x64\x34\x54\x9c\x48\x0f\x46\xcc\x22\x8d\x9b\x30\x1f\x8c\xd6\xea\xe1\xfc\xc3\x54\x0c\x10\x20\x43\xcd\x75\x96\x37\xa2\x21\x81\xc4\x56\x46\xad\xfd\xc2\x22\x35\xc8\xa8\x43\x2a\xd4\x89\x79\xc2\x51\xe6\x7f\x36\x5f\xbb\xc0\xe3\x5c\x95\xb8\x11\x85\xd2\x58\xee\xb6\xac\xdc\x80\xeb\x57\x6e\xbf\x9d\x4e\x73\xef\x87\x27\x85\xc1\x0c\xbc\xff\xa8\x76\x4e\x58\x9f\x5d\xa2\xb7\xd4\x16\xf8\xa5\xd0\x0d\x82\xda\x9d\xc8\x70\xe3\xab\x30\x77\x9f\x2a\x15\xf5\xc0\xd9\x01\x70\x73\xc8\x37\x3a\x15\x28\x07\x2c\x9e\x82\x87\x50\xc5\x9a\x24\xb9\x55\x97\x9b\xe3\xc0\x02\x22\xdf\x7f\xd2\x78\x81\xfb\xb5\x42\x0d\xae\xe8\x34\x23\x8a\x44\x8f\x33\x85\x0f\x7d\xc0\xb0\xc1\x21\x64\x93\x95\x64\x10\x29\xa6\xe0\x1f\x69\x7b\x3a\x7d\x53\xfa\x2e\x73\xaa\x7d\x26\xe1\x19\x55\x43\x72\x48\x22\x28\xb2\x1f\x17\xe0\x9b\xa7\xbb\xda\xca\x3f\xc8\xe1\xee\x8d\x22\x4d\xc6\x91\xef\xa2\x00\xa9\x0e\x14\x7c\x1f\x73\xf0\x94\x8d\x52\xae\x5c\x23\x25\x43\x66\x75\x77\x0d\xba\x31\x6a\xc5\x91\x73\xff\x08\xc7\x5c\x1d\xb4\x13\x3e\xcc\x98\x8d\xd3\xc3\xe7\xa5\x2c\xa5\x87\xb1\x79\x00\x9c\x7d\x0e\xa3\xe3\x2a\x36\x04\x5d\xb1\xca\x18\xa7\x80\x37\xf4\x8f\x28\x78\x7f\x37\xb0\x08\xca\x08\x79\x41\x4b\xe5\x25\x72\x1d\x92\xf2\x8e\xbd\x2c\x3e\x70\xc7\x8b\xdd\x55\x9a\x2b\x99\x92\x4b\xff\x3d\xbb\xf9\xa8\x84\x5d\xdf\x47\x4c\x36\x43\x3b\xf3\x8b\xdd\x15\x68\xa5\x59\x01\x4e\x33\x93\x98\xe3\x9b\x2e\x6e\x84\xc8\x49\x16\x7e\x5d\xf9\x2a\xf9\x37\x11\xfc\x21\xcf\x33\xd9\x3e\xfc\xad\x2b\x7a\xa0\x31\x14\xea\x44\x17\xde\x65\xd3\xaa\xba\x37\xf6\x8d\x01\x1a\x25\x66\x81\xe6\x2f\xbd\xe5\x14\x4e\xea\xb7\x58\x3d\xdb\xc6\x86\xa4\xb5\xc5\x51\x25\x27\x22\x08\x58\xd4\xf2\xb9\xe7\x93\xde\x2e\xfc\x19\x24\x4a\x0a\x8b\x96\xa3\xc3\xd0\x80\xc0\xa0\x07\xca\x73\x30\x3d\xf2\x06\xac\x3e\xf9\x78\x26\xfd\x75\x1f\xf9\xe6\xda\x4d\x03\x1a\x67\xde\xa3\x00\x8d\x55\x60\x6f\xf0\x74\xe1\xc4\xfe\xde\x26\xcc\x6d\xb0\x1b\xad\x6f\xd1\x47\x45\x13\x83\xe5\xb6\xac\x2d\x34\x1d\xd7\x35\xdd\x59\xf9\xc2\x49\x21\x23\xe3\xa4\x65\x4e\x32\xf3\xa7\x6d\x51\x28\x82\x46\xe8\x05\xe3\x15\xdf\x37\x22\x1a\x8d\xba\x6c\x22\xc8\x0a\x95\xe9\x38\xc8\xe0\x50\xae\xaf\xf6\x3a\xf6\x5c\xb9\xed\xbd\xec\x1a\xe6\x00\x9f\x81\x31\x82\x86\x21\xd1\x50\x2d\x42\x63\xdf\x88\xd3\xff\x2a\x45\xb4\xdc\x40\x69\x51\xf5\x39\xfb\xe9\x95\x66\x4a\x23\x0b\x82\x20\x55\xc3\x32\xe3\xef\xe8\xa8\xd7\x88\x2d\xb6\x9c\x44\x43\xa7\xeb\xbe\xec\xe4\x69\x21\x04\xc9\x10\x6f\x4d\x07\xf6\x6d\x6f\x5f\xa0\xc5\x31\x63\xd3\xe8\x2b\xf9\x60\x62\x05\x65\x2e\x9d\x66\x6f\x48\xcc\x0f\xe1\x5b\xda\xa6\x5f\x54\x61\xc3\xd5\x4a\x41\xaf\xd5\x03\x4f\x90\x52\xd0\x2c\x98\x8f\x32\xeb\x17\x4e\x49\x4f\x79\xe2\xdb\x97\xda\x55\xc7\xaa\xf9\x74\x22\x37\xd4\x4e\xbc\xad\xa4\x25\x59\x30\xd5\x9a\xa1\x64\x42\x03\x6d\x74\xb2\x99\xe7\xbf\xe1\x58\xb9\x93\x22\x5b\xac\x7d\x77\x24\x74\x16\x2f\x15\x78\xb3\x98\x50\xdd\xec\x4c\xf5\xe0\x21\xa8\x13\xe0\x05\x47\x5c\xc9\xc5\xb2\x16\x8a\xea\x69\xb8\xb0\x99\xdd\x39\x0e\xc7\xeb\x97\x0e\xf7\xb0\x8f\xe7\xf6\xa6\x88\x53\xbb\x9a\xa9\x30\x3d\xe1\xf3\x81\x3f\xd8\xa6\xe4\xf0\x3e\xdf\x45\xc2\x61\x01\x00\x00\x80\x82\x8c\xaa\x74\x83\xa4\x59\xe8\xff\x17\x00\x00\xff\xff\xec\x84\xbb\x4a\x53\x3a\x00\x00") + +func dataVgcSonic128PngBytes() ([]byte, error) { + return bindataRead( + _dataVgcSonic128Png, + "data/VGC Sonic 128.png", + ) +} + +func dataVgcSonic128Png() (*asset, error) { + bytes, err := dataVgcSonic128PngBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/VGC Sonic 128.png", size: 14931, mode: os.FileMode(420), modTime: time.Unix(1531392864, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _dataVgcSonicPng = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x77\x79\x38\x14\x6a\xfb\xff\x50\xc8\x64\x5f\x3a\x33\x19\x1a\x34\x92\xcc\x62\x37\x13\xde\x30\x68\x18\xb2\x37\xf6\x75\x26\xc2\xcc\x98\x19\x8c\xb2\xcb\x16\x3a\xa8\xa4\xce\x88\x21\x5b\x07\x27\x4e\x1c\x2d\xd6\xc6\x58\x47\x96\x2c\x27\x92\xa5\x65\xe8\x90\xea\xc8\xce\xef\xf2\xd6\xfb\x7b\xdf\xab\xef\xb9\xaf\xeb\xfe\xe3\xb9\xef\xcf\xf2\x3c\xd7\x7d\xff\xf3\x64\xd8\xdb\x59\x89\x03\x8f\x02\x01\x00\x80\x38\xee\x2c\xd6\x11\x00\x00\xa0\xf6\xf3\x90\x20\x00\x00\x98\xf0\x25\x0f\x02\x00\x00\x01\xba\xa3\x95\x19\xa0\x76\x00\xb2\x00\x00\x00\x44\xa9\x67\xdd\xe8\x00\xc0\x61\xd9\xfd\x14\x00\xdc\x61\x81\x00\x00\x41\x5a\xb0\x33\x81\x41\xb0\xc5\x63\x02\x28\x61\x08\xbf\x40\x8a\x3f\x11\xc1\x0c\xa3\x02\xf6\xc3\x88\x89\x61\x86\x51\xc3\x88\x0c\x3f\x28\x33\x2c\x94\x4c\xc7\x30\x8d\x55\xfe\x8d\xc0\x90\xe9\x98\xfd\x32\x52\x05\xfa\x6f\x08\x23\xc4\x58\x85\x60\x6b\x0f\x35\xa7\xd0\x88\x50\x3d\x84\x2e\x02\xa5\x62\x02\x84\x42\xa1\x46\xb4\x40\x12\xc6\x11\x6b\xf9\x9d\x4e\x0b\x24\x19\xab\x04\x31\x18\x54\x0c\x12\x19\x15\x15\x85\x88\xd2\x41\x50\x68\x17\x90\x5a\x68\x34\x1a\x89\xd2\x46\x6a\x6b\xc3\x69\x81\x24\x38\x3d\x9a\xcc\xf0\x63\xc2\xc9\x74\xd5\x6f\x22\xff\xd1\xc1\x12\xe9\x01\xb4\x60\x2a\x23\x98\x42\x86\xee\x9f\xfd\xfc\x29\x11\x0c\x63\x15\x95\xef\x98\x6f\xf1\xcd\x88\x11\x4c\xfa\xaf\x13\x99\xfe\xfd\x59\x01\x94\x30\xe4\x7e\x07\xa9\x85\x40\x21\xff\x89\xc6\x0c\xa3\xfe\x33\x8b\xe9\x47\xfd\x46\x32\xf9\x2f\xcb\x68\x5f\x0a\xe3\xe6\x48\xa4\x53\x42\x23\xf6\x2f\x65\x62\xa0\x6d\x84\xfc\x3f\xc5\x1f\x09\xe6\x94\x30\x2a\x8d\x48\xa7\xef\xf7\xf4\xbe\xe3\xff\xb7\xf6\x23\x9e\xf0\x4f\x06\x84\x7f\x36\x60\x86\x51\x31\xe6\x34\xa2\x1f\x83\x42\x73\xa6\x50\x42\x4d\x2c\x43\xa3\x83\xc9\x17\xa0\xb6\x44\x3f\x06\xd4\x34\x80\x42\x23\x43\xf5\x10\xfa\x08\x7d\x23\xe4\x8f\xc0\x1f\x34\x6c\x29\x81\xc1\xa4\x68\xac\x1f\x83\x68\xa2\x8d\xd2\x32\x84\xa3\x0c\xe0\x5a\xda\xce\x5a\xda\x18\x3d\x3d\x8c\x8e\xce\x37\xfa\xff\x60\xfe\x33\x24\xe4\x0f\x53\xfa\xb6\x02\xc8\xef\x3b\x60\x02\x34\x42\xfe\xff\x7d\x32\x01\x96\xc3\x31\x51\x00\x00\x30\x04\x87\x35\x75\x26\x88\x66\xbb\x79\x64\x15\xba\xc8\xef\x0e\x84\x0d\xf4\x4f\x04\x5f\xb8\x74\x3b\xd2\xba\x5c\xe2\xa0\xc3\xa4\xb3\xf5\x87\xec\x43\x1a\x19\x92\x52\xc9\x19\x2d\x41\x72\x78\x54\xb6\x74\x3b\x47\xad\x85\x9f\xac\xe5\x3d\x97\x2c\xea\x0e\x54\xe6\x8a\xa8\x0a\xe3\x0e\x49\x1e\x7d\x91\xe7\xeb\xec\xff\x80\x63\xd3\x17\x14\xe2\x60\xed\xc1\xdf\xc1\x8c\xd8\x3d\x5f\xb8\x4d\x5f\xdd\x1a\x7b\x79\x79\x6f\x8f\x75\xfb\x48\x64\xe4\xee\x9f\x97\xf7\x42\x62\x3f\x78\xdc\x6a\xde\x8b\x7d\xd9\xb1\xfb\xf3\x57\xe0\x19\x1f\xa4\x19\x1c\x4e\x73\x9f\xb9\x57\x71\xd3\x5f\x40\x48\x62\x5d\x17\xdc\xfc\xd8\x35\x0a\x37\x52\x06\x94\x97\x25\x75\x18\x64\x09\xfd\x4c\x8b\x3a\xd9\xa4\x24\x9e\x79\x06\x55\x9e\x58\xa7\x5c\xce\xe5\x72\xab\x34\xab\x94\xd4\x2d\xe5\x6d\x0f\x0b\xf0\xaa\x18\x62\x7f\x4a\x1f\x19\x14\xe8\x7d\xc5\x27\x9f\xd1\x1a\xf2\x35\xbc\xf6\xfb\xf9\x9e\x85\xee\x7e\xf1\xa6\x04\x85\x15\x2d\x5e\xe0\x08\xa7\x3f\xd8\x22\xb3\x67\xf6\x9e\x59\x8d\x94\xf2\xe7\xf3\x88\x23\x3a\x7c\x8f\x1e\xd3\x53\x24\x61\x1c\x3c\xcd\x4e\x98\x05\xab\x00\x9f\xe8\x0a\x5f\x2d\x13\x7b\x04\x64\xf7\x66\x79\x0f\xbd\xc1\x64\xc8\x56\xd8\x2c\x68\x16\x7f\x18\xb4\x23\xe6\x82\x2b\xcc\xa2\x8c\xf5\x9c\x63\x6c\x48\x3d\x60\x0b\x75\xdb\x4b\xc2\x35\x8b\x36\x6f\xf8\x7f\xae\x6e\xc0\xf4\x83\x7d\x35\xbc\xbf\xf4\xa4\xc9\xc1\xcb\xb4\x71\xe5\xa6\x08\x49\x37\x87\x0a\xc3\xcc\x7c\xd8\xe4\x2b\x15\x46\x6b\x3c\x7a\xc7\x77\x98\x2d\xbb\x45\xe8\xc8\xc8\xd9\x50\xcf\xbb\xf2\x39\x16\x5a\xd4\x33\xb0\x11\x6d\xd6\x4f\xb6\x54\xef\xc4\xa9\x93\x12\xde\x9b\x76\x93\xfa\x67\x3c\x74\x32\x1f\x8f\x5b\x3f\xf6\x08\x87\xb8\x64\x7e\xa1\xc7\xf7\x5b\x5b\xaa\x7f\xb4\x05\xad\x99\xe9\xe3\x32\xa7\x3f\xb5\xcf\x34\xd7\xbc\x3c\xc7\xce\xf3\x3e\x84\x9f\xf6\xbb\x66\x6c\xd4\x02\xda\xfe\x2b\x73\x50\xe6\x8a\x0a\x4b\xe5\x83\x1d\xa1\x5d\x68\x67\x6a\x32\xb4\x0c\x04\x3a\x54\xa3\xd0\x35\x3e\x50\xac\xc0\x0b\x5a\x9d\x6b\x6c\x95\x50\x68\xe9\x65\x05\x38\x83\x1b\x53\xc0\x72\x5e\xaa\x15\x8b\x95\x93\x05\x89\xd5\xc5\xce\xe2\x55\xad\xbb\x8d\xc5\x3a\xd7\x9e\x9f\xe8\x22\x2e\x47\xbe\xdd\x0a\x7b\xd7\x31\x2f\xe7\xc2\x02\x3b\x60\x02\x5f\x99\x11\xe5\x2d\xff\x95\x38\x07\x00\x12\x75\xbf\x96\xee\x46\xdd\xd0\x40\xdb\x71\x2f\x87\x73\x75\x28\xd9\x6e\xd5\xca\x96\xa3\x86\x0e\x7d\xf3\x45\xc2\x36\x22\xc8\x2e\xfc\x5d\xf9\xc1\xb0\x00\xaa\xfc\x58\x8c\xed\x72\x1d\x6f\xf7\x28\x1d\xc9\x46\x41\x55\x3d\xa9\x78\x89\xfa\x11\x04\xe9\xa9\xe4\xeb\x43\x68\x4c\x04\x2f\xa2\x03\x5c\xae\x82\x83\x16\xa9\x61\xed\x46\x08\x84\x72\x1b\x5c\xa3\x9d\xc8\xab\x53\xea\xf0\xeb\x71\x51\xde\x8b\xfd\xca\xbf\x94\x96\x55\x54\x17\x48\xde\xb5\x06\xbe\x11\x08\xb8\x82\xe7\x89\x4b\xa5\x7b\x3b\xa6\x1f\xc8\xa6\xa7\x4b\xf4\x76\x1a\x61\x87\x87\x1b\xda\x7d\xec\xeb\xa4\xae\x05\x9f\xb8\x41\x85\xe4\x27\xb5\x94\xec\x84\x74\x88\x52\xa5\xd9\xdd\x6d\xa5\xb0\x80\xb1\xe4\x96\x17\x0d\x79\x06\x39\x07\x8d\x94\xd4\x54\x04\xec\x5c\x8a\xa5\x95\x33\x52\x6e\x82\xdc\xed\x70\x99\x2e\x95\xa6\x9b\x4d\xc2\xca\xe7\x49\xcc\x18\xf2\x84\x3f\x97\x0c\x04\x4b\x8e\xb1\x2c\x72\x5d\x8d\x43\x20\x7f\x4b\x03\x8f\xd5\x5f\xe1\x2e\x2d\xd8\x1f\x3e\x39\x2c\xe7\xd7\x93\x91\x00\x99\xe8\x1a\x3d\x3e\x15\xe1\x10\x5c\xfa\x4a\x42\xec\xec\x23\x8e\x97\xac\x0e\xa8\xa0\x0e\x7b\xa7\x1e\x99\x3f\x05\xb4\x58\xa2\x7a\x07\x4c\x38\xcd\xdb\x0b\x5f\xe5\xeb\x0f\x92\x17\xf1\x1a\xf9\x30\xee\xfb\xbc\x87\x2c\xd9\xd1\x3e\x48\x4f\x5f\x77\xd5\x2b\xd3\x08\x74\xaa\x6b\xe9\xb8\x19\x9b\x53\x5c\x1a\x6e\xed\x9a\xb1\x9c\x0b\x15\x5d\x5e\x69\x0f\x77\xb6\xc4\x16\x9e\x7e\x1a\x25\xcf\xa3\xb7\xb7\x66\x49\x85\xa4\xeb\x99\xa4\x2d\x62\xc4\x5e\xdf\xfb\xbd\x98\x2e\xfa\x25\x6d\x88\x65\xba\xa1\xa8\xbe\xb2\xfd\xf0\xc6\x23\xaf\xf8\x3c\x51\x91\x48\x09\x2d\xff\x7c\xe9\xa3\xaa\x3b\xde\xa2\x7f\xc4\x29\xf4\xa9\x0d\x91\x8b\x8d\xb2\xf6\x8c\x92\x0c\xee\x9a\x46\x08\x5d\x52\x14\x2a\x1d\x69\x5b\x39\xde\xb8\x0d\xba\x79\x53\xc5\xab\x7a\xae\xf5\xb5\xc5\x4e\x53\xe8\x1b\x27\xcb\x3a\x32\x58\x41\xa9\x63\xf1\x9c\x75\x5e\x39\xe8\xc5\x18\xd8\x73\x5b\x1c\x61\xa4\x7a\x0e\x74\xbf\x93\x75\xb6\xb2\x72\x6a\xf2\x9a\x6f\xaf\x4b\xb2\xbc\x27\x3a\x63\xfc\xae\x59\xf0\xa1\x51\x2d\xa1\x8d\x7c\x64\x40\xd0\x81\x3b\xe2\x9d\x3e\xda\x8a\x53\x0f\x15\xcd\xaf\xcf\xb9\x83\x3b\x3b\x8d\x0d\xbf\x9c\x90\x4b\x1d\x5c\x40\x40\x75\x75\x53\xa2\x3e\x72\x38\x3f\x45\xb8\xf7\xe2\x30\x3a\x09\x8b\xc7\xd5\x94\xb8\x9b\x14\xeb\xfa\x90\xcf\x07\x5f\x18\x8e\x67\x4b\x8d\x3c\x87\x5d\x6e\x7c\xa2\x9e\x3d\xd3\x4d\x67\xce\xa3\x53\x8a\xc8\x2e\xe7\x67\xbd\x26\x87\xc8\x6c\x8f\x8e\x6d\xc8\xd8\x54\x4a\xc6\x13\x62\x82\x9d\xc1\xc0\xcc\xa8\x15\x7e\xc8\xcc\x46\x8a\xd2\x8c\xe0\x2e\x73\xf8\x69\xcf\x9c\x45\xfd\xbf\xba\xb9\x90\xe2\xdf\x1a\x13\x76\xa2\x1f\x2e\xf2\xdd\xed\xb7\x0c\xc1\xec\x6e\xee\x61\xa7\xa6\x03\xeb\x6b\xb2\x95\x45\xf9\x1e\xd8\xd7\x54\x24\xf7\x23\x36\xc0\x72\x79\xfa\xf3\xa8\x6f\x6a\xfc\xee\x1a\xa8\x96\x75\xc6\x8f\x30\xab\x8c\xc8\x6c\xfb\x65\xb1\x5f\xed\x96\x4f\x7b\xae\x55\xf8\xba\x95\xe1\xaa\xae\xbb\x4e\x49\x67\x50\x5d\xcd\x86\xa6\xf5\x3c\xff\xe1\xf1\x4e\x3b\xaf\x8f\xba\x5e\x7f\xa9\x76\xc3\x84\xab\x50\xe9\x8f\x9c\x0a\xa6\xc1\x62\x2b\xcf\xb9\x69\x2e\x1e\x06\x71\x12\xaa\x06\x25\x21\xab\x71\x9f\xa3\x21\x93\xd9\xc3\x34\x98\x56\x62\x01\x7a\xa3\x6d\xbd\x30\x5e\x7a\x44\x72\x96\x57\x34\xcf\x19\x61\x2e\xa5\x19\x36\x1d\x58\xb2\x39\x72\xbf\x16\xa2\xf0\x32\x7d\xec\x69\x12\x07\xc3\x66\x2e\x62\x34\x64\x59\xbf\xd1\x52\x2e\x67\x67\x9c\xde\x14\xbc\xb6\x3b\x70\xbf\x21\x5b\xde\x60\x7e\xe7\x75\xd7\x8b\x0a\x8b\x5c\x9f\xcf\x5c\x34\x84\x09\x69\xa6\x75\x47\x08\x86\x4b\x82\x32\x22\xc5\xd3\x22\x4f\x0e\x95\xdd\x54\xaf\x3d\x13\xad\x47\xfa\x3a\xbb\x7d\x6c\xd5\x9b\x91\xd8\xeb\xce\x78\x99\x9b\xa6\x08\xf6\x54\x93\x2d\xd3\x6c\x87\x91\x34\xcc\x35\x6e\x6a\xed\x1c\xac\xbd\xdb\xf0\xac\x34\xb3\xe7\x89\xc3\xdb\xb8\xa1\xc3\x10\xa6\x18\x7d\xca\x2d\x99\x9d\x2f\x3a\x07\xab\x8c\x1a\xbb\x68\x65\x7f\x3a\x58\x10\xfe\xa8\x8b\x55\x92\xf4\x67\xf3\x27\x3d\x71\x49\x0f\x2f\xfa\x5a\xd9\x0a\xa1\xf2\x86\xf6\xfb\xdc\x78\xc7\x74\xa9\x14\x0d\x44\x11\x67\xda\x2a\xa5\xa3\xe3\xde\x05\xef\xbe\xae\xdd\x84\xd2\x27\x0e\x4a\x75\xd7\xd5\xee\x5f\x3f\xd1\xe0\xf8\x6b\xd1\x03\x00\x75\x8f\x9b\x45\x6d\x6b\x2a\xe7\x2e\x69\x6a\x7c\x0a\x2b\xee\x0c\xd0\xcf\x92\xb1\xd5\x99\xd1\x74\x83\x6c\x33\x5a\x5b\x14\xd2\xe2\x91\x10\x19\x7f\x44\xed\xdd\x67\x7f\x23\x4e\x27\xd1\x35\x0e\xb2\xaf\x76\x22\x08\xa1\xe3\xe6\x2b\xcc\x12\xe1\x5f\xf5\x07\x43\xf2\x8d\x52\x6c\xdc\xaa\x9a\xd0\x17\x1c\xc5\x86\x63\xf1\xaf\x0a\x0a\x1b\xcb\xfc\x14\xa7\x2e\x0a\x73\xe8\xf9\x22\x89\xf5\x39\x99\x8d\x56\xb7\xec\xab\x60\xe3\x60\x90\xe4\xdd\x49\x1b\xc1\x16\xfe\x55\xc8\xef\x5b\x86\x96\xea\x1b\xb9\x6b\x4f\x12\x36\x30\xfa\x71\x02\xb2\xc7\x1e\xae\xa0\x42\x14\x9d\x53\xeb\x56\x15\xa0\x36\x71\x1f\x8d\x50\xf5\x72\x3b\xb8\xaf\xcc\xda\xc5\xa7\x0d\xab\xd8\x5b\x79\x17\x75\xed\xe4\xef\xca\x7d\x0a\x2b\xe9\x4c\x94\x9e\x3b\xec\x0d\x80\x8c\xb3\x17\x5c\xa5\xbf\xe8\x65\xbd\x4e\xf5\xf8\xc3\xa6\x68\x76\xa8\xe2\x86\x08\x4e\xe4\x57\xfd\x39\xe3\xb5\xd2\x85\x13\x0a\xf2\x55\x32\xa5\xc4\xc6\xe3\x26\x31\xe6\x53\xc9\x43\x11\xf5\x55\xb5\x43\xa7\x28\x85\xa4\xf2\xb7\xd7\xd0\x35\x7f\x41\x54\xf2\x63\x22\xdc\x19\xb5\x35\xa8\x98\xa0\xb4\x46\xd0\x6f\x15\x81\xf4\x15\xb7\x17\x03\x2f\x4d\x05\x43\x2f\x01\xe6\x27\x51\xf1\x16\x65\x33\x1a\x12\x3f\xf1\x16\x9a\xee\x5f\x2b\xe5\x5d\x56\x6d\x6d\x31\xca\xcd\xec\xfd\xcc\x77\x6d\x8e\x1a\xae\xb9\x23\x5d\x4f\x38\xf8\x84\xe4\x25\x4d\xd3\xf4\x79\x5b\x50\x28\x75\x3e\x64\x22\xe6\xd8\x18\x79\x4c\xeb\xac\x5d\x85\x4f\x7b\x51\x6b\xf2\x4e\xab\x4c\x89\xab\xf5\x44\xa3\x79\x56\x44\x3e\x67\x45\xaa\xfd\x17\xed\xd3\x49\x6b\x71\x16\xc1\x61\xfd\x25\x6e\xe6\x3b\x9a\xf9\x94\x0e\x8e\xd7\xa5\xe7\xe2\x7d\x5c\x9e\x34\xeb\x93\x14\x48\x41\x85\xbf\x39\x5b\x08\x53\x58\xce\x9d\xb2\x3e\xff\x60\x57\x3f\x74\x2b\x4f\x37\x5a\x2c\xaf\xdd\x27\xbe\xc4\x69\xa6\xed\x77\xe6\xfb\xae\x3a\x72\xa6\xab\x2b\xa5\x83\xbb\x55\xb7\x16\x2e\x96\xed\xce\xdd\xe1\xe7\xa5\x0a\xac\x3e\x98\x55\x8a\x4d\x38\xd7\x52\xb9\xb5\xda\x7d\xbc\xdc\x24\x39\x51\x8b\xc8\x8a\xa6\x0c\xf8\xbf\x83\x3a\x05\xa5\x65\x2e\x1d\x89\xdd\x58\xe2\x4a\x23\x82\xde\x60\xcd\x62\x36\x3f\xa6\x0d\x43\x97\xd7\x1f\xc7\x71\x56\xe8\xcf\xde\xb7\xf8\xcc\x71\x4c\xc4\x7e\xe3\x6c\xc3\x9e\x3e\x3d\x5c\xb4\x59\x45\xa8\x0f\x8b\x03\x74\xad\xcd\x4c\xb5\xda\x2b\xe6\x92\xaf\xe6\x08\xe0\x7c\x77\xba\x5e\x38\xa9\x2a\x34\x0b\x4e\x6f\xc3\x2f\x56\xa0\x1e\xe0\xcb\x78\x47\x04\xfd\x58\x9b\x4e\xdc\xf5\x71\x64\xc0\x87\x58\x42\x2b\xec\x01\x3a\xfe\xd0\x1e\x73\xb8\xd1\xe5\xf0\x5b\xa2\xe1\xee\x39\xfc\xbf\x60\x72\xef\x37\x13\x15\xaf\xb2\x22\x59\xb1\x2b\xaf\x94\xae\xd3\x76\x01\x47\x7f\x82\x71\xe5\x95\x7c\xab\xcd\x7e\x16\x47\x32\x2e\x7c\x9d\x99\x79\xfe\x97\xd4\x1f\xe2\xd8\xd4\x37\x38\xff\x45\x23\x50\xec\xc7\x0d\x56\xd9\x48\x65\xc0\xa2\x44\x0c\xc7\x64\x21\x4b\x7c\xd2\x27\xb2\x44\xff\xd7\xcb\x79\x91\xfe\xe7\xd6\xee\x35\xb7\xb6\x75\x9a\x5f\x85\x4c\xbe\x5c\xf3\xfb\x90\x1c\xb4\x99\x3f\xeb\x15\x69\x9f\x19\xfa\xbe\xfc\x53\x47\xb2\xe1\xce\x1f\x54\x6f\x3e\x86\x3b\xd9\xa7\xe9\x1d\x2b\x83\x8c\xb7\xb9\xe5\xae\x98\xd5\xd2\x90\x0d\x44\x47\x23\xb7\x67\x22\x2d\x35\x84\xb3\x77\x1f\x6a\x45\x1e\xc8\xff\xd2\x37\x8e\xad\xaa\xf6\xe8\x4b\x31\x7f\xac\xae\xcd\x9e\x46\xbf\x83\x05\x6f\x60\xb2\xf0\x67\x0b\x46\x79\x0b\xfd\xca\xb3\x2e\xa9\x28\xfd\x32\x79\xcb\xca\x70\x81\x5b\x92\xa5\x9e\x9d\x13\x3c\x50\xe1\xf0\x42\xa7\x46\xcb\xdf\x98\x8b\x5d\xf6\x4a\xd8\x8c\x71\xd5\xd6\x3c\xab\x89\x2b\x3e\xeb\xd3\x4b\x46\x53\x0d\xfd\xf5\x8e\x88\x47\xc4\x3b\xfe\xaa\x32\x9e\x27\xfd\x7c\x51\x01\xb2\x86\xb7\xc5\x3e\x60\x1c\xaa\xc5\xee\xaf\xdf\xb8\x93\x0a\xd7\xce\x91\x07\x97\xbb\xd4\x76\x37\x82\xc7\x4a\xf6\x66\x7e\x86\xe1\xfb\xd8\x09\xa1\x6d\xc1\xdb\xa3\xed\x78\x0f\xe9\x74\x6c\x84\x80\xe8\x58\xe1\xb3\xb2\x13\x1f\x05\x6f\x28\x8b\xd2\xb1\x4f\x1f\x75\x5c\xd1\x46\x6d\x7f\x15\xd2\x2a\x4f\x6e\xd5\x05\x6d\xcd\x3e\x98\x4b\x85\x95\xdf\x20\x18\xb8\x57\x1f\xb1\xcd\x06\x88\x6c\xb2\xbb\xeb\xf3\x74\x6f\xad\xeb\x5a\xc9\xeb\xba\x37\xc4\xf2\x5d\xaf\x1f\x63\xd1\xaa\x7d\x0d\x73\xaa\xba\x53\x3b\x9b\xda\xbc\x96\x4e\xa9\x71\x35\x70\xef\x4e\x16\x89\xcc\x3e\x56\x10\xff\x5c\xeb\x9d\xbe\x07\x51\xd5\x98\xce\x36\x31\x7e\xe7\xcc\x13\xf6\x0f\xa6\x28\xe6\x0e\x86\x8a\x1a\x17\x64\xe5\xdd\xa9\x7f\x72\x35\x9a\x88\x12\xc4\x43\x3d\x7b\xce\x1d\x48\xda\x78\xbd\xe7\x74\x87\x24\x83\xae\x80\x95\x90\xda\x94\x65\x24\xc9\xc0\xda\x7b\x27\xbb\x42\x9f\x26\x58\x9b\x8c\xcb\xba\xbb\xbf\xe9\x15\x3c\x25\xca\xc1\x6e\x89\xc2\xc7\x24\xcf\xa0\xed\x3c\xc3\x95\xe4\x22\xff\x0e\xcc\xd2\x3c\xd0\xc9\xd1\xd4\x50\x03\x4b\x6c\x56\x17\x20\xdf\x29\xed\x09\xc1\x93\xbb\x1f\xdd\x3c\x3f\xc0\x95\x08\x7b\x7e\xe5\xec\x97\x58\x76\xef\xc5\x3e\x81\x80\xf0\x40\xc3\x4c\x31\x98\xae\xf6\x48\x3c\x3c\xc7\xc3\x1a\x38\x3a\x91\x74\x2b\x53\xd7\xbc\x9e\xde\x8f\xf7\x1c\x80\x0d\xde\x53\xa0\xaa\xe3\x59\xac\xde\xd5\xdb\x0b\xf3\x1a\x3f\xd1\x2f\xa8\xca\x6f\x36\x7b\xf6\xf7\x9f\x9f\xf3\xe6\x4d\x42\x8b\xe9\x2a\x6c\xdd\x48\xbb\x12\x7e\x7f\xad\x10\xff\x7d\xce\x16\xaf\x66\xf9\xc0\x72\x64\x63\xa0\x6a\x34\xcb\x15\x62\x8c\x37\x06\x34\x9a\xe6\x25\x7c\xa1\xe6\x08\xb1\xfa\x37\xec\xd7\xb5\xe0\xfe\xb7\x75\xd6\xe2\xc5\x26\xde\x8d\x2d\x19\xdf\xcc\xd8\xff\xda\xe0\x2c\xec\xb0\xb5\x66\xbe\x49\xff\x2f\x00\x00\xff\xff\x84\x03\xf6\x98\x44\x0d\x00\x00") + +func dataVgcSonicPngBytes() ([]byte, error) { + return bindataRead( + _dataVgcSonicPng, + "data/VGC Sonic.png", + ) +} + +func dataVgcSonicPng() (*asset, error) { + bytes, err := dataVgcSonicPngBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/VGC Sonic.png", size: 3396, mode: os.FileMode(420), modTime: time.Unix(1531392933, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + cannonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[cannonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "data/VGC Sonic 128.png": dataVgcSonic128Png, + "data/VGC Sonic.png": dataVgcSonicPng, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"} +// AssetDir("data/img") would return []string{"a.png", "b.png"} +// AssetDir("foo.txt") and AssetDir("notexist") would return an error +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + cannonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(cannonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} +var _bintree = &bintree{nil, map[string]*bintree{ + "data": &bintree{nil, map[string]*bintree{ + "VGC Sonic 128.png": &bintree{dataVgcSonic128Png, map[string]*bintree{}}, + "VGC Sonic.png": &bintree{dataVgcSonicPng, map[string]*bintree{}}, + }}, +}} + +// RestoreAsset restores an asset under the given directory +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) + if err != nil { + return err + } + return nil +} + +// RestoreAssets restores an asset under the given directory recursively +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + cannonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) +} + diff --git a/xbase/db.go b/xbase/db.go new file mode 100644 index 000000000..f1d34d4c0 --- /dev/null +++ b/xbase/db.go @@ -0,0 +1,65 @@ +package xbase + +import ( + "fmt" + "net" + "path/filepath" + "sync" + "time" + + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" +) + +func GetDB() (*gorm.DB, error) { + db, err := gorm.Open("sqlite3", filepath.Join(appDir, "main.db")) + if err != nil { + log.Fatal("failed to connect database", err) + } + return db, nil +} + +func waitForServices(services []string, timeOut time.Duration) error { + var depChan = make(chan struct{}) + var wg sync.WaitGroup + wg.Add(len(services)) + go func() { + for _, s := range services { + go func(s string) { + defer wg.Done() + for { + _, err := net.Dial("tcp", s) + if err == nil { + return + } + time.Sleep(100 * time.Millisecond) + } + }(s) + } + wg.Wait() + close(depChan) + }() + + select { + case <-depChan: // services are ready + return nil + case <-time.After(timeOut): + return fmt.Errorf("services aren't ready in %s", timeOut) + } +} + +func init() { + initPaths() + + db, _ := GetDB() + defer db.Close() + + db.AutoMigrate(&Scene{}) + db.AutoMigrate(&Actor{}) + db.AutoMigrate(&Tag{}) + db.AutoMigrate(&Image{}) + db.AutoMigrate(&PossibleFilename{}) + + db.AutoMigrate(&File{}) + db.AutoMigrate(&Volume{}) +} diff --git a/xbase/dms.go b/xbase/dms.go new file mode 100644 index 000000000..e0a3505e0 --- /dev/null +++ b/xbase/dms.go @@ -0,0 +1,214 @@ +package xbase + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "net" + "os" + "os/user" + "path/filepath" + "runtime" + "sync" + "time" + + "github.com/cld9x/xbvr/dms/dlna/dms" + "github.com/cld9x/xbvr/dms/rrcache" +) + +type dmsConfig struct { + Path string + IfName string + Http string + FriendlyName string + LogHeaders bool + FFprobeCachePath string + NoTranscode bool + NoProbe bool + StallEventSubscribe bool + NotifyInterval time.Duration + IgnoreHidden bool + IgnoreUnreadable bool +} + +func getDefaultFFprobeCachePath() (path string) { + _user, err := user.Current() + if err != nil { + log.Print(err) + return + } + path = filepath.Join(_user.HomeDir, ".dms-ffprobe-cache") + return +} + +type fFprobeCache struct { + c *rrcache.RRCache + sync.Mutex +} + +func (fc *fFprobeCache) Get(key interface{}) (value interface{}, ok bool) { + fc.Lock() + defer fc.Unlock() + return fc.c.Get(key) +} + +func (fc *fFprobeCache) Set(key interface{}, value interface{}) { + fc.Lock() + defer fc.Unlock() + var size int64 + for _, v := range []interface{}{key, value} { + b, err := json.Marshal(v) + if err != nil { + log.Printf("Could not marshal %v: %s", v, err) + continue + } + size += int64(len(b)) + } + fc.c.Set(key, value, size) +} + +func (cache *fFprobeCache) load(path string) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + dec := json.NewDecoder(f) + var items []dms.FfprobeCacheItem + err = dec.Decode(&items) + if err != nil { + return err + } + for _, item := range items { + cache.Set(item.Key, item.Value) + } + log.Printf("added %d items from cache", len(items)) + return nil +} + +func (cache *fFprobeCache) save(path string) error { + cache.Lock() + items := cache.c.Items() + cache.Unlock() + f, err := ioutil.TempFile(filepath.Dir(path), filepath.Base(path)) + if err != nil { + return err + } + enc := json.NewEncoder(f) + err = enc.Encode(items) + f.Close() + if err != nil { + os.Remove(f.Name()) + return err + } + if runtime.GOOS == "windows" { + err = os.Remove(path) + if err == os.ErrNotExist { + err = nil + } + } + if err == nil { + err = os.Rename(f.Name(), path) + } + if err == nil { + log.Printf("saved cache with %d items", len(items)) + } else { + os.Remove(f.Name()) + } + return err +} + +func StartDMS() { + var config = &dmsConfig{ + Path: "", + IfName: "", + Http: ":1338", + FriendlyName: "", + LogHeaders: false, + FFprobeCachePath: getDefaultFFprobeCachePath(), + NotifyInterval: 30 * time.Second, + } + + cache := &fFprobeCache{ + c: rrcache.New(64 << 20), + } + if err := cache.load(config.FFprobeCachePath); err != nil { + log.Print(err) + } + + dmsServer := &dms.Server{ + Interfaces: func(ifName string) (ifs []net.Interface) { + var err error + if ifName == "" { + ifs, err = net.Interfaces() + } else { + var if_ *net.Interface + if_, err = net.InterfaceByName(ifName) + if if_ != nil { + ifs = append(ifs, *if_) + } + } + if err != nil { + log.Fatal(err) + } + var tmp []net.Interface + for _, if_ := range ifs { + if if_.Flags&net.FlagUp == 0 || if_.MTU <= 0 { + continue + } + tmp = append(tmp, if_) + } + ifs = tmp + return + }(config.IfName), + HTTPConn: func() net.Listener { + conn, err := net.Listen("tcp", config.Http) + if err != nil { + log.Fatal(err) + } + return conn + }(), + FriendlyName: config.FriendlyName, + RootObjectPath: filepath.Clean(config.Path), + FFProbeCache: cache, + LogHeaders: config.LogHeaders, + NoTranscode: config.NoTranscode, + NoProbe: config.NoProbe, + Icons: []dms.Icon{ + { + Width: 48, + Height: 48, + Depth: 8, + Mimetype: "image/png", + ReadSeeker: bytes.NewReader(MustAsset("data/VGC Sonic.png")), + }, + { + Width: 128, + Height: 128, + Depth: 8, + Mimetype: "image/png", + ReadSeeker: bytes.NewReader(MustAsset("data/VGC Sonic 128.png")), + }, + }, + StallEventSubscribe: config.StallEventSubscribe, + NotifyInterval: config.NotifyInterval, + IgnoreHidden: config.IgnoreHidden, + IgnoreUnreadable: config.IgnoreUnreadable, + } + go func() { + log.Info("Starting DMS") + if err := dmsServer.Serve(); err != nil { + log.Fatal(err) + } + }() + // sigs := make(chan os.Signal, 1) + // signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) + // <-sigs + // err := dmsServer.Close() + // if err != nil { + // log.Fatal(err) + // } + // if err := cache.save(config.FFprobeCachePath); err != nil { + // log.Print(err) + // } +} diff --git a/xbase/log.go b/xbase/log.go new file mode 100644 index 000000000..7a338e60a --- /dev/null +++ b/xbase/log.go @@ -0,0 +1,30 @@ +package xbase + +import ( + "os" + "runtime" + + "github.com/emicklei/go-restful" + "github.com/sirupsen/logrus" + "github.com/x-cray/logrus-prefixed-formatter" +) + +var log = logrus.New() + +func APIError(req *restful.Request, resp *restful.Response, status int, err error) { + log.Error(req.Request.URL.String(), err) + resp.WriteError(status, err) +} + +func init() { + log.Out = os.Stdout + log.SetLevel(logrus.InfoLevel) + + if runtime.GOOS == "windows" { + log.Formatter = &prefixed.TextFormatter{ + DisableColors: true, + } + } else { + log.Formatter = &prefixed.TextFormatter{} + } +} \ No newline at end of file diff --git a/xbase/model_actor.go b/xbase/model_actor.go new file mode 100644 index 000000000..07c66a18a --- /dev/null +++ b/xbase/model_actor.go @@ -0,0 +1,15 @@ +package xbase + +import ( + "time" +) + +type Actor struct { + ID uint `gorm:"primary_key" json:"id"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt *time.Time `sql:"index" json:"-"` + + Name string `gorm:"unique_index" json:"name"` + Count int `json:"count"` +} diff --git a/xbase/model_file.go b/xbase/model_file.go new file mode 100644 index 000000000..04639b541 --- /dev/null +++ b/xbase/model_file.go @@ -0,0 +1,46 @@ +package xbase + +import ( + "os" + "path/filepath" + "time" +) + +type File struct { + ID uint `gorm:"primary_key" json:"id"` + CreatedAt time.Time `json:"created_at" json:"-"` + UpdatedAt time.Time `json:"updated_at" json:"-"` + + Path string `json:"path"` + Filename string `json:"filename"` + Hash string `json:"-"` + Size int64 `json:"size"` + CreatedTime time.Time `json:"created_time"` + UpdatedTime time.Time `json:"updated_time"` + SceneID uint `json:"-"` + Scene Scene `json:"-"` + + VideoWidth int `json:"video_width"` + VideoHeight int `json:"video_height"` + VideoBitRate int `json:"video_bitrate"` + VideoAvgFrameRate string `json:"-"` + VideoCodecName string `json:"-"` +} + +func (f *File) GetPath() string { + return filepath.Join(f.Path, f.Filename) +} + +func (f *File) Save() error { + db, _ := GetDB() + err := db.Save(f).Error + db.Close() + return err +} + +func (f *File) Exists() bool { + if _, err := os.Stat(f.GetPath()); os.IsNotExist(err) { + return false + } + return true +} diff --git a/xbase/model_image.go b/xbase/model_image.go new file mode 100644 index 000000000..2c6848ed5 --- /dev/null +++ b/xbase/model_image.go @@ -0,0 +1,39 @@ +package xbase + +import ( + "time" +) + +type Image struct { + ID uint `gorm:"primary_key" json:"-"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt *time.Time `sql:"index" json:"-"` + + ActorID uint `json:"-"` + SceneID uint `json:"-"` + URL string `json:"url"` + Type string `json:"type"` + Orientation string `json:"orientation"` +} + +func (i *Image) GetPath() string { + return "" +} + +func (i *Image) Save() error { + db, _ := GetDB() + err := db.Save(i).Error + db.Close() + return err +} + +func (i *Image) Delete() error { + db, _ := GetDB() + err := db.Delete(i).Error + db.Close() + + // TODO: delete downloaded file + + return err +} diff --git a/xbase/model_scene.go b/xbase/model_scene.go new file mode 100644 index 000000000..6eb6bbb8a --- /dev/null +++ b/xbase/model_scene.go @@ -0,0 +1,154 @@ +package xbase + +import ( + "strings" + "time" + + "github.com/araddon/dateparse" +) + +type PossibleFilename struct { + ID uint `gorm:"primary_key" json:"id"` + Name string `gorm:"unique_index" json:"name"` + Scenes []Scene `gorm:"many2many:scene_filenames;" json:"scenes"` +} + +type Scene struct { + ID uint `gorm:"primary_key" json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt *time.Time `sql:"index" json:"-"` + + SceneID string `json:"scene_id"` + Title string `json:"title"` + SceneType string `json:"scene_type"` + Studio string `json:"studio"` + Site string `json:"site"` + Tags []Tag `gorm:"many2many:scene_tags;" json:"tags"` + Cast []Actor `gorm:"many2many:scene_cast;" json:"cast"` + Filenames []PossibleFilename `gorm:"many2many:scene_filenames;" json:"filename"` + Images []Image `json:"images"` + Files []File `json:"file"` + Duration int `json:"duration"` + Synopsis string `json:"synopsis" sql:"type:text;"` + ReleaseDate time.Time `json:"release_date"` + ReleaseDateText string `json:"release_date_text"` + CoverURL string `json:"cover_url"` + SceneURL string `json:"scene_url"` + Rating int `json:"rating"` + Favourite bool `json:"favourite"` + Wishlist bool `json:"wishlist"` + Watchlist bool `json:"watchlist"` + IsAvailable bool `json:"is_available"` + IsAccessible bool `json:"is_accessible"` +} + +func (i *Scene) Save() error { + db, _ := GetDB() + err := db.Save(i).Error + db.Close() + return err +} + +func (o *Scene) GetIfExist(id string) error { + db, _ := GetDB() + defer db.Close() + + return db.Preload("Tags").Preload("Cast").Preload("Filenames").Where(&Scene{SceneID: id}).First(o).Error +} + +func (o *Scene) GetIfExistURL(u string) error { + db, _ := GetDB() + defer db.Close() + + return db.Preload("Tags").Preload("Cast").Preload("Filenames").Where(&Scene{SceneURL: u}).First(o).Error +} + +func (o *Scene) GetFiles() ([]File, error) { + db, _ := GetDB() + defer db.Close() + + var files []File + db.Where(&File{SceneID: o.ID}).Find(&files) + + return files, nil +} + +func (o *Scene) CreateUpdateFromExternal(ext ExtScene) error { + // Check if scene exist + // o.GetIfExist(ext.SceneID) + + // Save + db, _ := GetDB() + defer db.Close() + + db.Where(&Scene{SceneID: ext.SceneID}).FirstOrCreate(&o) + + o.SceneID = ext.SceneID + o.Title = ext.Title + o.SceneType = ext.SceneType + o.Studio = ext.Studio + o.Site = ext.Site + o.Duration = ext.Duration + o.Synopsis = ext.Synopsis + o.ReleaseDateText = ext.Released + o.CoverURL = ext.Covers[0] + o.SceneURL = ext.HomepageURL + + if ext.Released != "" { + dateParsed, err := dateparse.ParseLocal(strings.Replace(ext.Released, ",", "", -1)) + if err == nil { + o.ReleaseDate = dateParsed + } + } + + db.Save(o) + + // Clean & Associate Tags + var tmpTag Tag + for _, name := range ext.Tags { + tagClean := convertTag(name) + if tagClean != "" { + tmpTag = Tag{} + db.Where(&Tag{Name: tagClean}).FirstOrCreate(&tmpTag) + db.Model(&o).Association("Tags").Append(tmpTag) + } + } + + // Clean & Associate Actors + var tmpActor Actor + for _, name := range ext.Cast { + tmpActor = Actor{} + db.Where(&Actor{Name: strings.Replace(name, ".", "", -1)}).FirstOrCreate(&tmpActor) + db.Model(&o).Association("Cast").Append(tmpActor) + } + + // Associate Filenames + var tmpSceneFilename PossibleFilename + for _, name := range ext.Filenames { + tmpSceneFilename = PossibleFilename{} + db.Where(&PossibleFilename{Name: name}).FirstOrCreate(&tmpSceneFilename) + db.Model(&o).Association("Filenames").Append(tmpSceneFilename) + } + + // Associate Images (but first remove old ones) + db.Unscoped().Where(&Image{SceneID: o.ID}).Delete(Image{}) + + for _, u := range ext.Covers { + tmpImage := Image{} + db.Where(&Image{URL: u}).FirstOrCreate(&tmpImage) + tmpImage.SceneID = o.ID + tmpImage.Type = "cover" + tmpImage.Save() + } + + for _, u := range ext.Gallery { + tmpImage := Image{} + db.Where(&Image{URL: u}).FirstOrCreate(&tmpImage) + tmpImage.SceneID = o.ID + tmpImage.Type = "gallery" + tmpImage.Save() + } + + return nil +} diff --git a/xbase/model_tag.go b/xbase/model_tag.go new file mode 100644 index 000000000..3a0434c20 --- /dev/null +++ b/xbase/model_tag.go @@ -0,0 +1,272 @@ +package xbase + +import ( + "strings" + + "github.com/thoas/go-funk" +) + +type Tag struct { + ID uint `gorm:"primary_key" json:"id"` + Scenes []Scene `gorm:"many2many:scene_tags;" json:"scenes"` + Name string `gorm:"index" json:"name"` + Clean string `gorm:"index" json:"clean"` + Count int `json:"count"` +} + +func (t *Tag) Save() error { + db, _ := GetDB() + err := db.Save(t).Error + db.Close() + return err +} + +func convertTag(t string) string { + t = strings.ToLower(t) + + if funk.Contains([]string{"180", "60fps", "60 fps", "5k", "5k+", "big dick", "big cocks", "axaxqxrrysrwqua", "girl-boy", "virtual reality", "vr porn"}, t) { + return "" + } + + if funk.Contains([]string{"sixty-nine"}, t) { + return "69" + } + + if funk.Contains([]string{"anal"}, t) { + return "anal sex" + } + + if funk.Contains([]string{"butt plug"}, t) { + return "anal toys" + } + + if funk.Contains([]string{"cum in ass", "creampie - ass"}, t) { + return "anal creampie" + } + + if funk.Contains([]string{"athletic"}, t) { + return "athletic body" + } + + if funk.Contains([]string{"threesome bgg", "bgg", "girl-girl-boy"}, t) { + return "threesome ffm" + } + + if funk.Contains([]string{"threesome bbg", "bbg", "mmf"}, t) { + return "threesome fmm" + } + + if funk.Contains([]string{"big boobs"}, t) { + return "big tits" + } + + if funk.Contains([]string{"blow job"}, t) { + return "blowjob" + } + + if funk.Contains([]string{"boobs job", "titty fucking", "titjob"}, t) { + return "titty fuck" + } + + if funk.Contains([]string{"catsuite"}, t) { + return "catsuit" + } + + if funk.Contains([]string{"cum swapping"}, t) { + return "cum swap" + } + + if funk.Contains([]string{"cum shot", "cum-shot"}, t) { + return "cumshot" + } + + if funk.Contains([]string{"curvy woman"}, t) { + return "curvy" + } + + if funk.Contains([]string{"cowgirl reverse"}, t) { + return "reverse cowgirl" + } + + if funk.Contains([]string{"deepthroat", "deepthroating"}, t) { + return "deep throat" + } + + if funk.Contains([]string{"dominating"}, t) { + return "dominant" + } + + if funk.Contains([]string{"double penetration"}, t) { + return "dp" + } + + if funk.Contains([]string{"doggy", "doggy style"}, t) { + return "doggystyle" + } + + if funk.Contains([]string{"face cumshot", "facial cumshot", "facial", "face cumshot"}, t) { + return "cum on face" + } + + if funk.Contains([]string{"girlfrien"}, t) { + return "girlfriend" + } + + if funk.Contains([]string{"hand job"}, t) { + return "handjob" + } + + if funk.Contains([]string{"latin", "latin babe"}, t) { + return "latina" + } + + if funk.Contains([]string{"lesbian love", "lesbians"}, t) { + return "lesbian" + } + + if funk.Contains([]string{"milfs", "cougar", "mother", "mom", "british mom"}, t) { + return "milf" + } + + if funk.Contains([]string{"european"}, t) { + return "euro" + } + + if funk.Contains([]string{"red head"}, t) { + return "redhead" + } + + if funk.Contains([]string{"role playing"}, t) { + return "role play" + } + + if funk.Contains([]string{"shaved"}, t) { + return "shaved pussy" + } + + if funk.Contains([]string{"squirt"}, t) { + return "squirting" + } + + if funk.Contains([]string{"teens"}, t) { + return "teen" + } + + if funk.Contains([]string{"trimmed"}, t) { + return "trimmed pussy" + } + + if funk.Contains([]string{"voayer"}, t) { + return "voyeur" + } + + if funk.Contains([]string{"small boobs", "small natural tits"}, t) { + return "small tits" + } + + if funk.Contains([]string{"natural boobs"}, t) { + return "natural tits" + } + + if funk.Contains([]string{"medium boobs"}, t) { + return "medium tits" + } + + if funk.Contains([]string{"shaved"}, t) { + return "shaved pussy" + } + + if funk.Contains([]string{"pussy eating"}, t) { + return "pussy licking" + } + + if funk.Contains([]string{"pussy cumshot"}, t) { + return "cum on pussy" + } + + if funk.Contains([]string{"tits cumshoot"}, t) { + return "cum on tits" + } + + if funk.Contains([]string{"hairy", "hairy bush"}, t) { + return "hairy pussy" + } + + if funk.Contains([]string{"no tattoo"}, t) { + return "no tattoos" + } + + if funk.Contains([]string{"tattoo", "tatoos"}, t) { + return "tattoos" + } + + if funk.Contains([]string{"piercing", "pirced pussy"}, t) { + return "piercings" + } + + if funk.Contains([]string{"russian girl"}, t) { + return "russian" + } + + if funk.Contains([]string{"spanish girl"}, t) { + return "spanish" + } + + if funk.Contains([]string{"stepbro"}, t) { + return "step brother" + } + + if funk.Contains([]string{"stepsis"}, t) { + return "step sister" + } + + if funk.Contains([]string{"toys", "vibrator"}, t) { + return "sex toys" + } + + if funk.Contains([]string{"ass cumshot"}, t) { + return "cum on ass" + } + + if funk.Contains([]string{"busty"}, t) { + return "big tits" + } + + if funk.Contains([]string{"mature mother"}, t) { + return "mature" + } + + if funk.Contains([]string{"latin step sister"}, t) { + return "latina" + } + + if funk.Contains([]string{"group"}, t) { + return "group sex" + } + + if funk.Contains([]string{"lesbian mom"}, t) { + return "lesbian" + } + + if funk.Contains([]string{"twin sisters"}, t) { + return "twins" + } + + if funk.Contains([]string{"threesomes"}, t) { + return "threesome" + } + + if funk.Contains([]string{"feet cumshot"}, t) { + return "cum on feet" + } + + if funk.Contains([]string{"black female"}, t) { + return "black" + } + + if funk.Contains([]string{"double penetration"}, t) { + return "dp" + } + + return t +} diff --git a/xbase/model_volume.go b/xbase/model_volume.go new file mode 100644 index 000000000..180d66025 --- /dev/null +++ b/xbase/model_volume.go @@ -0,0 +1,244 @@ +package xbase + +import ( + "encoding/json" + "io" + "net/http" + "os" + "path" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/creasty/defaults" + "github.com/djherbis/times" + "github.com/jinzhu/gorm" + "github.com/pkg/errors" + "github.com/thoas/go-funk" + "github.com/vansante/go-ffprobe" + "gopkg.in/cheggaaa/pb.v1" +) + +type Volume struct { + gorm.Model + Path string + LastScan time.Time + IsEnabled bool + IsAvailable bool +} + +func (o *Volume) IsMounted() bool { + if _, err := os.Stat(o.Path); os.IsNotExist(err) { + return false + } + return true +} + +func (o *Volume) Save() error { + db, _ := GetDB() + err := db.Save(o).Error + db.Close() + return err +} + +func (o *Volume) Files() []File { + var allFiles []File + db, _ := GetDB() + db.Where("path LIKE ?", o.Path+"%").Find(&allFiles) + db.Close() + return allFiles +} + +func (o *Volume) Rescan() error { + if o.IsMounted() { + notAllowedFn := []string{".DS_Store", ".tmp"} + allowedExt := []string{".mp4", ".avi", ".wmv", ".mpeg4", ".mov"} + + procList := make([]string, 0) + + _ = filepath.Walk(o.Path, func(path string, f os.FileInfo, err error) error { + if !f.Mode().IsDir() { + // Make sure the filename should be considered + if !funk.Contains(notAllowedFn, filepath.Base(path)) && funk.Contains(allowedExt, strings.ToLower(filepath.Ext(path))) { + + // cleanPath := strings.Replace(path, o.Path+string(os.PathSeparator), "", -1) + + var fl File + db, _ := GetDB() + err = db.Where(&File{Path: filepath.Dir(path), Filename: filepath.Base(path)}).First(&fl).Error + db.Close() + + if err == gorm.ErrRecordNotFound { + procList = append(procList, path) + } + } + } + return nil + }) + + bar := pb.StartNew(len(procList)) + for _, path := range procList { + fStat, _ := os.Stat(path) + fTimes, _ := times.Stat(path) + // fHash, _ := hashFileXX(path) + + var fl File + fl = File{ + Path: filepath.Dir(path), + Filename: filepath.Base(path), + Size: fStat.Size(), + CreatedTime: fTimes.BirthTime(), + UpdatedTime: fTimes.ModTime(), + // Hash: fHash, + } + + ffdata, err := ffprobe.GetProbeData(path, time.Second*3) + if err != nil { + log.Errorf("Error running ffprobe", path, err) + } + + vs := ffdata.GetFirstVideoStream() + bitRate, _ := strconv.Atoi(vs.BitRate) + fl.VideoAvgFrameRate = vs.AvgFrameRate + fl.VideoBitRate = bitRate + fl.VideoCodecName = vs.CodecName + fl.VideoWidth = vs.Width + fl.VideoHeight = vs.Height + + err = fl.Save() + if err != nil { + log.Errorf("New file %s, but got error %s", path, err) + } + + bar.Increment() + } + + bar.Finish() + + o.LastScan = time.Now() + o.Save() + + // Check if files are still present at the location + allFiles := o.Files() + for i := range allFiles { + if !allFiles[i].Exists() { + log.Info(allFiles[i].GetPath()) + db, _ := GetDB() + db.Delete(&allFiles[i]) + db.Close() + } + } + } + + return nil +} + +func (o *Volume) SaveLocalInfo() { + if o.IsMounted() { + var files []File + + db, _ := GetDB() + _ = db.Where("path LIKE ?", o.Path+"%").Find(&files).Error + defer db.Close() + + for i := range files { + fn := files[i].Filename + + var pfn PossibleFilename + var scenes []Scene + db.Where(&PossibleFilename{Name: path.Base(fn)}).First(&pfn) + db.Model(&pfn).Preload("Cast").Preload("Tags").Related(&scenes, "Scenes") + + if len(scenes) == 1 { + downloadFile(scenes[0].CoverURL, files[i].GetPath()+".png") + saveJSON(scenes[0], files[i].GetPath()+".json") + + files[i].SceneID = scenes[0].ID + files[i].Save() + } + } + } +} + +func caseInsensitiveContains(s, substr string) bool { + s, substr = strings.ToUpper(s), strings.ToUpper(substr) + return strings.Contains(s, substr) +} + +func downloadFile(url, destPath string) error { + resp, err := http.Get("http://127.0.0.1:9999/img/700x/" + strings.Replace(url, "://", ":/", -1)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return errors.Errorf("HTTP status code %d", resp.StatusCode) + } + + out, err := os.Create(destPath) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + return nil +} + +type InfoFile struct { + Position float32 `json:"position" default:"0.0"` + DisplayName string `json:"display"` + PresetID int `json:"presetId" default:"0"` + RememberPosition int `json:"rememberPosition" default:"1"` + Present bool `json:"present" default:"false"` + PlaybackType int `json:"playbackType" default:"15"` + Type int `json:"type" default:"1"` + SceneDetails Scene `json:"scene_details"` +} + +func saveJSON(sc Scene, destPath string) error { + out, err := os.Create(destPath) + if err != nil { + return err + } + defer out.Close() + + info := &InfoFile{} + defaults.Set(info) + + c := make([]string, 0) + for i := range sc.Cast { + c = append(c, sc.Cast[i].Name) + } + + info.DisplayName = strings.Join(c, ", ") + " - " + sc.Title + info.SceneDetails = sc + + infoJSON, _ := json.Marshal(info) + + out.Write([]byte(infoJSON)) + if err != nil { + return err + } + + return nil +} + +func CheckVolumes() { + db, _ := GetDB() + defer db.Close() + + var vol []Volume + db.Find(&vol) + + for i := range vol { + vol[i].IsAvailable = vol[i].IsMounted() + vol[i].Save() + } +} diff --git a/xbase/paths.go b/xbase/paths.go new file mode 100644 index 000000000..577200afa --- /dev/null +++ b/xbase/paths.go @@ -0,0 +1,24 @@ +package xbase + +import ( + "os" + "path/filepath" + + "github.com/ProtonMail/go-appdir" +) + +var appDir string + +var imgDir string +var cacheDir string + +func initPaths() { + appDir = appdir.New("xbvr").UserConfig() + + imgDir = filepath.Join(appDir, "imageproxy") + cacheDir = filepath.Join(appDir, "cache") + + _ = os.MkdirAll(appDir, os.ModePerm) + _ = os.MkdirAll(imgDir, os.ModePerm) + _ = os.MkdirAll(cacheDir, os.ModePerm) +} \ No newline at end of file diff --git a/xbase/scrape/badoink.go b/xbase/scrape/badoink.go new file mode 100644 index 000000000..a64eed231 --- /dev/null +++ b/xbase/scrape/badoink.go @@ -0,0 +1,172 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" +) + +func ScrapeBadoink() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("badoinkvr.com", "babevr.com", "vrcosplayx.com", "18vr.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("badoinkvr.com", "babevr.com", "vrcosplayx.com", "18vr.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + trailerCollector := siteCollector.Clone() + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + trailerCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "Badoink" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Site ID + if e.Request.URL.Host == "badoinkvr.com" { + sc.Site = "BadoinkVR" + } + + if e.Request.URL.Host == "babevr.com" { + sc.Site = "BabeVR" + } + + if e.Request.URL.Host == "vrcosplayx.com" { + sc.Site = "VRCosplayX" + } + + if e.Request.URL.Host == "18vr.com" { + sc.Site = "18VR" + } + + // Scene ID - get from URL + tmp := strings.Split(sc.HomepageURL, "-") + sc.SiteID = strings.Replace(tmp[len(tmp)-1], "/", "", -1) + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + + // Title + e.ForEach(`h1.video-title`, func(id int, e *colly.HTMLElement) { + sc.Title = strings.TrimSpace(e.Text) + }) + + // Filenames + base := e.Request.URL.Path + base = strings.Replace(base, "/", "", -1) + base = strings.Replace(base, sc.SiteID, "", -1) + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"180_180x180_3dh_LR.mp4") + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"gearvr-180_180x180_3dh_LR.mp4") + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"smartphone-180_180x180_3dh_LR.mp4") + + // Cover URLs + e.ForEach(`div#videoPreviewContainer picture source`, func(id int, e *colly.HTMLElement) { + if id == 0 { + sc.Covers = append(sc.Covers, strings.Split(e.Request.AbsoluteURL(e.Attr("srcset")), "?")[0]) + } + }) + + // Gallery + e.ForEach(`div#gallery div.gallery-item`, func(id int, e *colly.HTMLElement) { + sc.Gallery = append(sc.Gallery, e.Request.AbsoluteURL(e.Attr("data-big-image"))) + }) + + // Synopsis + e.ForEach(`p.video-description`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(e.Text) + }) + + // Tags + e.ForEach(`a.video-tag`, func(id int, e *colly.HTMLElement) { + sc.Tags = append(sc.Tags, strings.TrimSpace(e.Text)) + }) + + // Cast + e.ForEach(`a.video-actor-link`, func(id int, e *colly.HTMLElement) { + sc.Cast = append(sc.Cast, strings.TrimSpace(e.Text)) + }) + + // Date + e.ForEach(`p.video-upload-date`, func(id int, e *colly.HTMLElement) { + tmpDate, _ := goment.New(strings.Replace(e.Text, "Uploaded: ", "", -1), "MMMM DD, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + }) + + // Duration + e.ForEach(`p.video-duration`, func(id int, e *colly.HTMLElement) { + tmpDuration, err := strconv.Atoi(strings.Split(e.Attr("content"), ":")[1]) + if err == nil { + sc.Duration = tmpDuration + } + }) + + ctx := colly.NewContext() + ctx.Put("scene", sc) + + trailerCollector.Request("GET", e.Request.URL.String()+"trailer/", nil, ctx, nil) + }) + + trailerCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := e.Request.Ctx.GetAny("scene").(xbase.ExtScene) + + e.ForEach(`dl8-video source`, func(id int, e *colly.HTMLElement) { + if id == 0 { + origURL := e.Attr("src") + fpName := strings.Split(strings.Split(strings.Split(origURL, "_trailer")[0], "_3M")[0], "_3m")[0] + fragmentName := strings.Split(fpName, "/") + baseName := fragmentName[len(fragmentName)-1] + + filenames := []string{"samsung_180_180x180_3dh_LR", "oculus_180_180x180_3dh_LR", "mobile_180_180x180_3dh_LR", "5k_180_180x180_3dh_LR", "(Oculus)", "(S.GearVR)"} + + for i := range filenames { + filenames[i] = baseName + "_" + filenames[i] + ".mp4" + } + + sc.Filenames = filenames + } + }) + + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`div.pagination a`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`main[data-page=VideoList] a.video-card-image-container`, func(e *colly.HTMLElement) { + sceneURL := e.Request.AbsoluteURL(e.Attr("href")) + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://badoinkvr.com/vrpornvideos") + siteCollector.Visit("https://18vr.com/vrpornvideos") + siteCollector.Visit("https://vrcosplayx.com/cosplaypornvideos") + siteCollector.Visit("https://babevr.com/vrpornvideos") +} diff --git a/xbase/scrape/milfvr.go b/xbase/scrape/milfvr.go new file mode 100644 index 000000000..93821a8a6 --- /dev/null +++ b/xbase/scrape/milfvr.go @@ -0,0 +1,130 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" +) + +func ScrapeMilfVR() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("www.milfvr.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("www.milfvr.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "Wankz" + sc.Site = "MilfVR" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Scene ID - get from URL + tmp := strings.Split(sc.HomepageURL, "-") + sc.SiteID = tmp[len(tmp)-1] + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + + // Title + e.ForEach(`div.title h2`, func(id int, e *colly.HTMLElement) { + sc.Title = e.Text + }) + + // Filenames + base := e.Request.URL.Path + base = strings.Replace(base, "/", "", -1) + base = strings.Replace(base, sc.SiteID, "", -1) + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"180_180x180_3dh_LR.mp4") + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"gearvr-180_180x180_3dh_LR.mp4") + sc.Filenames = append(sc.Filenames, "milfvr-"+base+"smartphone-180_180x180_3dh_LR.mp4") + + // Cover URLs + e.ForEach(`div.swiper-slide img`, func(id int, e *colly.HTMLElement) { + if id == 0 { + sc.Covers = append(sc.Covers, e.Request.AbsoluteURL(e.Attr("src"))) + } + }) + + // Gallery + e.ForEach(`div.swiper-slide img.swiper-lazy`, func(id int, e *colly.HTMLElement) { + if id > 0 { + sc.Gallery = append(sc.Gallery, e.Request.AbsoluteURL(e.Attr("data-src"))) + } + }) + + // Synopsis + e.ForEach(`p.desc`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(e.Text) + }) + + // Tags + e.ForEach(`i.icon-tag`, func(id int, e *colly.HTMLElement) { + e.DOM.Parent().Find(`a`).Each(func(id int, e *goquery.Selection) { + sc.Tags = append(sc.Tags, e.Text()) + }) + }) + + // Cast + e.ForEach(`i.icon-head`, func(id int, e *colly.HTMLElement) { + e.DOM.Parent().Find(`a`).Each(func(id int, e *goquery.Selection) { + sc.Cast = append(sc.Cast, e.Text()) + }) + }) + + // Date + e.ForEach(`i.icon-bell`, func(id int, e *colly.HTMLElement) { + tmpDate, _ := goment.New(e.DOM.Parent().Text(), "DD MMMM, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + }) + + // Duration + e.ForEach(`i.icon-clock`, func(id int, e *colly.HTMLElement) { + tmpDuration, err := strconv.Atoi(strings.TrimSpace(strings.Replace(e.DOM.Parent().Text(), "min", "", -1))) + if err == nil { + sc.Duration = tmpDuration + } + }) + + // spew.Dump(sc) + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`nav.pager a`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`div.contentContainer article a`, func(e *colly.HTMLElement) { + sceneURL := e.Request.AbsoluteURL(e.Attr("href")) + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://www.milfvr.com/videos") +} diff --git a/xbase/scrape/navr.go b/xbase/scrape/navr.go new file mode 100644 index 000000000..4d65003f1 --- /dev/null +++ b/xbase/scrape/navr.go @@ -0,0 +1,153 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" + "github.com/robertkrimen/otto" +) + +func ScrapeNA() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("www.naughtyamerica.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("www.naughtyamerica.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "NaughtyAmerica" + sc.Site = "NaughtyAmerica VR" + sc.Title = "" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Scene ID - get from URL + tmp := strings.Split(sc.HomepageURL, "-") + sc.SiteID = tmp[len(tmp)-1] + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + + // Date + e.ForEach(`div.date-tags span.entry-date`, func(id int, e *colly.HTMLElement) { + tmpDate, _ := goment.New(e.Text, "MMM DD, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + }) + + // Duration + e.ForEach(`div.duration-ratings div.duration`, func(id int, e *colly.HTMLElement) { + tmpDuration, err := strconv.Atoi(strings.Replace(strings.Replace(e.Text, "Duration: ", "", -1), " min", "", -1)) + if err == nil { + sc.Duration = tmpDuration + } + }) + + // Filenames + e.ForEach(`a.play-trailer img.start-card`, func(id int, e *colly.HTMLElement) { + // images5.naughtycdn.com/cms/nacmscontent/v1/scenes/2cst/nikkijaclynmarco/scene/horizontal/1252x708c.jpg + base := strings.Split(strings.Replace(e.Attr("src"), "//", "", -1), "/") + baseName := base[5] + base[6] + + filenames := []string{"_180x180_3dh.mp4", "_smartphonevr60.mp4", "_smartphonevr30.mp4", "_vrdesktopsd.mp4", "_vrdesktophd.mp4", "_180_sbs.mp4", "_180x180_3dh.mp4"} + + for i := range filenames { + filenames[i] = baseName + filenames[i] + } + + sc.Filenames = filenames + }) + + // Cover URLs + e.ForEach(`a.play-trailer img.start-card`, func(id int, e *colly.HTMLElement) { + // images5.naughtycdn.com/cms/nacmscontent/v1/scenes/2cst/nikkijaclynmarco/scene/horizontal/1252x708c.jpg + base := strings.Split(strings.Replace(e.Attr("src"), "//", "", -1), "/") + + base[8] = "vertical" + base[9] = "400x605c.jpg" + sc.Covers = append(sc.Covers, "https://"+strings.Join(base, "/")) + + base[8] = "horizontal" + base[9] = "1252x708c.jpg" + sc.Covers = append(sc.Covers, "https://"+strings.Join(base, "/")) + }) + + // Gallery + e.ForEach(`div.contain-scene-images.desktop-only a`, func(id int, e *colly.HTMLElement) { + if id > 0 { + sc.Gallery = append(sc.Gallery, strings.Replace(e.Request.AbsoluteURL(e.Attr("href")), "dynamic", "", -1)) + } + }) + + // Synopsis + e.ForEach(`div.synopsis`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(strings.Replace(e.Text, "Synopsis:", "", -1)) + }) + + // Tags + e.ForEach(`div.categories a.cat-tag`, func(id int, e *colly.HTMLElement) { + sc.Tags = append(sc.Tags, e.Text) + }) + + // Cast (from tags) + // e.ForEach(`div.scene-info span.scene-title a`, func(id int, e *colly.HTMLElement) { + // sc.Cast = append(sc.Cast, e.Text) + // }) + + // Cast (extract from JavaScript) + e.ForEach(`script`, func(id int, e *colly.HTMLElement) { + if strings.Contains(e.Text, "femaleStar") { + vm := otto.New() + + script := e.Text + script = strings.Replace(script, "window.dataLayer", "dataLayer", -1) + script = strings.Replace(script, "dataLayer = dataLayer || []", "dataLayer = []", -1) + script = script + "\nout = []; dataLayer.forEach(function(v) { if (v.femaleStar) { out.push(v.femaleStar); } });" + vm.Run(script) + + out, _ := vm.Get("out") + outs, _ := out.ToString() + + sc.Cast = strings.Split(outs, ",") + } + }) + + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`ul[class=pagination] li a`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`div[class=site-list] div[class=scene-item] a[class=contain-img]`, func(e *colly.HTMLElement) { + sceneURL := strings.Split(e.Request.AbsoluteURL(e.Attr("href")), "?")[0] + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://www.naughtyamerica.com/vr-porn") +} diff --git a/xbase/scrape/scrape.go b/xbase/scrape/scrape.go new file mode 100644 index 000000000..bddc0fe89 --- /dev/null +++ b/xbase/scrape/scrape.go @@ -0,0 +1,31 @@ +package scrape + +import ( + "os" + "path/filepath" + + "github.com/ProtonMail/go-appdir" +) + +var appDir string +var cacheDir string + +var siteCacheDir string +var sceneCacheDir string + +var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36" + +func init() { + appDir = appdir.New("xbvr").UserConfig() + + cacheDir = filepath.Join(appDir, "cache") + + siteCacheDir = filepath.Join(cacheDir, "site_cache") + sceneCacheDir = filepath.Join(cacheDir, "scene_cache") + + _ = os.MkdirAll(appDir, os.ModePerm) + _ = os.MkdirAll(cacheDir, os.ModePerm) + + _ = os.MkdirAll(siteCacheDir, os.ModePerm) + _ = os.MkdirAll(sceneCacheDir, os.ModePerm) +} diff --git a/xbase/scrape/virtualtaboo.go b/xbase/scrape/virtualtaboo.go new file mode 100644 index 000000000..33af2c2db --- /dev/null +++ b/xbase/scrape/virtualtaboo.go @@ -0,0 +1,129 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" + "github.com/thoas/go-funk" +) + +func ScrapeVirtualTaboo() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("virtualtaboo.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("virtualtaboo.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "VirtualTaboo" + sc.Site = "VirtualTaboo" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Scene ID - get from URL + e.ForEach(`#player`, func(id int, e *colly.HTMLElement) { + sc.SiteID = strings.Split(e.Attr("data-poster-index"), ":")[0] + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + }) + + // Title + e.ForEach(`div.video-detail h1`, func(id int, e *colly.HTMLElement) { + sc.Title = strings.TrimSpace(e.Text) + }) + + // Filenames + base := strings.Split(e.Request.URL.Path, "/")[2] + sc.Filenames = append(sc.Filenames, base+"-files-smartphone.mp4") + sc.Filenames = append(sc.Filenames, base+"-files-gear.mp4") + sc.Filenames = append(sc.Filenames, base+"-files-psvr_180_sbs.mp4") + sc.Filenames = append(sc.Filenames, base+"-files-oculus.mp4") + sc.Filenames = append(sc.Filenames, base+"-files-oculus5k.mp4") + + // Cover URLs + e.ForEach(`meta[property="og:image"]`, func(id int, e *colly.HTMLElement) { + if id == 0 { + sc.Covers = append(sc.Covers, strings.Split(e.Request.AbsoluteURL(e.Attr("content")), "?")[0]) + } + }) + + // Gallery + e.ForEach(`div.gallery-item:not(.link) a.gallery-image`, func(id int, e *colly.HTMLElement) { + sc.Gallery = append(sc.Gallery, e.Request.AbsoluteURL(strings.Split(e.Attr("href"), "?")[0])) + }) + + // Synopsis + e.ForEach(`div.description span.full`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(e.Text) + }) + + // Tags + e.ForEach(`div.tag-list a`, func(id int, e *colly.HTMLElement) { + sc.Tags = append(sc.Tags, strings.TrimSpace(e.Text)) + }) + + // Cast + e.ForEach(`div.video-detail .info a`, func(id int, e *colly.HTMLElement) { + sc.Cast = append(sc.Cast, strings.TrimSpace(e.Text)) + }) + + // Date + e.ForEach(`div.right-info div.info`, func(id int, e *colly.HTMLElement) { + tmpData := funk.ReverseStrings(strings.Split(e.Text, "\n")) + + tmpDate, _ := goment.New(strings.TrimSpace(tmpData[1]), "MMMM DD, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + + sc.Duration, _ = strconv.Atoi(strings.TrimSpace(strings.Replace(tmpData[3], "min", "", -1))) + + }) + + // Duration + e.ForEach(`p.video-duration`, func(id int, e *colly.HTMLElement) { + tmpDuration, err := strconv.Atoi(strings.Split(e.Attr("content"), ":")[1]) + if err == nil { + sc.Duration = tmpDuration + } + }) + + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`ul.pagination a`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`div.video-title a`, func(e *colly.HTMLElement) { + sceneURL := e.Request.AbsoluteURL(e.Attr("href")) + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://virtualtaboo.com/videos") +} diff --git a/xbase/scrape/vrbangers.go b/xbase/scrape/vrbangers.go new file mode 100644 index 000000000..faaf25317 --- /dev/null +++ b/xbase/scrape/vrbangers.go @@ -0,0 +1,138 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" +) + +func ScrapeVRB() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("vrbangers.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("vrbangers.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "VRBangers" + sc.Site = "VRBangers" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Scene ID - get from URL + e.ForEach(`link[rel=shortlink]`, func(id int, e *colly.HTMLElement) { + tmp := strings.Split(e.Attr("href"), "?p=") + sc.SiteID = tmp[1] + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + }) + + // Title + e.ForEach(`div.video-info-title h1 span`, func(id int, e *colly.HTMLElement) { + if id == 0 { + sc.Title = e.Text + } + }) + + // Date + e.ForEach(`p[itemprop=datePublished]`, func(id int, e *colly.HTMLElement) { + tmpDate, _ := goment.New(e.Text, "DD MMMM, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + }) + + // Duration + e.ForEach(`p.minutes`, func(id int, e *colly.HTMLElement) { + minutes := strings.Split(e.Text, ":")[0] + tmpDuration, err := strconv.Atoi(strings.TrimSpace(minutes)) + if err == nil { + sc.Duration = tmpDuration + } + }) + + // Filenames + e.ForEach(`dl8-video source`, func(id int, e *colly.HTMLElement) { + if id == 0 { + basePath := strings.Split(strings.Replace(e.Attr("src"), "//", "", -1), "/") + baseName := strings.Replace(basePath[1], "vrb_", "", -1) + + filenames := []string{"6K_180x180_3dh", "5K_180x180_3dh", "4K_180x180_3dh", "HD_180x180_3dh", "HQ_180x180_3dh", "PSVRHQ_180x180_3dh", "UHD_180x180_3dh", "PSVRHQ_180_sbs", "PSVR_mono", "HQ_mono360", "HD_mono360", "PSVRHQ_ou", "UHD_3dv", "HD_3dv", "HQ_3dv"} + + for i := range filenames { + filenames[i] = "VRBANGERS_" + baseName + "_" + filenames[i] + ".mp4" + } + + sc.Filenames = filenames + } + }) + + // Cover URLs + e.ForEach(`dl8-video`, func(id int, e *colly.HTMLElement) { + sc.Covers = append(sc.Covers, e.Request.AbsoluteURL(e.Attr("poster"))) + }) + + e.ForEach(`img.girls_image`, func(id int, e *colly.HTMLElement) { + sc.Covers = append(sc.Covers, e.Request.AbsoluteURL(e.Attr("src"))) + }) + + // Gallery + e.ForEach(`div#single-video-gallery-free a,div.old-gallery a`, func(id int, e *colly.HTMLElement) { + sc.Gallery = append(sc.Gallery, e.Request.AbsoluteURL(e.Attr("href"))) + }) + + // Synopsis + e.ForEach(`div.mainContent`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(e.Text) + }) + + // Tags + e.ForEach(`div.video-tags a`, func(id int, e *colly.HTMLElement) { + sc.Tags = append(sc.Tags, e.Text) + }) + + // Cast + e.ForEach(`div.video-info-title h1 span a`, func(id int, e *colly.HTMLElement) { + sc.Cast = append(sc.Cast, strings.TrimSpace(strings.Replace(e.Text, ",", "", -1))) + }) + + // spew.Dump(sc) + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`div.wp-pagenavi a.page`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`div.video-page-block a.model-foto`, func(e *colly.HTMLElement) { + sceneURL := e.Request.AbsoluteURL(e.Attr("href")) + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://vrbangers.com/videos/") +} diff --git a/xbase/scrape/wankzvr.go b/xbase/scrape/wankzvr.go new file mode 100644 index 000000000..642b8c1d8 --- /dev/null +++ b/xbase/scrape/wankzvr.go @@ -0,0 +1,126 @@ +package scrape + +import ( + "log" + "strconv" + "strings" + + "github.com/cld9x/xbvr/xbase" + "github.com/gocolly/colly" + "github.com/mozillazg/go-slugify" + "github.com/nleeper/goment" +) + +func ScrapeWankz() { + siteCollector := colly.NewCollector( + colly.AllowedDomains("www.wankzvr.com"), + colly.CacheDir(siteCacheDir), + colly.UserAgent(userAgent), + ) + + sceneCollector := colly.NewCollector( + colly.AllowedDomains("www.wankzvr.com"), + colly.CacheDir(sceneCacheDir), + colly.UserAgent(userAgent), + ) + + siteCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnRequest(func(r *colly.Request) { + log.Println("visiting", r.URL.String()) + }) + + sceneCollector.OnHTML(`html`, func(e *colly.HTMLElement) { + sc := xbase.ExtScene{} + sc.SceneType = "VR" + sc.Studio = "Wankz" + sc.Site = "WankzVR" + sc.HomepageURL = strings.Split(e.Request.URL.String(), "?")[0] + + // Scene ID - get from URL + tmp := strings.Split(sc.HomepageURL, "-") + sc.SiteID = tmp[len(tmp)-1] + sc.SceneID = slugify.Slugify(sc.Site) + "-" + sc.SiteID + + // Title + e.ForEach(`header h2`, func(id int, e *colly.HTMLElement) { + sc.Title = e.Text + }) + + // Date + e.ForEach(`div.date`, func(id int, e *colly.HTMLElement) { + tmpDate, _ := goment.New(e.Text, "DD MMMM, YYYY") + sc.Released = tmpDate.Format("YYYY-MM-DD") + }) + + // Duration + e.ForEach(`div.duration`, func(id int, e *colly.HTMLElement) { + if id == 1 { + tmpDuration, err := strconv.Atoi(strings.TrimSpace(strings.Replace(e.Text, "minutes", "", -1))) + if err == nil { + sc.Duration = tmpDuration + } + } + }) + + // Filenames + base := e.Request.URL.Path + base = strings.Replace(base, "/", "", -1) + base = strings.Replace(base, sc.SiteID, "", -1) + sc.Filenames = append(sc.Filenames, "wankzvr-"+base+"180_180x180_3dh_LR.mp4") + + // Cover URLs + e.ForEach(`div.swiper-slide img`, func(id int, e *colly.HTMLElement) { + if id == 0 { + sc.Covers = append(sc.Covers, e.Request.AbsoluteURL(e.Attr("src"))) + } + }) + + // Gallery + e.ForEach(`div.swiper-slide img.lazyload`, func(id int, e *colly.HTMLElement) { + if id > 0 { + sc.Gallery = append(sc.Gallery, e.Request.AbsoluteURL(e.Attr("data-src"))) + } + }) + + // Synopsis + e.ForEach(`p.description`, func(id int, e *colly.HTMLElement) { + sc.Synopsis = strings.TrimSpace(strings.Replace(e.Text, " Read more", "", -1)) + }) + + // Tags + e.ForEach(`div.tags a`, func(id int, e *colly.HTMLElement) { + sc.Tags = append(sc.Tags, e.Text) + }) + + // Cast + e.ForEach(`header h4 a`, func(id int, e *colly.HTMLElement) { + sc.Cast = append(sc.Cast, strings.TrimSpace(e.Text)) + }) + + // spew.Dump(sc) + ns := xbase.Scene{} + ns.CreateUpdateFromExternal(sc) + }) + + siteCollector.OnHTML(`nav.pager a`, func(e *colly.HTMLElement) { + pageURL := e.Request.AbsoluteURL(e.Attr("href")) + siteCollector.Visit(pageURL) + }) + + siteCollector.OnHTML(`div.contentContainer article a`, func(e *colly.HTMLElement) { + sceneURL := e.Request.AbsoluteURL(e.Attr("href")) + sceneURL = strings.Replace(sceneURL, "/preview", "", -1) + + // If scene exist in database, there's no need to scrape + ts := xbase.Scene{} + ts.GetIfExistURL(sceneURL) + if ts.SceneURL != sceneURL { + sceneCollector.Visit(sceneURL) + } + }) + + siteCollector.Visit("https://www.wankzvr.com/videos") +} diff --git a/xbase/server.go b/xbase/server.go new file mode 100644 index 000000000..850232c9f --- /dev/null +++ b/xbase/server.go @@ -0,0 +1,82 @@ +package xbase + +import ( + "fmt" + "net/http" + "os" + "path/filepath" + + "github.com/cld9x/xbvr/xbase/assets" + "github.com/emicklei/go-restful" + wwwlog "github.com/gowww/log" + "github.com/gregjones/httpcache/diskcache" + "github.com/peterbourgon/diskv" + "github.com/rs/cors" + "willnorris.com/go/imageproxy" +) + +var ( + DEBUG = os.Getenv("DEBUG") +) + +func StartServer() { + CheckVolumes() + + // API endpoints + ws := new(restful.WebService) + ws.Route(ws.GET("/").To(redirectUI)) + + restful.Add(ws) + restful.Add(ExtResource{}.WebService()) + restful.Add(SceneResource{}.WebService()) + restful.Add(TaskResource{}.WebService()) + restful.Add(DMSResource{}.WebService()) + + // Static files + if DEBUG == "" { + http.Handle("/static/", http.FileServer(assets.HTTP)) + } else { + http.Handle("/static/", http.FileServer(http.Dir("ui/public"))) + } + + // SPA + http.HandleFunc("/ui/", func(resp http.ResponseWriter, req *http.Request) { + b, _ := assets.ReadFile("index.html") + resp.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(resp, string(b)) + }) + + // Imageproxy + p := imageproxy.NewProxy(nil, diskCache(filepath.Join(appDir, "imageproxy"))) + http.Handle("/img/", http.StripPrefix("/img", p)) + + // CORS + handler := cors.Default().Handler(http.DefaultServeMux) + + log.Infof("XBVR starting...") + + go StartDMS() + + log.Infof("Web UI available at http://127.0.0.1:9999/") + log.Infof("Database file stored at %s", appDir) + + if DEBUG == "" { + log.Fatal(http.ListenAndServe(":9999", handler)) + } else { + log.Infof("Running in DEBUG mode") + log.Fatal(http.ListenAndServe(":9999", wwwlog.Handle(handler, &wwwlog.Options{Color: true}))) + } +} + +func redirectUI(req *restful.Request, resp *restful.Response) { + resp.AddHeader("Location", "/ui/") + resp.WriteHeader(http.StatusFound) +} + +func diskCache(path string) *diskcache.Cache { + d := diskv.New(diskv.Options{ + BasePath: path, + Transform: func(s string) []string { return []string{s[0:2], s[2:4]} }, + }) + return diskcache.NewWithDiskv(d) +} diff --git a/xbase/task_content.go b/xbase/task_content.go new file mode 100644 index 000000000..19923c606 --- /dev/null +++ b/xbase/task_content.go @@ -0,0 +1,126 @@ +package xbase + +import ( + "path" + + "github.com/go-test/deep" +) + +func RenameTags() { + db, _ := GetDB() + defer db.Close() + + var scenes []Scene + db.Find(&scenes) + + for i := range scenes { + currentTags := make([]Tag, 0) + db.Model(&scenes[i]).Related(¤tTags, "Tags") + + newTags := make([]Tag, 0) + for j := range currentTags { + nt := Tag{} + if convertTag(currentTags[j].Name) != "" { + db.Where(&Tag{Name: convertTag(currentTags[j].Name)}).FirstOrCreate(&nt) + newTags = append(newTags, nt) + } + } + + diffs := deep.Equal(currentTags, newTags) + if len(diffs) > 0 { + for j := range currentTags { + db.Model(&scenes[i]).Association("Tags").Delete(¤tTags[j]) + } + + for j := range newTags { + db.Model(&scenes[i]).Association("Tags").Append(&newTags[j]) + } + } + + } +} + +func CountTags() { + db, _ := GetDB() + defer db.Close() + + var tags []Tag + db.Model(&Tag{}).Find(&tags) + + for i := range tags { + var scenes []Scene + db.Model(tags[i]).Related(&scenes, "Scenes") + + tags[i].Count = len(scenes) + tags[i].Save() + } + + db.Where("count = ?", 0).Delete(&Tag{}) +} + +func UpdateScenes() { + db, _ := GetDB() + defer db.Close() + + var files []File + var scenes []Scene + var changed = false + + db.Model(&File{}).Find(&files) + + for i := range files { + fn := files[i].Filename + + var pfn PossibleFilename + db.Where("name LIKE ?", path.Base(fn)).First(&pfn) + db.Model(&pfn).Preload("Cast").Preload("Tags").Related(&scenes, "Scenes") + + if len(scenes) == 1 { + files[i].SceneID = scenes[0].ID + files[i].Save() + } + } + + db.Model(&Scene{}).Find(&scenes) + + for i := range scenes { + // Check if file with scene association exists + files, err := scenes[i].GetFiles() + if err != nil { + return + } + + changed = false + + if len(files) > 0 { + if !scenes[i].IsAvailable { + scenes[i].IsAvailable = true + changed = true + } + for j := range files { + if files[j].Exists() { + if !scenes[i].IsAccessible { + scenes[i].IsAccessible = true + changed = true + } + } else { + if scenes[i].IsAccessible { + scenes[i].IsAccessible = false + changed = true + } + } + } + } else { + if scenes[i].IsAvailable { + scenes[i].IsAvailable = false + changed = true + } + } + + if changed { + scenes[i].Save() + } + + } + +} diff --git a/xbase/task_volume.go b/xbase/task_volume.go new file mode 100644 index 000000000..32408be04 --- /dev/null +++ b/xbase/task_volume.go @@ -0,0 +1,15 @@ +package xbase + +func RescanVolumes() { + db, _ := GetDB() + defer db.Close() + + var vol []Volume + db.Find(&vol) + + for i := range vol { + vol[i].Rescan() + } + + UpdateScenes() +}