first commit

This commit is contained in:
Dmitry Lyzo 2019-10-12 17:25:42 +03:00
commit ea271573c1
7 changed files with 365 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.wgt
www
.*

63
README.md Normal file
View File

@ -0,0 +1,63 @@
<h1 align="center">Jellyfin Tizen</h1>
<h3 align="center">Part of the <a href="https://jellyfin.media">Jellyfin Project</a></h3>
## Build Process
### Getting Started
1. Download and install Tizen Studio (<a href="https://developer.tizen.org/development/tizen-studio/download">https://developer.tizen.org/development/tizen-studio/download</a>).
2. Setup Samsung certificate (need Samsung account).
3. Clone or download this repository.
```sh
git clone https://github.com/jellyfin/jellyfin-tizen.git
```
4. Clone or download Jellyfin Web repository.
```sh
git clone https://github.com/jellyfin/jellyfin-web.git
```
5. Go to Jellyfin Tizen directory.
```sh
cd jellyfin-tizen
```
### Prepare Interface
If any changes are made to `jellyfin-web/`, the `www/` directory will need to be rebuilt using the following command.
```sh
JELLYFIN_WEB_DIR=../jellyfin-web/src npx gulp
```
> If `NODE_ENV=development` is set in the environment, then the source files will be copied without being minified.
> The `JELLYFIN_WEB_DIR` environment variable can be used to override the location of `jellyfin-web`.
### Build WGT
```sh
tizen build-web -e ".*" -e gulpfile.js
tizen package -t wgt -o . -- .buildResult
```
### Deploy to Emulator
1. Run emulator.
2. Install package.
```sh
tizen install -n *.wgt -t T-samsung-5.0-x86
```
> Specify target with `-t` option.
### Deploy to TV
1. Run TV.
2. Activate Developer Mode on TV (<a href="https://developer.samsung.com/tv/develop/getting-started/using-sdk/tv-device">https://developer.samsung.com/tv/develop/getting-started/using-sdk/tv-device</a>).
3. Connect to TV with Device Manager from Tizen Studio. Or with sdb.
```sh
sdb connect YOUR_TV_IP
```
4. Install package.
```sh
tizen install -n *.wgt -t UE65NU7400
```
> Specify target with `-t` option.

14
config.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets" id="http://jellyfin.org/Jellyfin" version="0.1.0" viewmodes="fullscreen">
<access origin="*" subdomains="true"></access>
<tizen:application id="AprZAARz4r.Jellyfin" package="AprZAARz4r" required_version="3.0"/>
<author href="http://jellyfin.org" email="apps@jellyfin.org">Jellyfin</author>
<content src="index.html"/>
<description>Jellyfin for Samsung Smart TV (Tizen).</description>
<feature name="http://tizen.org/feature/screen.size.all"/>
<icon src="icon.png"/>
<name>Jellyfin</name>
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice"/>
<tizen:profile name="tv"/>
<tizen:setting screen-orientation="auto-rotation" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
</widget>

100
gulpfile.js Normal file
View File

@ -0,0 +1,100 @@
var gulp = require('gulp');
var gulpif = require('gulp-if');
var del = require('del');
var dom = require('gulp-dom');
var uglifyes = require('uglify-es');
var composer = require('gulp-uglify/composer');
var uglify = composer(uglifyes, console);
// Check the NODE_ENV environment variable
var isDev = process.env.NODE_ENV === 'development';
// Allow overriding of jellyfin-web directory
var WEB_DIR = process.env.JELLYFIN_WEB_DIR || 'node_modules/jellyfin-web/dist';
console.info('Using jellyfin-web from', WEB_DIR);
// Skip minification for development builds or minified files
var compress = !isDev && [
'**/*',
'!**/*min.*',
'!**/*hls.js',
// Temporarily exclude apiclient until updated
'!bower_components/emby-apiclient/**/*.js'
];
var uglifyOptions = {
compress: {
drop_console: true
}
};
var paths = {
assets: {
src: [
WEB_DIR + '/**/*',
'!' + WEB_DIR + '/index.html'
],
dest: 'www/'
},
index: {
src: WEB_DIR + '/index.html',
dest: 'www/'
}
};
// Clean the www directory
function clean() {
return del([
'www'
]);
}
// Copy unmodified assets
function copy() {
return gulp.src(paths.assets.src)
.pipe(gulp.dest(paths.assets.dest));
}
// Add required tags to index.html
function modifyIndex() {
return gulp.src(paths.index.src)
.pipe(dom(function() {
// inject CSP meta tag
var meta = this.createElement('meta');
meta.setAttribute('http-equiv', 'Content-Security-Policy');
meta.setAttribute('content', 'default-src * \'self\' \'unsafe-inline\' \'unsafe-eval\' data: gap: file: filesystem: ws: wss:;');
this.head.appendChild(meta);
// inject appMode script
var appMode = this.createElement('script');
appMode.text = 'window.appMode=\'cordova\';';
this.body.appendChild(appMode);
// inject tizen.js
var tizen = this.createElement('script');
tizen.setAttribute('src', '../tizen.js');
tizen.setAttribute('defer', '');
this.body.appendChild(tizen);
// inject apploader.js
var apploader = this.createElement('script');
apploader.setAttribute('src', 'scripts/apploader.js');
apploader.setAttribute('defer', '');
this.body.appendChild(apploader);
return this;
}))
.pipe(gulp.dest(paths.index.dest))
}
// Default build task
var build = gulp.series(
clean,
gulp.parallel(copy, modifyIndex)
);
// Export tasks so they can be run individually
exports.clean = clean;
exports.copy = copy;
exports.modifyIndex = modifyIndex;
// Export default task
exports.default = build;

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

6
index.html Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0; url=www/index.html" />
</head>
</html>

179
tizen.js Normal file
View File

@ -0,0 +1,179 @@
'use strict';
console.log('Tizen adapter');
window.addEventListener('load', function() {
//console.log(JSON.stringify(tizen.tvinputdevice.getSupportedKeys()));
tizen.tvinputdevice.registerKey('MediaPlay');
tizen.tvinputdevice.registerKey('MediaTrackPrevious');
tizen.tvinputdevice.registerKey('MediaTrackNext');
tizen.tvinputdevice.registerKey('MediaRewind');
tizen.tvinputdevice.registerKey('MediaFastForward');
require(['inputManager', 'focusManager', 'viewManager', 'appRouter', 'actionsheet'], function(inputManager, focusManager, viewManager, appRouter, actionsheet) {
const commands = {
'10009': 'back',
'415': 'playpause',
'10232': 'previoustrack',
'10233': 'nexttrack',
'412': 'rewind',
'417': 'fastforward'
};
var isRestored;
var lastActiveElement;
var historyStartup;
var historyDepth = 0;
var exitPromise;
//document.addEventListener('keypress', function(e) {
// console.log('keypress');
//});
//document.addEventListener('keyup', function(e) {
// console.log('keyup');
//});
document.addEventListener('keydown', function(e) {
//console.log('keydown: keyCode: ' + e.keyCode + ' key: ' + e.key + ' location: ' + e.location);
var command = commands[e.keyCode];
if (command) {
//console.log('command: ' + command);
if (command === 'back' && historyDepth < 2 && !exitPromise) {
exitPromise = actionsheet.show({
title: Globalize.translate('Exit?'),
items: [
{id: 'yes', name: Globalize.translate('Yes')},
{id: 'no', name: Globalize.translate('No')}
]
}).then(function (value) {
exitPromise = null;
if (value === 'yes') {
try {
tizen.application.getCurrentApplication().exit();
} catch (ignore) {}
}
},
function () {
exitPromise = null;
});
return;
}
inputManager.trigger(command);
}
});
document.addEventListener('click', function() {
lastActiveElement = document.activeElement;
});
document.addEventListener('viewhide', function() {
lastActiveElement = document.activeElement;
});
function onPageLoad() {
console.debug('onPageLoad ' + window.location.href + ' isRestored=' + isRestored);
if (isRestored) {
return;
}
var view = viewManager.currentView() || document.body;
var element = lastActiveElement;
lastActiveElement = null;
// These elements are recreated
if (element) {
if (element.classList.contains('btnPreviousPage')) {
element = view.querySelector('.btnPreviousPage');
} else if (element.classList.contains('btnNextPage')) {
element = view.querySelector('.btnNextPage');
}
}
if (element && focusManager.isCurrentlyFocusable(element)) {
focusManager.focus(element);
} else {
element = focusManager.autoFocus(view);
}
}
// Starts listening for changes in the '.loading-spinner' HTML element
function installMutationObserver() {
var mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.debug(mutation.type);
if (mutation.target.classList.contains('hide')) {
onPageLoad();
}
});
});
var spinner = document.querySelector('.loading-spinner');
if (spinner) {
mutationObserver.observe(spinner, { attributes : true });
document.removeEventListener('viewshow', installMutationObserver);
}
}
document.addEventListener('viewshow', installMutationObserver);
window.addEventListener('pushState', function(e) {
// Reset history on some pages
var path = e.arguments && e.arguments[2] ? e.arguments[2] : '';
var pos = path.indexOf('?');
path = path.substring(0, pos !== -1 ? pos : path.length);
switch (path) {
case '#!/home.html':
if (!historyStartup || historyStartup !== path) {
historyStartup = path;
historyDepth = 0;
}
break;
case '#!/selectserver.html':
case '#!/login.html':
historyStartup = path;
historyDepth = 0;
break;
}
historyDepth++;
isRestored = false;
//console.log('history: ' + historyDepth + ', ' + historyStartup);
});
window.addEventListener('popstate', function() {
historyDepth--;
isRestored = true;
//console.log('history: ' + historyDepth + ', ' + historyStartup);
});
// Add 'pushState' and 'replaceState' events
var _wr = function(type) {
var orig = history[type];
return function() {
var rv = orig.apply(this, arguments);
var e = new Event(type);
e.arguments = arguments;
window.dispatchEvent(e);
return rv;
};
};
history.pushState = _wr('pushState');
history.replaceState = _wr('replaceState');
});
});