mirror of
https://github.com/Ernous/TorrServerJellyfin.git
synced 2025-12-19 13:36:09 +05:00
Merge branch 'master' into new-torrent
This commit is contained in:
17
README.md
17
README.md
@@ -180,15 +180,20 @@ local:127.0.0.1\
|
|||||||
127.0.0.1\
|
127.0.0.1\
|
||||||
\# at the beginning of the line, comment
|
\# at the beginning of the line, comment
|
||||||
|
|
||||||
|
#
|
||||||
|
### MSX Install:
|
||||||
|
Open msx and goto: Settings -> Start Parameter -> Setup \
|
||||||
|
Enter current ip address and port of server e.g. _127.0.0.1:8090_
|
||||||
|
|
||||||
#
|
#
|
||||||
### Donate:
|
### Donate:
|
||||||
[PayPal](https://www.paypal.me/yourok)
|
[PayPal](https://www.paypal.me/yourok) \
|
||||||
|
[QIWI](qiwi.com/n/YOUROK85) \
|
||||||
[YooMoney](https://yoomoney.ru/to/410013733697114/200)
|
[YooMoney](https://yoomoney.ru/to/410013733697114/200)
|
||||||
YooMoney card: 4048 4150 1812 8179
|
|
||||||
|
|
||||||
SberBank card: 4276 4000 6707 2919
|
SberBank card: **4276 4000 6707 2919**
|
||||||
|
|
||||||
|
YooMoney card: **4048 4150 1812 8179**
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -200,6 +205,8 @@ SberBank card: 4276 4000 6707 2919
|
|||||||
|
|
||||||
###### **dancheskus** [github.com/dancheskus](https://github.com/dancheskus)
|
###### **dancheskus** [github.com/dancheskus](https://github.com/dancheskus)
|
||||||
|
|
||||||
|
###### **kolsys** [github.com/kolsys](https://github.com/kolsys)
|
||||||
|
|
||||||
###### **Tw1cker Руслан Пахнев** [github.com/Nemiroff](https://github.com/Nemiroff)
|
###### **Tw1cker Руслан Пахнев** [github.com/Nemiroff](https://github.com/Nemiroff)
|
||||||
|
|
||||||
###### **SpAwN_LMG**
|
###### **SpAwN_LMG** [github.com/spawnlmg](https://github.com/spawnlmg)
|
||||||
|
|||||||
@@ -83,11 +83,6 @@ done
|
|||||||
### Android build section
|
### Android build section
|
||||||
#####
|
#####
|
||||||
|
|
||||||
if [ -z "$NDK_TOOLCHAIN" ]; then
|
|
||||||
echo "NDK_TOOLCHAIN is not defined. Android builds was skipped"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
declare -a COMPILERS=(
|
declare -a COMPILERS=(
|
||||||
"arm7:armv7a-linux-androideabi21-clang"
|
"arm7:armv7a-linux-androideabi21-clang"
|
||||||
"arm64:aarch64-linux-android21-clang"
|
"arm64:aarch64-linux-android21-clang"
|
||||||
|
|||||||
@@ -49,20 +49,6 @@ func Start() {
|
|||||||
NoProbe: true,
|
NoProbe: true,
|
||||||
StallEventSubscribe: true,
|
StallEventSubscribe: true,
|
||||||
Icons: []dms.Icon{
|
Icons: []dms.Icon{
|
||||||
// dms.Icon{
|
|
||||||
// Width: 48,
|
|
||||||
// Height: 48,
|
|
||||||
// Depth: 24,
|
|
||||||
// Mimetype: "image/jpeg",
|
|
||||||
// ReadSeeker: bytes.NewReader(template.Dlnaicon48jpg),
|
|
||||||
// },
|
|
||||||
// dms.Icon{
|
|
||||||
// Width: 120,
|
|
||||||
// Height: 120,
|
|
||||||
// Depth: 24,
|
|
||||||
// Mimetype: "image/jpeg",
|
|
||||||
// ReadSeeker: bytes.NewReader(template.Dlnaicon120jpg),
|
|
||||||
// },
|
|
||||||
dms.Icon{
|
dms.Icon{
|
||||||
Width: 48,
|
Width: 48,
|
||||||
Height: 48,
|
Height: 48,
|
||||||
|
|||||||
@@ -243,8 +243,8 @@ func Preload(torr *Torrent, index int) {
|
|||||||
cache := float32(sets.BTsets.CacheSize)
|
cache := float32(sets.BTsets.CacheSize)
|
||||||
preload := float32(sets.BTsets.PreloadCache)
|
preload := float32(sets.BTsets.PreloadCache)
|
||||||
size := int64((cache / 100.0) * preload)
|
size := int64((cache / 100.0) * preload)
|
||||||
if size < 32*1024*1024 {
|
if size <= 0 {
|
||||||
size = 32 * 1024 * 1024
|
return
|
||||||
}
|
}
|
||||||
if size > sets.BTsets.CacheSize {
|
if size > sets.BTsets.CacheSize {
|
||||||
size = sets.BTsets.CacheSize
|
size = sets.BTsets.CacheSize
|
||||||
|
|||||||
134
server/web/cors/config.go
Normal file
134
server/web/cors/config.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package cors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cors struct {
|
||||||
|
allowAllOrigins bool
|
||||||
|
allowCredentials bool
|
||||||
|
allowOriginFunc func(string) bool
|
||||||
|
allowOrigins []string
|
||||||
|
exposeHeaders []string
|
||||||
|
normalHeaders http.Header
|
||||||
|
preflightHeaders http.Header
|
||||||
|
wildcardOrigins [][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultSchemas = []string{
|
||||||
|
"http://",
|
||||||
|
"https://",
|
||||||
|
}
|
||||||
|
ExtensionSchemas = []string{
|
||||||
|
"chrome-extension://",
|
||||||
|
"safari-extension://",
|
||||||
|
"moz-extension://",
|
||||||
|
"ms-browser-extension://",
|
||||||
|
}
|
||||||
|
FileSchemas = []string{
|
||||||
|
"file://",
|
||||||
|
}
|
||||||
|
WebSocketSchemas = []string{
|
||||||
|
"ws://",
|
||||||
|
"wss://",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCors(config Config) *cors {
|
||||||
|
if err := config.Validate(); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cors{
|
||||||
|
allowOriginFunc: config.AllowOriginFunc,
|
||||||
|
allowAllOrigins: config.AllowAllOrigins,
|
||||||
|
allowCredentials: config.AllowCredentials,
|
||||||
|
allowOrigins: normalize(config.AllowOrigins),
|
||||||
|
normalHeaders: generateNormalHeaders(config),
|
||||||
|
preflightHeaders: generatePreflightHeaders(config),
|
||||||
|
wildcardOrigins: config.parseWildcardRules(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cors *cors) applyCors(c *gin.Context) {
|
||||||
|
origin := c.Request.Header.Get("Origin")
|
||||||
|
if len(origin) == 0 {
|
||||||
|
// request is not a CORS request
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host := c.Request.Host
|
||||||
|
|
||||||
|
if origin == "http://"+host || origin == "https://"+host {
|
||||||
|
// request is not a CORS request but have origin header.
|
||||||
|
// for example, use fetch api
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cors.validateOrigin(origin) {
|
||||||
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Request.Method == "OPTIONS" {
|
||||||
|
cors.handlePreflight(c)
|
||||||
|
defer c.AbortWithStatus(http.StatusNoContent) // Using 204 is better than 200 when the request status is OPTIONS
|
||||||
|
} else {
|
||||||
|
cors.handleNormal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cors.allowAllOrigins {
|
||||||
|
c.Header("Access-Control-Allow-Origin", origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cors *cors) validateWildcardOrigin(origin string) bool {
|
||||||
|
for _, w := range cors.wildcardOrigins {
|
||||||
|
if w[0] == "*" && strings.HasSuffix(origin, w[1]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if w[1] == "*" && strings.HasPrefix(origin, w[0]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(origin, w[0]) && strings.HasSuffix(origin, w[1]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cors *cors) validateOrigin(origin string) bool {
|
||||||
|
if cors.allowAllOrigins {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, value := range cors.allowOrigins {
|
||||||
|
if value == origin {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(cors.wildcardOrigins) > 0 && cors.validateWildcardOrigin(origin) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if cors.allowOriginFunc != nil {
|
||||||
|
return cors.allowOriginFunc(origin)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cors *cors) handlePreflight(c *gin.Context) {
|
||||||
|
header := c.Writer.Header()
|
||||||
|
for key, value := range cors.preflightHeaders {
|
||||||
|
header[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cors *cors) handleNormal(c *gin.Context) {
|
||||||
|
header := c.Writer.Header()
|
||||||
|
for key, value := range cors.normalHeaders {
|
||||||
|
header[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
174
server/web/cors/cors.go
Normal file
174
server/web/cors/cors.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package cors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents all available options for the middleware.
|
||||||
|
type Config struct {
|
||||||
|
AllowAllOrigins bool
|
||||||
|
|
||||||
|
// AllowOrigins is a list of origins a cross-domain request can be executed from.
|
||||||
|
// If the special "*" value is present in the list, all origins will be allowed.
|
||||||
|
// Default value is []
|
||||||
|
AllowOrigins []string
|
||||||
|
|
||||||
|
// AllowOriginFunc is a custom function to validate the origin. It take the origin
|
||||||
|
// as argument and returns true if allowed or false otherwise. If this option is
|
||||||
|
// set, the content of AllowOrigins is ignored.
|
||||||
|
AllowOriginFunc func(origin string) bool
|
||||||
|
|
||||||
|
// AllowMethods is a list of methods the client is allowed to use with
|
||||||
|
// cross-domain requests. Default value is simple methods (GET and POST)
|
||||||
|
AllowMethods []string
|
||||||
|
|
||||||
|
// AllowHeaders is list of non simple headers the client is allowed to use with
|
||||||
|
// cross-domain requests.
|
||||||
|
AllowHeaders []string
|
||||||
|
|
||||||
|
// AllowCredentials indicates whether the request can include user credentials like
|
||||||
|
// cookies, HTTP authentication or client side SSL certificates.
|
||||||
|
AllowCredentials bool
|
||||||
|
|
||||||
|
// AllowPrivateNetwork
|
||||||
|
AllowPrivateNetwork bool
|
||||||
|
|
||||||
|
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
|
||||||
|
// API specification
|
||||||
|
ExposeHeaders []string
|
||||||
|
|
||||||
|
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||||
|
// can be cached
|
||||||
|
MaxAge time.Duration
|
||||||
|
|
||||||
|
// Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com
|
||||||
|
AllowWildcard bool
|
||||||
|
|
||||||
|
// Allows usage of popular browser extensions schemas
|
||||||
|
AllowBrowserExtensions bool
|
||||||
|
|
||||||
|
// Allows usage of WebSocket protocol
|
||||||
|
AllowWebSockets bool
|
||||||
|
|
||||||
|
// Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed
|
||||||
|
AllowFiles bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAllowMethods is allowed to add custom methods
|
||||||
|
func (c *Config) AddAllowMethods(methods ...string) {
|
||||||
|
c.AllowMethods = append(c.AllowMethods, methods...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAllowHeaders is allowed to add custom headers
|
||||||
|
func (c *Config) AddAllowHeaders(headers ...string) {
|
||||||
|
c.AllowHeaders = append(c.AllowHeaders, headers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddExposeHeaders is allowed to add custom expose headers
|
||||||
|
func (c *Config) AddExposeHeaders(headers ...string) {
|
||||||
|
c.ExposeHeaders = append(c.ExposeHeaders, headers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) getAllowedSchemas() []string {
|
||||||
|
allowedSchemas := DefaultSchemas
|
||||||
|
if c.AllowBrowserExtensions {
|
||||||
|
allowedSchemas = append(allowedSchemas, ExtensionSchemas...)
|
||||||
|
}
|
||||||
|
if c.AllowWebSockets {
|
||||||
|
allowedSchemas = append(allowedSchemas, WebSocketSchemas...)
|
||||||
|
}
|
||||||
|
if c.AllowFiles {
|
||||||
|
allowedSchemas = append(allowedSchemas, FileSchemas...)
|
||||||
|
}
|
||||||
|
return allowedSchemas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) validateAllowedSchemas(origin string) bool {
|
||||||
|
allowedSchemas := c.getAllowedSchemas()
|
||||||
|
for _, schema := range allowedSchemas {
|
||||||
|
if strings.HasPrefix(origin, schema) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate is check configuration of user defined.
|
||||||
|
func (c *Config) Validate() error {
|
||||||
|
if c.AllowAllOrigins && (c.AllowOriginFunc != nil || len(c.AllowOrigins) > 0) {
|
||||||
|
return errors.New("conflict settings: all origins are allowed. AllowOriginFunc or AllowOrigins is not needed")
|
||||||
|
}
|
||||||
|
if !c.AllowAllOrigins && c.AllowOriginFunc == nil && len(c.AllowOrigins) == 0 {
|
||||||
|
return errors.New("conflict settings: all origins disabled")
|
||||||
|
}
|
||||||
|
for _, origin := range c.AllowOrigins {
|
||||||
|
if origin == "*" {
|
||||||
|
c.AllowAllOrigins = true
|
||||||
|
return nil
|
||||||
|
} else if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) {
|
||||||
|
return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) parseWildcardRules() [][]string {
|
||||||
|
var wRules [][]string
|
||||||
|
|
||||||
|
if !c.AllowWildcard {
|
||||||
|
return wRules
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range c.AllowOrigins {
|
||||||
|
if !strings.Contains(o, "*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := strings.Count(o, "*"); c > 1 {
|
||||||
|
panic(errors.New("only one * is allowed").Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
i := strings.Index(o, "*")
|
||||||
|
if i == 0 {
|
||||||
|
wRules = append(wRules, []string{"*", o[1:]})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i == (len(o) - 1) {
|
||||||
|
wRules = append(wRules, []string{o[:i-1], "*"})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
wRules = append(wRules, []string{o[:i], o[i+1:]})
|
||||||
|
}
|
||||||
|
|
||||||
|
return wRules
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig returns a generic default configuration mapped to localhost.
|
||||||
|
func DefaultConfig() Config {
|
||||||
|
return Config{
|
||||||
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||||
|
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
|
||||||
|
AllowCredentials: false,
|
||||||
|
MaxAge: 12 * time.Hour,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default returns the location middleware with default configuration.
|
||||||
|
func Default() gin.HandlerFunc {
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.AllowAllOrigins = true
|
||||||
|
return New(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns the location middleware with user-defined custom configuration.
|
||||||
|
func New(config Config) gin.HandlerFunc {
|
||||||
|
cors := newCors(config)
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
cors.applyCors(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
88
server/web/cors/utils.go
Normal file
88
server/web/cors/utils.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package cors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type converter func(string) string
|
||||||
|
|
||||||
|
func generateNormalHeaders(c Config) http.Header {
|
||||||
|
headers := make(http.Header)
|
||||||
|
if c.AllowCredentials {
|
||||||
|
headers.Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
}
|
||||||
|
if c.AllowPrivateNetwork {
|
||||||
|
headers.Set("Access-Control-Allow-Private-Network", "true")
|
||||||
|
}
|
||||||
|
if len(c.ExposeHeaders) > 0 {
|
||||||
|
exposeHeaders := convert(normalize(c.ExposeHeaders), http.CanonicalHeaderKey)
|
||||||
|
headers.Set("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ","))
|
||||||
|
}
|
||||||
|
if c.AllowAllOrigins {
|
||||||
|
headers.Set("Access-Control-Allow-Origin", "*")
|
||||||
|
} else {
|
||||||
|
headers.Set("Vary", "Origin")
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePreflightHeaders(c Config) http.Header {
|
||||||
|
headers := make(http.Header)
|
||||||
|
if c.AllowCredentials {
|
||||||
|
headers.Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
}
|
||||||
|
if len(c.AllowMethods) > 0 {
|
||||||
|
allowMethods := convert(normalize(c.AllowMethods), strings.ToUpper)
|
||||||
|
value := strings.Join(allowMethods, ",")
|
||||||
|
headers.Set("Access-Control-Allow-Methods", value)
|
||||||
|
}
|
||||||
|
if len(c.AllowHeaders) > 0 {
|
||||||
|
allowHeaders := convert(normalize(c.AllowHeaders), http.CanonicalHeaderKey)
|
||||||
|
value := strings.Join(allowHeaders, ",")
|
||||||
|
headers.Set("Access-Control-Allow-Headers", value)
|
||||||
|
}
|
||||||
|
if c.MaxAge > time.Duration(0) {
|
||||||
|
value := strconv.FormatInt(int64(c.MaxAge/time.Second), 10)
|
||||||
|
headers.Set("Access-Control-Max-Age", value)
|
||||||
|
}
|
||||||
|
if c.AllowAllOrigins {
|
||||||
|
headers.Set("Access-Control-Allow-Origin", "*")
|
||||||
|
} else {
|
||||||
|
// Always set Vary headers
|
||||||
|
// see https://github.com/rs/cors/issues/10,
|
||||||
|
// https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001
|
||||||
|
|
||||||
|
headers.Add("Vary", "Origin")
|
||||||
|
headers.Add("Vary", "Access-Control-Request-Method")
|
||||||
|
headers.Add("Vary", "Access-Control-Request-Headers")
|
||||||
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize(values []string) []string {
|
||||||
|
if values == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
distinctMap := make(map[string]bool, len(values))
|
||||||
|
normalized := make([]string, 0, len(values))
|
||||||
|
for _, value := range values {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
value = strings.ToLower(value)
|
||||||
|
if _, seen := distinctMap[value]; !seen {
|
||||||
|
normalized = append(normalized, value)
|
||||||
|
distinctMap[value] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
func convert(s []string, c converter) []string {
|
||||||
|
var out []string
|
||||||
|
for _, i := range s {
|
||||||
|
out = append(out, c(i))
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
@@ -2,11 +2,11 @@ package msx
|
|||||||
|
|
||||||
import _ "embed"
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed pages/html5x.html
|
||||||
|
var Msxhtml5xhtml []byte
|
||||||
|
|
||||||
//go:embed pages/tizen.html
|
//go:embed pages/tizen.html
|
||||||
var Msxtizenhtml []byte
|
var Msxtizenhtml []byte
|
||||||
|
|
||||||
//go:embed pages/tizen.js
|
|
||||||
var Msxtizenjs []byte
|
|
||||||
|
|
||||||
//go:embed pages/tvx-plugin.min.js
|
//go:embed pages/tvx-plugin.min.js
|
||||||
var Msxtvxpluginminjs []byte
|
var Msxtvxpluginminjs []byte
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ type msxPage struct {
|
|||||||
Items []gin.H `json:"items,omitempty"`
|
Items []gin.H `json:"items,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ICON_OPTIONS = "tune"
|
||||||
|
|
||||||
func msxStart(c *gin.Context) {
|
func msxStart(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"name": "TorrServer",
|
"name": "TorrServer",
|
||||||
@@ -173,6 +175,7 @@ func msxPlaylist(c *gin.Context) {
|
|||||||
status := tor.Status()
|
status := tor.Status()
|
||||||
viewed := sets.ListViewed(hash)
|
viewed := sets.ListViewed(hash)
|
||||||
var list []msxItem
|
var list []msxItem
|
||||||
|
contentAction := ""
|
||||||
|
|
||||||
for _, f := range status.FileStats {
|
for _, f := range status.FileStats {
|
||||||
mime := utils.GetMimeType(f.Path)
|
mime := utils.GetMimeType(f.Path)
|
||||||
@@ -196,24 +199,16 @@ func msxPlaylist(c *gin.Context) {
|
|||||||
"uri": uri,
|
"uri": uri,
|
||||||
"type": mime,
|
"type": mime,
|
||||||
}
|
}
|
||||||
} else if platform == "lg" {
|
} else if platform == "tizen" {
|
||||||
// TODO - custom player needed
|
contentAction = "content:request:interaction:init@" + host + "/msx/tizen.html"
|
||||||
//item.Action = "system:lg:launch:com.webos.app.mediadiscovery"
|
} else if platform == "netcast" {
|
||||||
//item.Data = gin.H{
|
contentAction = "system:netcast:menu"
|
||||||
// "properties": gin.H{
|
|
||||||
// "videoList": gin.H{
|
|
||||||
// "result": [1]gin.H{
|
|
||||||
// gin.H{
|
|
||||||
// "url": uri,
|
|
||||||
// "thumbnail": tor.Poster,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
//}
|
|
||||||
} else if platform == "ios" || platform == "mac" {
|
} else if platform == "ios" || platform == "mac" {
|
||||||
// TODO - for iOS and Mac the application must be defined in scheme but we don't know what user has installed
|
// TODO - for iOS and Mac the application must be defined in scheme but we don't know what user has installed
|
||||||
item.Action = "system:tvx:launch:vlc://" + uri
|
item.Action = "system:tvx:launch:vlc://" + uri
|
||||||
|
} else {
|
||||||
|
item.Action = "video:plugin:" + host + "/msx/html5x.html?url= " + url.QueryEscape(uri)
|
||||||
|
contentAction = "panel:request:player:options"
|
||||||
}
|
}
|
||||||
|
|
||||||
if isViewed(viewed, f.Id) {
|
if isViewed(viewed, f.Id) {
|
||||||
@@ -247,23 +242,13 @@ func msxPlaylist(c *gin.Context) {
|
|||||||
Items: list,
|
Items: list,
|
||||||
}
|
}
|
||||||
|
|
||||||
if platform == "tizen" {
|
if contentAction != "" {
|
||||||
res.Template.Properties = gin.H{
|
res.Template.Properties = gin.H{
|
||||||
"button:content:icon": "tune",
|
"button:content:icon": ICON_OPTIONS,
|
||||||
"button:content:action": "content:request:interaction:init@" + host + "/msx/tizen.html",
|
"button:content:action": contentAction,
|
||||||
}
|
|
||||||
} else if platform == "netcast" {
|
|
||||||
res.Template.Properties = gin.H{
|
|
||||||
"button:content:icon": "tune",
|
|
||||||
"button:content:action": "system:netcast:menu",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If only one item start to play immediately but it not works
|
|
||||||
// if (len(list) == 1) {
|
|
||||||
// res.Action = "execute:" + list[0].Action
|
|
||||||
// }
|
|
||||||
|
|
||||||
c.JSON(200, res)
|
c.JSON(200, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
763
server/web/msx/pages/html5x.html
Normal file
763
server/web/msx/pages/html5x.html
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>HTML5 Extended Video Plugin</title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="author" content="Benjamin Zachey" />
|
||||||
|
<meta name="contact" content="admin@benzac.de" />
|
||||||
|
<meta name="copyright" content="Benjamin Zachey" />
|
||||||
|
<style>
|
||||||
|
body
|
||||||
|
{
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
background: black;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div,label,img,input,span,i,b,p,video,audio,object,h1,h2,h3,pre,canvas,iframe
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
video,.video,.filler
|
||||||
|
{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.fullscreen
|
||||||
|
{
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
.content-screen
|
||||||
|
{
|
||||||
|
left: 64px;
|
||||||
|
right: 64px;
|
||||||
|
top: 36px;
|
||||||
|
bottom: 36px;
|
||||||
|
}
|
||||||
|
.plugin-object
|
||||||
|
{
|
||||||
|
visibility: hidden;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript" src="tvx-plugin.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
//HTML5 Extended Video Plugin v0.0.5
|
||||||
|
//(c) 2021 Benjamin Zachey
|
||||||
|
//related API: https://www.w3.org/TR/2011/WD-html5-20110113/video.html
|
||||||
|
/******************************************************************************/
|
||||||
|
function Html5XPlayer() {
|
||||||
|
var player = null;
|
||||||
|
var ready = false;
|
||||||
|
var ended = false;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//Audio & Subtitle Tracks
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
var PROPERTY_PREFIX = "html5x:";
|
||||||
|
var SUBTITLE_KIND = "subtitles";
|
||||||
|
var PROXY_URL = TVXTools.getHostUrl("services/proxy.php?url={URL}");
|
||||||
|
var useProxy = false;
|
||||||
|
var showRelatedContent = false;
|
||||||
|
var hasRelatedContent = false;
|
||||||
|
var defaultAudioTrackLanguage = null;
|
||||||
|
var defaultSubtitleTrackIndex = -1;
|
||||||
|
var audioTrackIndicator = null;
|
||||||
|
var subtitleTrackIndicator = null;
|
||||||
|
var setupCrossOrigin = function(info) {
|
||||||
|
if (player != null) {
|
||||||
|
if (TVXPropertyTools.getBool(info, PROPERTY_PREFIX + "cors", true)) {
|
||||||
|
player.crossOrigin = "anonymous";
|
||||||
|
} else {
|
||||||
|
useProxy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var setupRelatedContent = function(info) {
|
||||||
|
showRelatedContent = TVXPropertyTools.getBool(info, PROPERTY_PREFIX + "content", false);
|
||||||
|
hasRelatedContent = info != null && info.index >= 0;
|
||||||
|
};
|
||||||
|
var hasAudioTracks = function() {
|
||||||
|
return player != null && player.audioTracks != null && player.audioTracks.length > 0;
|
||||||
|
};
|
||||||
|
var hasTextTracks = function() {
|
||||||
|
return player != null && player.textTracks != null && player.textTracks.length > 0;
|
||||||
|
};
|
||||||
|
var foreachAudioTrack = function(callback) {
|
||||||
|
if (hasAudioTracks() && typeof callback == "function") {
|
||||||
|
var tracks = player.audioTracks;
|
||||||
|
var length = player.audioTracks.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
if (callback(i, tracks[i]) === true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var foreachSubtitleTrack = function(callback) {
|
||||||
|
if (hasTextTracks() && typeof callback == "function") {
|
||||||
|
var tracks = player.textTracks;
|
||||||
|
var length = player.textTracks.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var track = tracks[i];
|
||||||
|
if (track.kind === SUBTITLE_KIND && callback(i, tracks[i]) === true) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var isAudioTrackSelected = function(track) {
|
||||||
|
return track != null && track.enabled === true;
|
||||||
|
};
|
||||||
|
var isSubtitleTrackSelected = function(track) {
|
||||||
|
return track != null && track.mode === "showing";
|
||||||
|
};
|
||||||
|
var createIndexTrack = function(index, track) {
|
||||||
|
if (index >= 0 && track != null) {
|
||||||
|
return {
|
||||||
|
index: index,
|
||||||
|
track: track
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
var getAudioTrackLabel = function(indexTrack) {
|
||||||
|
var index = indexTrack != null ? indexTrack.index : -1;
|
||||||
|
var track = indexTrack != null ? indexTrack.track : null;
|
||||||
|
if (index >= 0 && track != null) {
|
||||||
|
return (TVXTools.isFullStr(track.label) ? track.label : "Audio Track " + (index + 1)) +
|
||||||
|
(TVXTools.isFullStr(track.language) ? " (" + track.language.toUpperCase() + ")" : "");
|
||||||
|
}
|
||||||
|
return hasAudioTracks() ? "None" : "Original";
|
||||||
|
};
|
||||||
|
var getSubtitleTrackLabel = function(indexTrack) {
|
||||||
|
var index = indexTrack != null ? indexTrack.index : -1;
|
||||||
|
var track = indexTrack != null ? indexTrack.track : null;
|
||||||
|
if (index >= 0 && track != null) {
|
||||||
|
return (TVXTools.isFullStr(track.label) ? track.label : "Subtitles " + (index + 1)) +
|
||||||
|
(TVXTools.isFullStr(track.language) ? " (" + track.language.toUpperCase() + ")" : "");
|
||||||
|
}
|
||||||
|
return "Off";
|
||||||
|
};
|
||||||
|
var storeAudioTrack = function(track) {
|
||||||
|
if (track != null && TVXTools.isFullStr(track.language)) {
|
||||||
|
TVXServices.storage.set(PROPERTY_PREFIX + "audiotrack", track.language);
|
||||||
|
} else {
|
||||||
|
TVXServices.storage.remove(PROPERTY_PREFIX + "audiotrack");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var storeSubtitleTrack = function(track) {
|
||||||
|
if (track != null && TVXTools.isFullStr(track.language)) {
|
||||||
|
TVXServices.storage.set(PROPERTY_PREFIX + "subtitle", track.language);
|
||||||
|
} else {
|
||||||
|
TVXServices.storage.remove(PROPERTY_PREFIX + "subtitle");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var setupAudioTrackIndicator = function(track) {
|
||||||
|
if (track != null && TVXTools.isFullStr(track.language)) {
|
||||||
|
audioTrackIndicator = "{ico:msx-white:audiotrack} " + track.language.toUpperCase();
|
||||||
|
} else {
|
||||||
|
audioTrackIndicator = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var setupSubtitleTrackIndicator = function(track) {
|
||||||
|
if (track != null && TVXTools.isFullStr(track.language)) {
|
||||||
|
subtitleTrackIndicator = "{ico:msx-white:subtitles} " + track.language.toUpperCase();
|
||||||
|
} else {
|
||||||
|
subtitleTrackIndicator = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var applyIndicators = function() {
|
||||||
|
if (audioTrackIndicator != null && subtitleTrackIndicator != null) {
|
||||||
|
TVXVideoPlugin.setupExtensionLabel(audioTrackIndicator + " " + subtitleTrackIndicator);
|
||||||
|
} else if (audioTrackIndicator) {
|
||||||
|
TVXVideoPlugin.setupExtensionLabel(audioTrackIndicator);
|
||||||
|
} else if (subtitleTrackIndicator) {
|
||||||
|
TVXVideoPlugin.setupExtensionLabel(subtitleTrackIndicator);
|
||||||
|
} else {
|
||||||
|
TVXVideoPlugin.setupExtensionLabel(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var selectAudioTrack = function(trackIndex, store, apply) {
|
||||||
|
var selectedTrack = null;
|
||||||
|
foreachAudioTrack(function(index, track) {
|
||||||
|
if (index == trackIndex) {
|
||||||
|
selectedTrack = track;
|
||||||
|
track.enabled = true;
|
||||||
|
} else {
|
||||||
|
track.enabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setupAudioTrackIndicator(selectedTrack);
|
||||||
|
if (store === true) {
|
||||||
|
storeAudioTrack(selectedTrack);
|
||||||
|
}
|
||||||
|
if (apply === true) {
|
||||||
|
applyIndicators();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var selectSubtitleTrack = function(trackIndex, store, apply) {
|
||||||
|
var selectedTrack = null;
|
||||||
|
foreachSubtitleTrack(function(index, track) {
|
||||||
|
if (index == trackIndex) {
|
||||||
|
selectedTrack = track;
|
||||||
|
track.mode = "showing";
|
||||||
|
} else {
|
||||||
|
track.mode = "disabled";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setupSubtitleTrackIndicator(selectedTrack);
|
||||||
|
if (store === true) {
|
||||||
|
storeSubtitleTrack(selectedTrack);
|
||||||
|
}
|
||||||
|
if (apply === true) {
|
||||||
|
applyIndicators();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var getDefaultAudioTrackIndex = function() {
|
||||||
|
var trackIndex = -1;
|
||||||
|
var fallbackTrackIndex = -1;
|
||||||
|
foreachAudioTrack(function(index, track) {
|
||||||
|
if (fallbackTrackIndex == -1) {
|
||||||
|
//Fallback to first audio track
|
||||||
|
fallbackTrackIndex = index;
|
||||||
|
}
|
||||||
|
if (defaultAudioTrackLanguage != null && defaultAudioTrackLanguage === track.language) {
|
||||||
|
trackIndex = index;
|
||||||
|
return true;//break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return trackIndex >= 0 ? trackIndex : fallbackTrackIndex;
|
||||||
|
};
|
||||||
|
var getSelectedAudioIndexTrack = function() {
|
||||||
|
var indexTrack = null;
|
||||||
|
foreachAudioTrack(function(index, track) {
|
||||||
|
if (isAudioTrackSelected(track)) {
|
||||||
|
indexTrack = createIndexTrack(index, track);
|
||||||
|
return true;//break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return indexTrack;
|
||||||
|
};
|
||||||
|
var getSelectedSubtitleIndexTrack = function() {
|
||||||
|
var indexTrack = null;
|
||||||
|
foreachSubtitleTrack(function(index, track) {
|
||||||
|
if (isSubtitleTrackSelected(track)) {
|
||||||
|
indexTrack = createIndexTrack(index, track);
|
||||||
|
return true;//break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return indexTrack;
|
||||||
|
};
|
||||||
|
var hasSelectedSubtitleTrack = function() {
|
||||||
|
return getSelectedSubtitleIndexTrack() != null;
|
||||||
|
};
|
||||||
|
var setupAudioTracks = function(info) {
|
||||||
|
defaultAudioTrackLanguage = TVXPropertyTools.getFullStr(info, PROPERTY_PREFIX + "audiotrack", TVXServices.storage.get(PROPERTY_PREFIX + "audiotrack"));
|
||||||
|
if (defaultAudioTrackLanguage == "default") {
|
||||||
|
defaultAudioTrackLanguage = null;//Select first audio track
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var processSubtitleTrackCues = function(cues) {
|
||||||
|
if (cues != null && cues.length > 0) {
|
||||||
|
var length = cues.length;
|
||||||
|
//Note: On some platforms (e.g. chrome browsers and android devices), this will have no effect
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var cue = cues[i];
|
||||||
|
cue.snapToLines = true;//Use integer number of lines (default is true)
|
||||||
|
cue.line = -3;//Move the cue up to get some space at the bottom (default is -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var applySubtitleTrackCues = function() {
|
||||||
|
foreachSubtitleTrack(function(index, track) {
|
||||||
|
track.oncuechange = function() {
|
||||||
|
processSubtitleTrackCues(this.activeCues);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var secureSubtitleSource = function(src) {
|
||||||
|
return TVXTools.isSecureContext() ? TVXTools.secureUrl(src) : src;
|
||||||
|
};
|
||||||
|
var createSubtitleSource = function(src) {
|
||||||
|
return useProxy && TVXTools.isHttpUrl(src) ? TVXTools.strReplace(PROXY_URL, "{URL}", TVXTools.strToUrlStr(src)) : src;
|
||||||
|
};
|
||||||
|
var createSubtitleTrack = function(subtitle, src) {
|
||||||
|
if (TVXTools.isFullStr(subtitle) && TVXTools.isFullStr(src)) {
|
||||||
|
var separator = subtitle.indexOf(":");
|
||||||
|
if (separator > 0) {
|
||||||
|
return {
|
||||||
|
label: subtitle.substr(separator + 1),
|
||||||
|
language: subtitle.substr(0, separator),
|
||||||
|
src: secureSubtitleSource(createSubtitleSource(src))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
var completeSubtitleTracks = function(completeState, tracks, callback) {
|
||||||
|
if (completeState != null) {
|
||||||
|
completeState.size--;
|
||||||
|
if (completeState.size == 0 && typeof callback == "function") {
|
||||||
|
callback(tracks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var resolveSubtitleTrack = function(completeState, track, tracks, callback) {
|
||||||
|
if (track != null && !TVXTools.isHttpUrl(track.src)) {
|
||||||
|
TVXVideoPlugin.requestInteractionResponse(track.src, function(data) {
|
||||||
|
if (TVXTools.isFullStr(data.error)) {
|
||||||
|
TVXVideoPlugin.error(data.error);
|
||||||
|
} else if (data.response != null && TVXTools.isHttpUrl(data.response.url)) {
|
||||||
|
track.src = createSubtitleSource(data.response.url);
|
||||||
|
} else {
|
||||||
|
TVXVideoPlugin.warn("Track URL is missing or invalid");
|
||||||
|
}
|
||||||
|
completeSubtitleTracks(completeState, tracks, callback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
completeSubtitleTracks(completeState, tracks, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var createSubtitleTracks = function(info, callback) {
|
||||||
|
var tracks = [];
|
||||||
|
var prefix = PROPERTY_PREFIX + "subtitle:";
|
||||||
|
var prefixLength = prefix.length;
|
||||||
|
var order = TVXPropertyTools.getFullStr(info, prefix + "order", null);
|
||||||
|
TVXPropertyTools.foreach(info, function(key, value) {
|
||||||
|
if (TVXTools.isFullStr(key) && key.indexOf(prefix) == 0) {
|
||||||
|
var track = createSubtitleTrack(key.substr(prefixLength), value);
|
||||||
|
if (track != null) {
|
||||||
|
tracks.push(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (tracks.length > 1 && order != null) {
|
||||||
|
tracks.sort(function(track1, track2) {
|
||||||
|
if (order == "label") {
|
||||||
|
return track1.label.localeCompare(track2.label);
|
||||||
|
} else if (order == "language") {
|
||||||
|
return track1.language.localeCompare(track2.language);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tracks.length > 0) {
|
||||||
|
var completeState = {
|
||||||
|
size: tracks.length
|
||||||
|
};
|
||||||
|
for (var i = 0; i < tracks.length; i++) {
|
||||||
|
resolveSubtitleTrack(completeState, tracks[i], tracks, callback);
|
||||||
|
}
|
||||||
|
} else if (typeof callback == "function") {
|
||||||
|
callback(tracks);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var setupSubtitleTracks = function(info, callback) {
|
||||||
|
if (player != null) {
|
||||||
|
createSubtitleTracks(info, function(tracks) {
|
||||||
|
defaultSubtitleTrackIndex = -1;
|
||||||
|
var html = "";
|
||||||
|
var defaultLanguage = TVXPropertyTools.getFullStr(info, PROPERTY_PREFIX + "subtitle", TVXServices.storage.get(PROPERTY_PREFIX + "subtitle"));
|
||||||
|
if (defaultLanguage == "default") {
|
||||||
|
defaultLanguage = null;//Switch off subtitles
|
||||||
|
}
|
||||||
|
for (var i = 0; i < tracks.length; i++) {
|
||||||
|
var track = tracks[i];
|
||||||
|
var selected = false;
|
||||||
|
if (defaultLanguage != null && defaultLanguage == track.language) {
|
||||||
|
selected = true;
|
||||||
|
defaultSubtitleTrackIndex = i;
|
||||||
|
}
|
||||||
|
html += "<track" +
|
||||||
|
" kind='" + TVXTools.htmlAttrEscape(SUBTITLE_KIND) + "'" +
|
||||||
|
" label='" + TVXTools.htmlAttrEscape(track.label) + "'" +
|
||||||
|
" srclang='" + TVXTools.htmlAttrEscape(track.language) + "'" +
|
||||||
|
" src='" + TVXTools.htmlAttrEscape(track.src) + "'" +
|
||||||
|
(selected ? " default" : "") + "/>";
|
||||||
|
}
|
||||||
|
player.innerHTML = html;
|
||||||
|
applySubtitleTrackCues();
|
||||||
|
if (typeof callback == "function") {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (typeof callback == "function") {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var setupVideoInfo = function(data, callback) {
|
||||||
|
var info = data != null && data.video != null ? data.video.info : null;
|
||||||
|
setupCrossOrigin(info);
|
||||||
|
setupRelatedContent(info);
|
||||||
|
setupAudioTracks(info);
|
||||||
|
setupSubtitleTracks(info, callback);
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//Player Options
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
var currentOptionsFocus = null;
|
||||||
|
var createTrackItem = function(type, index, label, selected) {
|
||||||
|
return {
|
||||||
|
focus: selected,
|
||||||
|
label: label,
|
||||||
|
extensionIcon: selected ? "check" : "blank",
|
||||||
|
action: selected ? "back" : "player:commit:message:" + type + ":" + index
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createAudioTracksPanel = function() {
|
||||||
|
var items = [];
|
||||||
|
if (hasAudioTracks()) {
|
||||||
|
foreachAudioTrack(function(index, track) {
|
||||||
|
items.push(createTrackItem("audiotrack", index, getAudioTrackLabel(createIndexTrack(index, track)), isAudioTrackSelected(track)));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.push(createTrackItem("audiotrack", -1, getAudioTrackLabel(null), true));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
cache: false,
|
||||||
|
reuse: false,
|
||||||
|
headline: "Audio",
|
||||||
|
template: {
|
||||||
|
enumerate: false,
|
||||||
|
type: "control",
|
||||||
|
layout: "0,0,8,1"
|
||||||
|
},
|
||||||
|
items: items
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createSubtitleTracksPanel = function() {
|
||||||
|
var items = [createTrackItem("subtitle", -1, getSubtitleTrackLabel(null), !hasSelectedSubtitleTrack())];
|
||||||
|
foreachSubtitleTrack(function(index, track) {
|
||||||
|
items.push(createTrackItem("subtitle", index, getSubtitleTrackLabel(createIndexTrack(index, track)), isSubtitleTrackSelected(track)));
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
cache: false,
|
||||||
|
reuse: false,
|
||||||
|
headline: "Subtitles",
|
||||||
|
template: {
|
||||||
|
enumerate: false,
|
||||||
|
type: "control",
|
||||||
|
layout: "0,0,8,1"
|
||||||
|
},
|
||||||
|
items: items
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createOptionsPanel = function() {
|
||||||
|
var selectedAudioIndexTrack = getSelectedAudioIndexTrack();
|
||||||
|
var selectedSubtitleIndexTrack = getSelectedSubtitleIndexTrack();
|
||||||
|
return {
|
||||||
|
cache: false,
|
||||||
|
reuse: false,
|
||||||
|
headline: "Options",
|
||||||
|
template: {
|
||||||
|
enumerate: false,
|
||||||
|
type: "control",
|
||||||
|
layout: "0,0,8,1"
|
||||||
|
},
|
||||||
|
items: [{
|
||||||
|
focus: currentOptionsFocus == "audiotrack",
|
||||||
|
id: "audiotrack",
|
||||||
|
icon: "audiotrack",
|
||||||
|
label: "Audio",
|
||||||
|
extensionLabel: getAudioTrackLabel(selectedAudioIndexTrack),
|
||||||
|
action: "[player:commit:message:focus:audiotrack|panel:request:player:audiotrack]"
|
||||||
|
}, {
|
||||||
|
focus: currentOptionsFocus == "subtitle",
|
||||||
|
id: "subtitle",
|
||||||
|
icon: "subtitles",
|
||||||
|
label: "Subtitles",
|
||||||
|
extensionLabel: getSubtitleTrackLabel(selectedSubtitleIndexTrack),
|
||||||
|
action: "[player:commit:message:focus:subtitle|panel:request:player:subtitle]"
|
||||||
|
}, {
|
||||||
|
display: showRelatedContent,
|
||||||
|
enable: hasRelatedContent,
|
||||||
|
focus: currentOptionsFocus == "content",
|
||||||
|
id: "content",
|
||||||
|
icon: "pageview",
|
||||||
|
label: "Show related content",
|
||||||
|
action: "[player:commit:message:focus:content|player:content]"
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var handleMessage = function(message) {
|
||||||
|
if (TVXTools.isFullStr(message)) {
|
||||||
|
if (message.indexOf("focus:") == 0) {
|
||||||
|
currentOptionsFocus = message.substr(6);
|
||||||
|
} else if (message.indexOf("audiotrack:") == 0) {
|
||||||
|
selectAudioTrack(TVXTools.strToNum(message.substr(11), -1), true, true);
|
||||||
|
TVXVideoPlugin.executeAction("back");
|
||||||
|
} else if (message.indexOf("subtitle:") == 0) {
|
||||||
|
selectSubtitleTrack(TVXTools.strToNum(message.substr(9), -1), true, true);
|
||||||
|
TVXVideoPlugin.executeAction("back");
|
||||||
|
} else {
|
||||||
|
TVXVideoPlugin.warn("Unknown plugin message: '" + message + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var createResponseData = function(dataId) {
|
||||||
|
if (TVXTools.isFullStr(dataId)) {
|
||||||
|
if (dataId == "options") {
|
||||||
|
return createOptionsPanel();
|
||||||
|
} else if (dataId == "audiotrack") {
|
||||||
|
return createAudioTracksPanel();
|
||||||
|
} else if (dataId == "subtitle") {
|
||||||
|
return createSubtitleTracksPanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//Event Callbacks
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
var onWaiting = function() {
|
||||||
|
TVXVideoPlugin.startLoading();
|
||||||
|
};
|
||||||
|
var onPlaying = function() {
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
TVXVideoPlugin.setState(TVXVideoState.PLAYING);
|
||||||
|
};
|
||||||
|
var onPaused = function() {
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
TVXVideoPlugin.setState(TVXVideoState.PAUSED);
|
||||||
|
};
|
||||||
|
var onContinue = function() {
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
};
|
||||||
|
var onReady = function() {
|
||||||
|
if (player != null && !ready) {
|
||||||
|
ready = true;
|
||||||
|
TVXVideoPlugin.debug("Video ready");
|
||||||
|
selectAudioTrack(getDefaultAudioTrackIndex(), false, false);
|
||||||
|
selectSubtitleTrack(defaultSubtitleTrackIndex, false, true);
|
||||||
|
TVXVideoPlugin.applyVolume();
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
TVXVideoPlugin.startPlayback(true);//Accelerated start
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var getErrorText = function(code) {
|
||||||
|
if (code == 1) {
|
||||||
|
//The fetching of the associated resource was aborted by the user's request.
|
||||||
|
return "Playback Aborted";
|
||||||
|
} else if (code == 2) {
|
||||||
|
//Some kind of network error occurred which prevented the media from being successfully fetched, despite having previously been available.
|
||||||
|
return "Network Error";
|
||||||
|
} else if (code == 3) {
|
||||||
|
//Despite having previously been determined to be usable, an error occurred while trying to decode the media resource, resulting in an error.
|
||||||
|
return "Media Decode Error";
|
||||||
|
} else if (code == 4) {
|
||||||
|
//The associated resource or media provider object (such as a MediaStream) has been found to be unsuitable.
|
||||||
|
return "Source Not Supported";
|
||||||
|
}
|
||||||
|
return "Unknown Error";
|
||||||
|
};
|
||||||
|
var getErrorMessage = function(code, message) {
|
||||||
|
var msg = code + ": " + getErrorText(code);
|
||||||
|
if (TVXTools.isFullStr(message)) {
|
||||||
|
msg += ": " + message;
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
};
|
||||||
|
var onError = function() {
|
||||||
|
if (player != null && player.error != null) {
|
||||||
|
TVXVideoPlugin.error("Video error: " + getErrorMessage(player.error.code, player.error.message));
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onEnded = function() {
|
||||||
|
if (!ended) {
|
||||||
|
ended = true;
|
||||||
|
TVXVideoPlugin.debug("Video ended");
|
||||||
|
TVXVideoPlugin.stopPlayback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//Helper Functions
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
var setupVideoWithId = function(id) {
|
||||||
|
if (TVXTools.isFullStr(id)) {
|
||||||
|
TVXVideoPlugin.requestInteractionResponse(id, function(data) {
|
||||||
|
if (TVXTools.isFullStr(data.error)) {
|
||||||
|
TVXVideoPlugin.error(data.error);
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
} else if (!setupVideoWithUrl(data.response != null ? data.response.url : null)) {
|
||||||
|
TVXVideoPlugin.warn("Video URL is missing");
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
var setupVideoWithUrl = function(url) {
|
||||||
|
//Note: URL does not need to be an HTTP/HTTPS URL (it can be any URL)
|
||||||
|
if (TVXTools.isFullStr(url)) {
|
||||||
|
TVXVideoPlugin.requestData("video:info", function(data) {
|
||||||
|
setupVideoInfo(data, function() {
|
||||||
|
player.src = url;
|
||||||
|
player.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//Player Interface
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
this.init = function() {
|
||||||
|
player = document.getElementById("player");
|
||||||
|
player.addEventListener("canplay", onReady);
|
||||||
|
player.addEventListener("error", onError);
|
||||||
|
player.addEventListener("ended", onEnded);
|
||||||
|
player.addEventListener("waiting", onWaiting);
|
||||||
|
player.addEventListener("play", onContinue);
|
||||||
|
player.addEventListener("playing", onPlaying);
|
||||||
|
player.addEventListener("pause", onPaused);
|
||||||
|
player.addEventListener("seeked", onContinue);
|
||||||
|
player.addEventListener("abort", onContinue);
|
||||||
|
};
|
||||||
|
this.ready = function() {
|
||||||
|
if (player != null) {
|
||||||
|
TVXVideoPlugin.debug("Video plugin ready");
|
||||||
|
TVXVideoPlugin.startLoading();
|
||||||
|
if (!setupVideoWithId(TVXServices.urlParams.get("id")) &&
|
||||||
|
!setupVideoWithUrl(TVXServices.urlParams.get("url"))) {
|
||||||
|
TVXVideoPlugin.warn("Video ID or URL is missing");
|
||||||
|
TVXVideoPlugin.stopLoading();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TVXVideoPlugin.error("Video player is not initialized");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.dispose = function() {
|
||||||
|
if (player != null) {
|
||||||
|
player.removeEventListener("canplay", onReady);
|
||||||
|
player.removeEventListener("error", onError);
|
||||||
|
player.removeEventListener("ended", onEnded);
|
||||||
|
player.removeEventListener("waiting", onWaiting);
|
||||||
|
player.removeEventListener("play", onContinue);
|
||||||
|
player.removeEventListener("playing", onPlaying);
|
||||||
|
player.removeEventListener("pause", onPaused);
|
||||||
|
player.removeEventListener("seeked", onContinue);
|
||||||
|
player.removeEventListener("abort", onContinue);
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.play = function() {
|
||||||
|
if (player != null) {
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.pause = function() {
|
||||||
|
if (player != null) {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.stop = function() {
|
||||||
|
if (player != null) {
|
||||||
|
//Note: Html5 player does not support stop -> use pause
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.getDuration = function() {
|
||||||
|
if (player != null) {
|
||||||
|
return player.duration;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
this.getPosition = function() {
|
||||||
|
if (player != null) {
|
||||||
|
return player.currentTime;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
this.setPosition = function(position) {
|
||||||
|
if (player != null) {
|
||||||
|
player.currentTime = position;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.setVolume = function(volume) {
|
||||||
|
if (player != null) {
|
||||||
|
player.volume = volume / 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.getVolume = function() {
|
||||||
|
if (player != null) {
|
||||||
|
return player.volume * 100;
|
||||||
|
}
|
||||||
|
return 100;
|
||||||
|
};
|
||||||
|
this.setMuted = function(muted) {
|
||||||
|
if (player != null) {
|
||||||
|
player.muted = muted;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.isMuted = function() {
|
||||||
|
if (player != null) {
|
||||||
|
return player.muted;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
this.getSpeed = function() {
|
||||||
|
if (player != null) {
|
||||||
|
return player.playbackRate;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
this.setSpeed = function(speed) {
|
||||||
|
if (player != null) {
|
||||||
|
player.playbackRate = speed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.getUpdateData = function() {
|
||||||
|
return {
|
||||||
|
position: this.getPosition(),
|
||||||
|
duration: this.getDuration(),
|
||||||
|
speed: this.getSpeed()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
this.handleData = function(data) {
|
||||||
|
handleMessage(data.message);
|
||||||
|
};
|
||||||
|
this.handleRequest = function(dataId, data, callback) {
|
||||||
|
callback(createResponseData(dataId));
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
}
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
//Setup
|
||||||
|
/******************************************************************************/
|
||||||
|
window.onload = function() {
|
||||||
|
TVXVideoPlugin.setupPlayer(new Html5XPlayer());
|
||||||
|
TVXVideoPlugin.init();
|
||||||
|
};
|
||||||
|
/******************************************************************************/
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<video id="player" playsinline></video>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -7,7 +7,373 @@
|
|||||||
<meta name="contact" content="admin@benzac.de" />
|
<meta name="contact" content="admin@benzac.de" />
|
||||||
<meta name="copyright" content="Benjamin Zachey" />
|
<meta name="copyright" content="Benjamin Zachey" />
|
||||||
<script type="text/javascript" src="tvx-plugin.min.js"></script>
|
<script type="text/javascript" src="tvx-plugin.min.js"></script>
|
||||||
<script type="text/javascript" src="tizen.js"></script>
|
<script type="text/javascript">
|
||||||
|
/******************************************************************************/
|
||||||
|
//Tizen Interaction Plugin v0.0.1
|
||||||
|
//(c) 2021 Benjamin Zachey
|
||||||
|
//Action syntax example:
|
||||||
|
//- "content:request:interaction:init@http://msx.benzac.de/interaction/tizen.html"
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
//TizenPlayer
|
||||||
|
/******************************************************************************/
|
||||||
|
function TizenPlayer() {
|
||||||
|
var pushToArray = function(array, items) {
|
||||||
|
if (array != null && items != null) {
|
||||||
|
if (Array.isArray(items)) {
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
array.push(items[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
array.push(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var getValueLabel = function(value) {
|
||||||
|
if (typeof value == "string") {
|
||||||
|
return TVXTools.strFullCheck(value, "-");
|
||||||
|
}
|
||||||
|
return value != null ? TVXTools.strValue(value) : "-";
|
||||||
|
};
|
||||||
|
var getPropertyLabel = function(value, unit) {
|
||||||
|
if (TVXTools.isFullStr(value) && value.indexOf("|") >= 0) {
|
||||||
|
return value.split("|")[0];
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
if (unit != null) {
|
||||||
|
return value + " " + unit;
|
||||||
|
}
|
||||||
|
return TVXTools.strValue(value);
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
};
|
||||||
|
var getPropertyValue = function(value) {
|
||||||
|
if (TVXTools.isFullStr(value) && value.indexOf("|") >= 0) {
|
||||||
|
value = value.split("|")[1];
|
||||||
|
}
|
||||||
|
if (TVXTools.isFullStr(value) && value.indexOf("num:") == 0) {
|
||||||
|
return TVXTools.strToNum(value.substr(4));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
var getTrackLabel = function(track) {
|
||||||
|
if (track != null) {
|
||||||
|
var prefix = "Track " + track.index;
|
||||||
|
var suffix = track.info != null ? track.info.language : null;//Audio track
|
||||||
|
if (suffix == null) {
|
||||||
|
suffix = track.info != null ? track.info.track_lang : null;//Text track
|
||||||
|
}
|
||||||
|
return TVXTools.isFullStr(suffix) ? prefix + " (" + suffix + ")" : prefix;
|
||||||
|
}
|
||||||
|
return "None";
|
||||||
|
};
|
||||||
|
var createPropertyControls = function(y, propertyIcon, propertyLabel, propertyKey, propertyValue, propertyUnit, availableValues, nextButton) {
|
||||||
|
var panelItems = [];
|
||||||
|
var selectedPropertyLabel = getPropertyLabel(propertyValue, propertyUnit);
|
||||||
|
var firstPropertyValue = null;
|
||||||
|
var nextPropertyValue = null;
|
||||||
|
var selectNext = false;
|
||||||
|
if (availableValues != null) {
|
||||||
|
for (var i = 0; i < availableValues.length; i++) {
|
||||||
|
var value = getPropertyValue(availableValues[i]);
|
||||||
|
var label = getPropertyLabel(availableValues[i], propertyUnit);
|
||||||
|
var selected = value === propertyValue;
|
||||||
|
if (firstPropertyValue == null) {
|
||||||
|
firstPropertyValue = value;
|
||||||
|
}
|
||||||
|
if (selected) {
|
||||||
|
selectNext = true;
|
||||||
|
selectedPropertyLabel = label;
|
||||||
|
} else if (selectNext) {
|
||||||
|
selectNext = false;
|
||||||
|
nextPropertyValue = value;
|
||||||
|
}
|
||||||
|
panelItems.push({
|
||||||
|
focus: selected,
|
||||||
|
extensionIcon: selected ? "check" : "blank",
|
||||||
|
label: label,
|
||||||
|
action: selected ? "back" : "[invalidate:content|back|player:commit]",
|
||||||
|
data: {
|
||||||
|
key: propertyKey,
|
||||||
|
value: value,
|
||||||
|
action: "reload:content"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextPropertyValue == null) {
|
||||||
|
nextPropertyValue = firstPropertyValue;
|
||||||
|
}
|
||||||
|
return [{
|
||||||
|
enable: panelItems.length > 0,
|
||||||
|
type: "control",
|
||||||
|
layout: "0," + y + "," + (nextButton ? "7,1" : "8,1"),
|
||||||
|
icon: propertyIcon,
|
||||||
|
label: propertyLabel,
|
||||||
|
extensionLabel: selectedPropertyLabel,
|
||||||
|
action: "panel:data",
|
||||||
|
data: {
|
||||||
|
headline: propertyLabel,
|
||||||
|
compress: panelItems.length > 6,
|
||||||
|
template: {
|
||||||
|
type: "control",
|
||||||
|
enumerate: false,
|
||||||
|
layout: panelItems.length > 8 ? "0,0,5,1" : panelItems.length > 6 ? "0,0,10,1" : "0,0,8,1"
|
||||||
|
},
|
||||||
|
items: panelItems
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
display: nextButton,
|
||||||
|
enable: nextPropertyValue != null,
|
||||||
|
type: "button",
|
||||||
|
icon: "navigate-next",
|
||||||
|
iconSize: "small",
|
||||||
|
layout: "7," + y + ",1,1",
|
||||||
|
action: "[invalidate:content|player:commit]",
|
||||||
|
data: {
|
||||||
|
key: propertyKey,
|
||||||
|
value: nextPropertyValue,
|
||||||
|
action: "reload:content"
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
var createTrackControl = function(y, propertyIcon, propertyLabel, propertyKey, currentTrack, availableTracks) {
|
||||||
|
var panelItems = [];
|
||||||
|
if (availableTracks != null) {
|
||||||
|
for (var i = 0; i < availableTracks.length; i++) {
|
||||||
|
var track = availableTracks[i];
|
||||||
|
var selected = track.index === (currentTrack != null ? currentTrack.index : -1);
|
||||||
|
panelItems.push({
|
||||||
|
focus: selected,
|
||||||
|
extensionIcon: selected ? "check" : "blank",
|
||||||
|
label: getTrackLabel(track),
|
||||||
|
action: selected ? "back" : "[invalidate:content|back|player:commit]",
|
||||||
|
data: {
|
||||||
|
key: propertyKey,
|
||||||
|
value: track.index,
|
||||||
|
action: "reload:content"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
enable: panelItems.length > 0,
|
||||||
|
type: "control",
|
||||||
|
layout: "0," + y + ",8,1",
|
||||||
|
icon: propertyIcon,
|
||||||
|
label: propertyLabel,
|
||||||
|
extensionLabel: getTrackLabel(currentTrack),
|
||||||
|
action: "panel:data",
|
||||||
|
data: {
|
||||||
|
headline: propertyLabel,
|
||||||
|
compress: panelItems.length > 6,
|
||||||
|
template: {
|
||||||
|
type: "control",
|
||||||
|
enumerate: false,
|
||||||
|
layout: panelItems.length > 8 ? "0,0,5,1" : panelItems.length > 6 ? "0,0,10,1" : "0,0,8,1"
|
||||||
|
},
|
||||||
|
items: panelItems
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createControlItems = function(infoData) {
|
||||||
|
var items = [];
|
||||||
|
pushToArray(items, createPropertyControls(0, "featured-video", "Display Area", "tizen:display:area",
|
||||||
|
infoData && infoData.display != null ? infoData.display.area : null, null, [
|
||||||
|
"21x9|0,0.119,1,0.762",
|
||||||
|
"16x9 (Default)|0,0,1,1",
|
||||||
|
"4x3|0.125,0,0.75,1"
|
||||||
|
], true));
|
||||||
|
pushToArray(items, createPropertyControls(1, "aspect-ratio", "Display Mode", "tizen:display:mode",
|
||||||
|
infoData != null && infoData.display != null ? infoData.display.mode : null, null, [
|
||||||
|
"Fit Screen (Default)|PLAYER_DISPLAY_MODE_LETTER_BOX",
|
||||||
|
"Fill Screen|PLAYER_DISPLAY_MODE_FULL_SCREEN",
|
||||||
|
"Auto Aspect Ratio|PLAYER_DISPLAY_MODE_AUTO_ASPECT_RATIO"
|
||||||
|
], true));
|
||||||
|
pushToArray(items, createPropertyControls(2, "av-timer", "Buffer Timeout", "tizen:buffer:timeout",
|
||||||
|
infoData != null && infoData.buffer != null ? infoData.buffer.timeout : null, "sec", [
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, "20 sec (Default)|num:20", 25, 30, 40
|
||||||
|
], false));
|
||||||
|
pushToArray(items, createPropertyControls(3, "timelapse", "Buffer Size (Init)", "tizen:buffer:size:init",
|
||||||
|
infoData != null && infoData.buffer != null && infoData.buffer.size != null ? infoData.buffer.size.init : null, "sec", null, false));
|
||||||
|
pushToArray(items, createPropertyControls(4, "timelapse", "Buffer Size (Resume)", "tizen:buffer:size:resume",
|
||||||
|
infoData != null && infoData.buffer != null && infoData.buffer.size != null ? infoData.buffer.size.resume : null, "sec", null, false));
|
||||||
|
pushToArray(items, createTrackControl(5, "audiotrack", "Audio Track", "tizen:track:audio",
|
||||||
|
infoData != null && infoData.stream != null ? infoData.stream.audio : null,
|
||||||
|
infoData != null && infoData.tracks != null ? infoData.tracks.audio : null));
|
||||||
|
pushToArray(items, createTrackControl(6, "subtitles", "Text Track", "tizen:track:text",
|
||||||
|
infoData != null && infoData.stream != null ? infoData.stream.text : null,
|
||||||
|
infoData != null && infoData.tracks != null ? infoData.tracks.text : null));
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
var createInfoItems = function(infoData) {
|
||||||
|
var infoKeys = "-";
|
||||||
|
var infoValues = "-";
|
||||||
|
if (infoData != null) {
|
||||||
|
infoKeys = "Version:{br}State:{br}{br}";
|
||||||
|
infoValues = getValueLabel(infoData.version) + "{br}" +
|
||||||
|
getValueLabel(infoData.state) + "{br}{br}";
|
||||||
|
if (infoData.stream != null) {
|
||||||
|
if (infoData.stream.video != null && infoData.stream.video.info != null) {
|
||||||
|
for (var key in infoData.stream.video.info) {
|
||||||
|
infoKeys += "Video [" + key + "]:{br}";
|
||||||
|
infoValues += getValueLabel(infoData.stream.video.info[key]) + "{br}";
|
||||||
|
}
|
||||||
|
infoKeys += "{br}";
|
||||||
|
infoValues += "{br}";
|
||||||
|
}
|
||||||
|
if (infoData.stream.audio != null && infoData.stream.audio.info != null) {
|
||||||
|
for (var key in infoData.stream.audio.info) {
|
||||||
|
infoKeys += "Audio [" + key + "]:{br}";
|
||||||
|
infoValues += getValueLabel(infoData.stream.audio.info[key]) + "{br}";
|
||||||
|
}
|
||||||
|
infoKeys += "{br}";
|
||||||
|
infoValues += "{br}";
|
||||||
|
}
|
||||||
|
if (infoData.stream.text != null && infoData.stream.text.info != null) {
|
||||||
|
for (var key in infoData.stream.text.info) {
|
||||||
|
infoKeys += "Text [" + key + "]:{br}";
|
||||||
|
infoValues += getValueLabel(infoData.stream.text.info[key]) + "{br}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [{
|
||||||
|
type: "space",
|
||||||
|
layout: "8,0,3,8",
|
||||||
|
offset: "0.25,0,0,0",
|
||||||
|
truncation: "text",
|
||||||
|
text: infoKeys
|
||||||
|
}, {
|
||||||
|
type: "space",
|
||||||
|
layout: "11,0,5,8",
|
||||||
|
offset: "0.25,0,-0.25,0",
|
||||||
|
truncation: "text",
|
||||||
|
text: "{col:msx-white}" + infoValues,
|
||||||
|
live: {
|
||||||
|
type: "airtime",
|
||||||
|
duration: 2000,
|
||||||
|
over: {
|
||||||
|
action: "reload:content"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
var createContentItems = function(infoData) {
|
||||||
|
var items = [];
|
||||||
|
pushToArray(items, createControlItems(infoData));
|
||||||
|
pushToArray(items, createInfoItems(infoData));
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
var createContent = function(infoData) {
|
||||||
|
return {
|
||||||
|
cache: false,
|
||||||
|
compress: true,
|
||||||
|
type: "pages",
|
||||||
|
headline: "Tizen Player",
|
||||||
|
extension: "{ico:msx-white:timer} " + TVXDateTools.getTimestamp(),
|
||||||
|
pages: [{
|
||||||
|
items: createContentItems(infoData)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createWarningContent = function(playerInfo) {
|
||||||
|
return {
|
||||||
|
type: "pages",
|
||||||
|
headline: "Tizen Player",
|
||||||
|
pages: [{
|
||||||
|
items: [{
|
||||||
|
type: "default",
|
||||||
|
layout: "0,0,12,6",
|
||||||
|
color: "msx-glass",
|
||||||
|
headline: "{ico:msx-yellow:warning} Player Not Available",
|
||||||
|
text: "Tizen player is required for this plugin. Current player is: {txt:msx-white:" + playerInfo + "}."
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var createDummyData = function() {
|
||||||
|
return {
|
||||||
|
version: "1.0",
|
||||||
|
state: "PLAYING",
|
||||||
|
display: {
|
||||||
|
area: "0,0,1,1",
|
||||||
|
mode: "PLAYER_DISPLAY_MODE_LETTER_BOX"
|
||||||
|
},
|
||||||
|
buffer: {
|
||||||
|
timeout: 20,
|
||||||
|
size: {
|
||||||
|
init: 10,
|
||||||
|
resume: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stream: {
|
||||||
|
video: {
|
||||||
|
index: 0,
|
||||||
|
info: {
|
||||||
|
fourCC: "h264",
|
||||||
|
Width: 1280,
|
||||||
|
Height: 720,
|
||||||
|
Bit_rate: 128000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
this.handleRequest = function(playerInfo, dataId, callback) {
|
||||||
|
if (dataId == "init") {
|
||||||
|
if (TVXTools.isFullStr(playerInfo) && (playerInfo == "tizen" || playerInfo.indexOf("tizen/") == 0)) {
|
||||||
|
TVXInteractionPlugin.requestPlayerResponse("tizen:info", function(data) {
|
||||||
|
callback(createContent(data.response != null && data.response.tizen != null ? data.response.tizen.info : null));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(createWarningContent(playerInfo));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (dataId == "dummy") {
|
||||||
|
callback(createContent(createDummyData()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
//TizenHandler
|
||||||
|
/******************************************************************************/
|
||||||
|
function TizenHandler() {
|
||||||
|
var playerInfo = null;
|
||||||
|
var readyService = new TVXBusyService();
|
||||||
|
var player = new TizenPlayer();
|
||||||
|
|
||||||
|
this.ready = function() {
|
||||||
|
readyService.start();
|
||||||
|
TVXInteractionPlugin.requestData("info:base", function(data) {
|
||||||
|
playerInfo = TVXTools.strFullCheck(data.info != null ? data.info.player : null, "unknown");
|
||||||
|
readyService.stop();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.handleRequest = function(dataId, data, callback) {
|
||||||
|
readyService.onReady(function() {
|
||||||
|
if (!player.handleRequest(playerInfo, dataId, callback)) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
//Setup
|
||||||
|
/******************************************************************************/
|
||||||
|
window.onload = function() {
|
||||||
|
TVXInteractionPlugin.setupHandler(new TizenHandler());
|
||||||
|
TVXInteractionPlugin.init();
|
||||||
|
};
|
||||||
|
/******************************************************************************/
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,365 +0,0 @@
|
|||||||
/******************************************************************************/
|
|
||||||
//Tizen Interaction Plugin v0.0.1
|
|
||||||
//(c) 2021 Benjamin Zachey
|
|
||||||
//Action syntax example:
|
|
||||||
//- "content:request:interaction:init@http://msx.benzac.de/interaction/tizen.html"
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
//TizenPlayer
|
|
||||||
/******************************************************************************/
|
|
||||||
function TizenPlayer() {
|
|
||||||
var pushToArray = function(array, items) {
|
|
||||||
if (array != null && items != null) {
|
|
||||||
if (Array.isArray(items)) {
|
|
||||||
for (var i = 0; i < items.length; i++) {
|
|
||||||
array.push(items[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
array.push(items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var getValueLabel = function(value) {
|
|
||||||
if (typeof value == "string") {
|
|
||||||
return TVXTools.strFullCheck(value, "-");
|
|
||||||
}
|
|
||||||
return value != null ? TVXTools.strValue(value) : "-";
|
|
||||||
};
|
|
||||||
var getPropertyLabel = function(value, unit) {
|
|
||||||
if (TVXTools.isFullStr(value) && value.indexOf("|") >= 0) {
|
|
||||||
return value.split("|")[0];
|
|
||||||
}
|
|
||||||
if (value != null) {
|
|
||||||
if (unit != null) {
|
|
||||||
return value + " " + unit;
|
|
||||||
}
|
|
||||||
return TVXTools.strValue(value);
|
|
||||||
}
|
|
||||||
return "Unknown";
|
|
||||||
};
|
|
||||||
var getPropertyValue = function(value) {
|
|
||||||
if (TVXTools.isFullStr(value) && value.indexOf("|") >= 0) {
|
|
||||||
value = value.split("|")[1];
|
|
||||||
}
|
|
||||||
if (TVXTools.isFullStr(value) && value.indexOf("num:") == 0) {
|
|
||||||
return TVXTools.strToNum(value.substr(4));
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
var getTrackLabel = function(track) {
|
|
||||||
if (track != null) {
|
|
||||||
var prefix = "Track " + track.index;
|
|
||||||
var suffix = track.info != null ? track.info.language : null;//Audio track
|
|
||||||
if (suffix == null) {
|
|
||||||
suffix = track.info != null ? track.info.track_lang : null;//Text track
|
|
||||||
}
|
|
||||||
return TVXTools.isFullStr(suffix) ? prefix + " (" + suffix + ")" : prefix;
|
|
||||||
}
|
|
||||||
return "None";
|
|
||||||
};
|
|
||||||
var createPropertyControls = function(y, propertyIcon, propertyLabel, propertyKey, propertyValue, propertyUnit, availableValues, nextButton) {
|
|
||||||
var panelItems = [];
|
|
||||||
var selectedPropertyLabel = getPropertyLabel(propertyValue, propertyUnit);
|
|
||||||
var firstPropertyValue = null;
|
|
||||||
var nextPropertyValue = null;
|
|
||||||
var selectNext = false;
|
|
||||||
if (availableValues != null) {
|
|
||||||
for (var i = 0; i < availableValues.length; i++) {
|
|
||||||
var value = getPropertyValue(availableValues[i]);
|
|
||||||
var label = getPropertyLabel(availableValues[i], propertyUnit);
|
|
||||||
var selected = value === propertyValue;
|
|
||||||
if (firstPropertyValue == null) {
|
|
||||||
firstPropertyValue = value;
|
|
||||||
}
|
|
||||||
if (selected) {
|
|
||||||
selectNext = true;
|
|
||||||
selectedPropertyLabel = label;
|
|
||||||
} else if (selectNext) {
|
|
||||||
selectNext = false;
|
|
||||||
nextPropertyValue = value;
|
|
||||||
}
|
|
||||||
panelItems.push({
|
|
||||||
focus: selected,
|
|
||||||
extensionIcon: selected ? "check" : "blank",
|
|
||||||
label: label,
|
|
||||||
action: selected ? "back" : "[invalidate:content|back|player:commit]",
|
|
||||||
data: {
|
|
||||||
key: propertyKey,
|
|
||||||
value: value,
|
|
||||||
action: "reload:content"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nextPropertyValue == null) {
|
|
||||||
nextPropertyValue = firstPropertyValue;
|
|
||||||
}
|
|
||||||
return [{
|
|
||||||
enable: panelItems.length > 0,
|
|
||||||
type: "control",
|
|
||||||
layout: "0," + y + "," + (nextButton ? "7,1" : "8,1"),
|
|
||||||
icon: propertyIcon,
|
|
||||||
label: propertyLabel,
|
|
||||||
extensionLabel: selectedPropertyLabel,
|
|
||||||
action: "panel:data",
|
|
||||||
data: {
|
|
||||||
headline: propertyLabel,
|
|
||||||
compress: panelItems.length > 6,
|
|
||||||
template: {
|
|
||||||
type: "control",
|
|
||||||
enumerate: false,
|
|
||||||
layout: panelItems.length > 8 ? "0,0,5,1" : panelItems.length > 6 ? "0,0,10,1" : "0,0,8,1"
|
|
||||||
},
|
|
||||||
items: panelItems
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
display: nextButton,
|
|
||||||
enable: nextPropertyValue != null,
|
|
||||||
type: "button",
|
|
||||||
icon: "navigate-next",
|
|
||||||
iconSize: "small",
|
|
||||||
layout: "7," + y + ",1,1",
|
|
||||||
action: "[invalidate:content|player:commit]",
|
|
||||||
data: {
|
|
||||||
key: propertyKey,
|
|
||||||
value: nextPropertyValue,
|
|
||||||
action: "reload:content"
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
var createTrackControl = function(y, propertyIcon, propertyLabel, propertyKey, currentTrack, availableTracks) {
|
|
||||||
var panelItems = [];
|
|
||||||
if (availableTracks != null) {
|
|
||||||
for (var i = 0; i < availableTracks.length; i++) {
|
|
||||||
var track = availableTracks[i];
|
|
||||||
var selected = track.index === (currentTrack != null ? currentTrack.index : -1);
|
|
||||||
panelItems.push({
|
|
||||||
focus: selected,
|
|
||||||
extensionIcon: selected ? "check" : "blank",
|
|
||||||
label: getTrackLabel(track),
|
|
||||||
action: selected ? "back" : "[invalidate:content|back|player:commit]",
|
|
||||||
data: {
|
|
||||||
key: propertyKey,
|
|
||||||
value: track.index,
|
|
||||||
action: "reload:content"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
enable: panelItems.length > 0,
|
|
||||||
type: "control",
|
|
||||||
layout: "0," + y + ",8,1",
|
|
||||||
icon: propertyIcon,
|
|
||||||
label: propertyLabel,
|
|
||||||
extensionLabel: getTrackLabel(currentTrack),
|
|
||||||
action: "panel:data",
|
|
||||||
data: {
|
|
||||||
headline: propertyLabel,
|
|
||||||
compress: panelItems.length > 6,
|
|
||||||
template: {
|
|
||||||
type: "control",
|
|
||||||
enumerate: false,
|
|
||||||
layout: panelItems.length > 8 ? "0,0,5,1" : panelItems.length > 6 ? "0,0,10,1" : "0,0,8,1"
|
|
||||||
},
|
|
||||||
items: panelItems
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var createControlItems = function(infoData) {
|
|
||||||
var items = [];
|
|
||||||
pushToArray(items, createPropertyControls(0, "featured-video", "Display Area", "tizen:display:area",
|
|
||||||
infoData && infoData.display != null ? infoData.display.area : null, null, [
|
|
||||||
"21x9|0,0.119,1,0.762",
|
|
||||||
"16x9 (Default)|0,0,1,1",
|
|
||||||
"4x3|0.125,0,0.75,1"
|
|
||||||
], true));
|
|
||||||
pushToArray(items, createPropertyControls(1, "aspect-ratio", "Display Mode", "tizen:display:mode",
|
|
||||||
infoData != null && infoData.display != null ? infoData.display.mode : null, null, [
|
|
||||||
"Fit Screen (Default)|PLAYER_DISPLAY_MODE_LETTER_BOX",
|
|
||||||
"Fill Screen|PLAYER_DISPLAY_MODE_FULL_SCREEN",
|
|
||||||
"Auto Aspect Ratio|PLAYER_DISPLAY_MODE_AUTO_ASPECT_RATIO"
|
|
||||||
], true));
|
|
||||||
pushToArray(items, createPropertyControls(2, "av-timer", "Buffer Timeout", "tizen:buffer:timeout",
|
|
||||||
infoData != null && infoData.buffer != null ? infoData.buffer.timeout : null, "sec", [
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, "20 sec (Default)|num:20", 25, 30, 40
|
|
||||||
], false));
|
|
||||||
pushToArray(items, createPropertyControls(3, "timelapse", "Buffer Size (Init)", "tizen:buffer:size:init",
|
|
||||||
infoData != null && infoData.buffer != null && infoData.buffer.size != null ? infoData.buffer.size.init : null, "sec", null, false));
|
|
||||||
pushToArray(items, createPropertyControls(4, "timelapse", "Buffer Size (Resume)", "tizen:buffer:size:resume",
|
|
||||||
infoData != null && infoData.buffer != null && infoData.buffer.size != null ? infoData.buffer.size.resume : null, "sec", null, false));
|
|
||||||
pushToArray(items, createTrackControl(5, "audiotrack", "Audio Track", "tizen:track:audio",
|
|
||||||
infoData != null && infoData.stream != null ? infoData.stream.audio : null,
|
|
||||||
infoData != null && infoData.tracks != null ? infoData.tracks.audio : null));
|
|
||||||
pushToArray(items, createTrackControl(6, "subtitles", "Text Track", "tizen:track:text",
|
|
||||||
infoData != null && infoData.stream != null ? infoData.stream.text : null,
|
|
||||||
infoData != null && infoData.tracks != null ? infoData.tracks.text : null));
|
|
||||||
return items;
|
|
||||||
};
|
|
||||||
var createInfoItems = function(infoData) {
|
|
||||||
var infoKeys = "-";
|
|
||||||
var infoValues = "-";
|
|
||||||
if (infoData != null) {
|
|
||||||
infoKeys = "Version:{br}State:{br}{br}";
|
|
||||||
infoValues = getValueLabel(infoData.version) + "{br}" +
|
|
||||||
getValueLabel(infoData.state) + "{br}{br}";
|
|
||||||
if (infoData.stream != null) {
|
|
||||||
if (infoData.stream.video != null && infoData.stream.video.info != null) {
|
|
||||||
for (var key in infoData.stream.video.info) {
|
|
||||||
infoKeys += "Video [" + key + "]:{br}";
|
|
||||||
infoValues += getValueLabel(infoData.stream.video.info[key]) + "{br}";
|
|
||||||
}
|
|
||||||
infoKeys += "{br}";
|
|
||||||
infoValues += "{br}";
|
|
||||||
}
|
|
||||||
if (infoData.stream.audio != null && infoData.stream.audio.info != null) {
|
|
||||||
for (var key in infoData.stream.audio.info) {
|
|
||||||
infoKeys += "Audio [" + key + "]:{br}";
|
|
||||||
infoValues += getValueLabel(infoData.stream.audio.info[key]) + "{br}";
|
|
||||||
}
|
|
||||||
infoKeys += "{br}";
|
|
||||||
infoValues += "{br}";
|
|
||||||
}
|
|
||||||
if (infoData.stream.text != null && infoData.stream.text.info != null) {
|
|
||||||
for (var key in infoData.stream.text.info) {
|
|
||||||
infoKeys += "Text [" + key + "]:{br}";
|
|
||||||
infoValues += getValueLabel(infoData.stream.text.info[key]) + "{br}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [{
|
|
||||||
type: "space",
|
|
||||||
layout: "8,0,3,8",
|
|
||||||
offset: "0.25,0,0,0",
|
|
||||||
truncation: "text",
|
|
||||||
text: infoKeys
|
|
||||||
}, {
|
|
||||||
type: "space",
|
|
||||||
layout: "11,0,5,8",
|
|
||||||
offset: "0.25,0,-0.25,0",
|
|
||||||
truncation: "text",
|
|
||||||
text: "{col:msx-white}" + infoValues,
|
|
||||||
live: {
|
|
||||||
type: "airtime",
|
|
||||||
duration: 2000,
|
|
||||||
over: {
|
|
||||||
action: "reload:content"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
var createContentItems = function(infoData) {
|
|
||||||
var items = [];
|
|
||||||
pushToArray(items, createControlItems(infoData));
|
|
||||||
pushToArray(items, createInfoItems(infoData));
|
|
||||||
return items;
|
|
||||||
};
|
|
||||||
var createContent = function(infoData) {
|
|
||||||
return {
|
|
||||||
cache: false,
|
|
||||||
compress: true,
|
|
||||||
type: "pages",
|
|
||||||
headline: "Tizen Player",
|
|
||||||
extension: "{ico:msx-white:timer} " + TVXDateTools.getTimestamp(),
|
|
||||||
pages: [{
|
|
||||||
items: createContentItems(infoData)
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var createWarningContent = function(playerInfo) {
|
|
||||||
return {
|
|
||||||
type: "pages",
|
|
||||||
headline: "Tizen Player",
|
|
||||||
pages: [{
|
|
||||||
items: [{
|
|
||||||
type: "default",
|
|
||||||
layout: "0,0,12,6",
|
|
||||||
color: "msx-glass",
|
|
||||||
headline: "{ico:msx-yellow:warning} Player Not Available",
|
|
||||||
text: "Tizen player is required for this plugin. Current player is: {txt:msx-white:" + playerInfo + "}."
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var createDummyData = function() {
|
|
||||||
return {
|
|
||||||
version: "1.0",
|
|
||||||
state: "PLAYING",
|
|
||||||
display: {
|
|
||||||
area: "0,0,1,1",
|
|
||||||
mode: "PLAYER_DISPLAY_MODE_LETTER_BOX"
|
|
||||||
},
|
|
||||||
buffer: {
|
|
||||||
timeout: 20,
|
|
||||||
size: {
|
|
||||||
init: 10,
|
|
||||||
resume: 10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stream: {
|
|
||||||
video: {
|
|
||||||
index: 0,
|
|
||||||
info: {
|
|
||||||
fourCC: "h264",
|
|
||||||
Width: 1280,
|
|
||||||
Height: 720,
|
|
||||||
Bit_rate: 128000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
this.handleRequest = function(playerInfo, dataId, callback) {
|
|
||||||
if (dataId == "init") {
|
|
||||||
if (TVXTools.isFullStr(playerInfo) && (playerInfo == "tizen" || playerInfo.indexOf("tizen/") == 0)) {
|
|
||||||
TVXInteractionPlugin.requestPlayerResponse("tizen:info", function(data) {
|
|
||||||
callback(createContent(data.response != null && data.response.tizen != null ? data.response.tizen.info : null));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(createWarningContent(playerInfo));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (dataId == "dummy") {
|
|
||||||
callback(createContent(createDummyData()));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
//TizenHandler
|
|
||||||
/******************************************************************************/
|
|
||||||
function TizenHandler() {
|
|
||||||
var playerInfo = null;
|
|
||||||
var readyService = new TVXBusyService();
|
|
||||||
var player = new TizenPlayer();
|
|
||||||
|
|
||||||
this.ready = function() {
|
|
||||||
readyService.start();
|
|
||||||
TVXInteractionPlugin.requestData("info:base", function(data) {
|
|
||||||
playerInfo = TVXTools.strFullCheck(data.info != null ? data.info.player : null, "unknown");
|
|
||||||
readyService.stop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
this.handleRequest = function(dataId, data, callback) {
|
|
||||||
readyService.onReady(function() {
|
|
||||||
if (!player.handleRequest(playerInfo, dataId, callback)) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
//Setup
|
|
||||||
/******************************************************************************/
|
|
||||||
window.onload = function() {
|
|
||||||
TVXInteractionPlugin.setupHandler(new TizenHandler());
|
|
||||||
TVXInteractionPlugin.init();
|
|
||||||
};
|
|
||||||
/******************************************************************************/
|
|
||||||
@@ -8,12 +8,12 @@ func SetupRoute(route *gin.RouterGroup) {
|
|||||||
route.GET("/msx/playlist", msxPlaylist)
|
route.GET("/msx/playlist", msxPlaylist)
|
||||||
route.GET("/msx/playlist/*fname", msxPlaylist)
|
route.GET("/msx/playlist/*fname", msxPlaylist)
|
||||||
|
|
||||||
route.GET("/msx/tizen.html", func(c *gin.Context) {
|
route.GET("/msx/html5x.html", func(c *gin.Context) {
|
||||||
c.Data(200, "text/html; charset=utf-8", Msxtizenhtml)
|
c.Data(200, "text/html; charset=utf-8", Msxhtml5xhtml)
|
||||||
})
|
})
|
||||||
|
|
||||||
route.GET("/msx/tizen.js", func(c *gin.Context) {
|
route.GET("/msx/tizen.html", func(c *gin.Context) {
|
||||||
c.Data(200, "text/javascript; charset=utf-8", Msxtizenjs)
|
c.Data(200, "text/html; charset=utf-8", Msxtizenhtml)
|
||||||
})
|
})
|
||||||
|
|
||||||
route.GET("/msx/tvx-plugin.min.js", func(c *gin.Context) {
|
route.GET("/msx/tvx-plugin.min.js", func(c *gin.Context) {
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ package web
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
|
||||||
"github.com/gin-contrib/location"
|
"github.com/gin-contrib/location"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"server/dlna"
|
"server/dlna"
|
||||||
"server/settings"
|
"server/settings"
|
||||||
|
"server/web/cors"
|
||||||
"server/web/msx"
|
"server/web/msx"
|
||||||
|
|
||||||
"server/log"
|
"server/log"
|
||||||
@@ -39,7 +40,9 @@ func Start(port string) {
|
|||||||
|
|
||||||
corsCfg := cors.DefaultConfig()
|
corsCfg := cors.DefaultConfig()
|
||||||
corsCfg.AllowAllOrigins = true
|
corsCfg.AllowAllOrigins = true
|
||||||
corsCfg.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "X-Requested-With", "Accept", "Authorization"}
|
corsCfg.AllowHeaders = []string{"*"}
|
||||||
|
corsCfg.AllowMethods = []string{"*"}
|
||||||
|
corsCfg.AllowPrivateNetwork = true
|
||||||
|
|
||||||
route := gin.New()
|
route := gin.New()
|
||||||
route.Use(log.WebLogger(), blocker.Blocker(), gin.Recovery(), cors.New(corsCfg), location.Default())
|
route.Use(log.WebLogger(), blocker.Blocker(), gin.Recovery(), cors.New(corsCfg), location.Default())
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export default function AboutDialog() {
|
|||||||
<LinkComponent name='Daniel Shleifman' link='https://github.com/dancheskus' />
|
<LinkComponent name='Daniel Shleifman' link='https://github.com/dancheskus' />
|
||||||
<LinkComponent name='Matt Joiner' link='https://github.com/anacrolix' />
|
<LinkComponent name='Matt Joiner' link='https://github.com/anacrolix' />
|
||||||
<LinkComponent name='nikk' link='https://github.com/tsynik' />
|
<LinkComponent name='nikk' link='https://github.com/tsynik' />
|
||||||
|
<LinkComponent name='kolsys' link='https://github.com/kolsys' />
|
||||||
<LinkComponent name='tw1cker Руслан Пахнев' link='https://github.com/Nemiroff' />
|
<LinkComponent name='tw1cker Руслан Пахнев' link='https://github.com/Nemiroff' />
|
||||||
<LinkComponent name='SpAwN_LMG' link='https://github.com/spawnlmg' />
|
<LinkComponent name='SpAwN_LMG' link='https://github.com/spawnlmg' />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user