diff --git a/.github/assets/profiles.xml b/.github/assets/profiles.xml
new file mode 100644
index 0000000..b5a3e6c
--- /dev/null
+++ b/.github/assets/profiles.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.github/workflows/build-new-release.yaml b/.github/workflows/build-new-release.yaml
index 4fb4ab3..46e6b3a 100644
--- a/.github/workflows/build-new-release.yaml
+++ b/.github/workflows/build-new-release.yaml
@@ -6,7 +6,7 @@ on:
- 'v*.*.*'
env:
- TIZEN_STUDIO_VER: 4.5.1
+ TIZEN_STUDIO_VER: 5.6
jobs:
build:
@@ -18,7 +18,7 @@ jobs:
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- - name: Install modules and build service
+ - name: Install modules and build standalone service
working-directory: tizenbrew-app/TizenBrew/service
run: |
sudo apt install -y expect zip
@@ -28,6 +28,13 @@ jobs:
ncc build service.js
rm -r node_modules
+ - name: Install modules and build updater service
+ working-directory: tizenbrew-updater/TizenBrewUpdater/service
+ run: |
+ npm install
+ ncc build service.js
+ rm -r node_modules
+
- name: Download Tizen-Studio
run: |
curl -o tizen-installer "https://download.tizen.org/sdk/Installer/tizen-studio_${TIZEN_STUDIO_VER}/web-cli_Tizen_Studio_${TIZEN_STUDIO_VER}_ubuntu-64.bin"
@@ -41,50 +48,107 @@ jobs:
- name: Prepare Tizen Certificate
run: |
- ./tizen-studio/tools/ide/bin/tizen certificate -a TizenBrew -p 1234 -c NZ -s Aukland -ct Aukland -o Tizen -n TizenBrew -e reis@tizentube.live -f tizencert
- ./tizen-studio/tools/ide/bin/tizen security-profiles add -n TizenBrew -a "${GITHUB_WORKSPACE}/tizen-studio-data/keystore/author/tizencert.p12" -p 1234
+ ${{ secrets.TIZEN_AUTHOR_KEY }} | base64 -d > "${GITHUB_WORKSPACE}/tizen-studio-data/keystore/author/tizenbrew-author.p12"
./tizen-studio/tools/ide/bin/tizen cli-config "profiles.path=${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml"
+ cp .github/assets/profiles.xml "${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml"
+ sed -i "s|\$GITHUB_WORKSPACE|${GITHUB_WORKSPACE}|g" "${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml"
+ sed -i "s|\$KEY_PW|${{ secrets.TIZEN_AUTHOR_KEY_PW }}|g" "${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml"
chmod 755 "./tizen-studio-data/profile/profiles.xml"
- - name: Build
+ - name: Build TizenBrew
working-directory: tizenbrew-app/TizenBrew
run: |
${GITHUB_WORKSPACE}/tizen-studio/tools/ide/bin/tizen build-web -e ".*" -e "node_modules/*" -e "package*.json" -e "yarn.lock"
- - name: Prepare for password prompt
+ - name: Build TizenBrew Updater
+ working-directory: tizenbrew-updater/TizenBrewUpdater
run: |
- sed -i "s|${GITHUB_WORKSPACE}/tizen-studio-data/keystore/author/tizencert.pwd|1234|g" ${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml
- sed -i "s|${GITHUB_WORKSPACE}/tizen-studio-data/tools/certificate-generator/certificates/distributor/tizen-distributor-signer.pwd|tizenpkcs12passfordsigner|g" ${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml
- sed -i 's|password=""|password="tizenpkcs12passfordsigner"|g' ${GITHUB_WORKSPACE}/tizen-studio-data/profile/profiles.xml
-
- - name: Package WGT
+ ${GITHUB_WORKSPACE}/tizen-studio/tools/ide/bin/tizen build-web -e ".*" -e "node_modules/*" -e "package*.json" -e "yarn.lock"
+
+ - name: Package TizenBrew for Old Tizen
+ env:
+ PASSWORD: ${{ secrets.TIZEN_AUTHOR_KEY_PW }}
+ APP_PATH: tizenbrew-app/TizenBrew
+ CERT: TizenBrew-Old
+ run: |
+ expect ./package.exp
+ mv tizenbrew-app/TizenBrew/release/TizenBrewStandalone.wgt tizenbrew-app/TizenBrew/release/TizenBrewStandalone-Old.wgt
+
+ - name: Package TizenBrew for New Tizen
+ env:
+ PASSWORD: ${{ secrets.TIZEN_AUTHOR_KEY_PW }}
+ APP_PATH: tizenbrew-app/TizenBrew
+ CERT: TizenBrew-New
+ run: |
+ expect ./package.exp
+ mv tizenbrew-app/TizenBrew/release/TizenBrewStandalone.wgt tizenbrew-app/TizenBrew/release/TizenBrewStandalone-New.wgt
+
+ - name: Package TizenBrew Updater for Old Tizen
+ env:
+ PASSWORD: ${{ secrets.TIZEN_AUTHOR_KEY_PW }}
+ APP_PATH: tizenbrew-updater/TizenBrewUpdater
+ CERT: TizenBrew-Old
+ run: |
+ expect ./package.exp
+ mv tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater.wgt tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-Old.wgt
+
+ - name: Package TizenBrew Updater for New Tizen
+ env:
+ PASSWORD: ${{ secrets.TIZEN_AUTHOR_KEY_PW }}
+ APP_PATH: tizenbrew-updater/TizenBrewUpdater
+ CERT: TizenBrew-New
run: |
expect ./package.exp
+ mv tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater.wgt tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-New.wgt
- - name: Package WGT as USB Demo Package
+ - name: Package TizenBrew as USB Demo Package
run: |
- wgt-to-usb tizenbrew-app/TizenBrew/release/TizenBrewStandalone.wgt
+ wgt-to-usb tizenbrew-app/TizenBrew/release/TizenBrewStandalone-Old.wgt
- - name: Upload Tizen package artifact
+ - name: Package Updater as USB Demo Package
+ run: |
+ wgt-to-usb tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-Old.wgt
+
+ - name: Upload TizenBrew package artifact for Old Tizen
+ uses: actions/upload-artifact@v2
+ with:
+ name: app-${{ github.sha }}-old.wgt
+ path: tizenbrew-app/TizenBrew/release/TizenBrewStandalone-Old.wgt
+
+ - name: Upload TizenBrew package artifact for New Tizen
uses: actions/upload-artifact@v2
with:
- name: app-${{ github.sha }}.wgt
- path: tizenbrew-app/TizenBrew/release/TizenBrewStandalone.wgt
+ name: app-${{ github.sha }}-new.wgt
+ path: tizenbrew-app/TizenBrew/release/TizenBrewStandalone-New.wgt
- - name: Release Build Result
- uses: softprops/action-gh-release@v1
+ - name: Upload TizenBrew Updater package artifact for Old Tizen
+ uses: actions/upload-artifact@v2
with:
- tag_name: ${{ env.RELEASE_VERSION }}
- files: |
- tizenbrew-app/TizenBrew/release/TizenBrewStandalone.wgt
+ name: updater-${{ github.sha }}-old.wgt
+ path: tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-Old.wgt
+ - name: Upload TizenBrew Updater package artifact for New Tizen
+ uses: actions/upload-artifact@v2
+ with:
+ name: updater-${{ github.sha }}-new.wgt
+ path: tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-New.wgt
+
- name: Zip USB Demo Package
run: |
zip -r tizenbrew-app/TizenBrew/release/TizenBrewStandaloneUSBDemo.zip userwidget/
-
- - name: Release USB Demo Package
+
+ - name: Zip Updater USB Demo Package
+ run: |
+ zip -r tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdaterUSBDemo.zip userwidget/
+
+ - name: Release TizenBrew Build Results
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.RELEASE_VERSION }}
files: |
- tizenbrew-app/TizenBrew/release/TizenBrewStandaloneUSBDemo.zip
\ No newline at end of file
+ tizenbrew-app/TizenBrew/release/TizenBrewStandalone-Old.wgt
+ tizenbrew-app/TizenBrew/release/TizenBrewStandalone-New.wgt
+ tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-Old.wgt
+ tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdater-New.wgt
+ tizenbrew-app/TizenBrew/release/TizenBrewStandaloneUSBDemo.zip
+ tizenbrew-updater/TizenBrewUpdater/release/TizenBrewUpdaterUSBDemo.zip
\ No newline at end of file
diff --git a/docs/MODULES.md b/docs/MODULES.md
index 813b161..abcbb32 100644
--- a/docs/MODULES.md
+++ b/docs/MODULES.md
@@ -1,32 +1,42 @@
-# Making a module
+# Creating modules for TizenBrew
-There are two types of modules: apps and mods. The modules are normal NPM modules, but they only have files for the app or mods.
+There are three types of modules: applications, site modifications and services. The modules itself are normal NPM/Node modules but special.
-## Package.json format
+## Application Modules
-The format is pretty same as a normal package.json file, but with some differences. There are some properties that are required for the module to work.
+Application modules are simply web pages without any modifications. They are not installed on the TV, but are served from a server. The module should contain a `package.json` file with the following properties:
-`packageType`: The type of the module. Can be `app` or `mods`. Required.
+- `packageType`: The type of the module. Should be `app`.
+- `appName`: The name of the module (user friendly, like "TizenTube").
+- `appPath`: The path to the index.html file. (like `app/index.html`)
+- `keys`: The keys that should be registered using the [TVInputDevice](https://developer.samsung.com/smarttv/develop/api-references/tizen-web-device-api-references/tvinputdevice-api.html) API.
-`appName`: The name of the module (user friendly, like "TizenTube"). Required.
+## Site Modification Modules
-`appPath`: The path to the app folder. Required if `packageType` is `app`.
+Site modification modules are basically the same as application modules, but they are used to modify a website. The modifications are basically JavaScript code that is injected into a website. The module should contain a `package.json` file with the following properties:
-`websiteURL`: The URL of the website. Required if `packageType` is `mods`.
+- `packageType`: The type of the module. Should be `mods`
+- `appName`: The name of the module (user friendly, like "TizenTube").
+- `websiteURL`: The URL of the website.
+- `main`: The JavaScript file to be injected to the website.
+- `keys`: The keys that should be registered using the [TVInputDevice](https://developer.samsung.com/smarttv/develop/api-references/tizen-web-device-api-references/tvinputdevice-api.html) API.
+- `tizenAppId`: The Tizen application ID of the application that should be modified. Will be used if it exists.
-`main`: The main file of the mods module. Required if `packageType` is `mods`.
+## Service Modules
-`keys`: The keys that should be registered using the [TVInputDevice](https://developer.samsung.com/smarttv/develop/api-references/tizen-web-device-api-references/tvinputdevice-api.html) API. Not required.
+Service modules are ran in the background using Node.js. They can be used to interact with the TV in the background.
-Here's an example mods module:
-```json
-{
- "name": "@foxreis/tizentube",
- "appName": "TizenTube",
- "version": "1.0.0",
- "description": "TizenTube is an ad-free and sponsor-free solution for your favourite streaming website on your Tizen (Samsung) TVs.",
- "main": "userScript.js",
- "packageType": "mods",
- "websiteURL": "https://example.com"
-}
-```
\ No newline at end of file
+Service modules is also an application module, but with a special property called `serviceFile`. This property should contain the path to the main JavaScript file of the service.
+
+The module should contain a `package.json` file with the following properties:
+
+- `packageType`: The type of the module. Should be `service`.
+- `appName`: The name of the module (user friendly, like "TizenTube").
+- `serviceFile`: The main JavaScript file of the service.
+- `appPath`: The path to the index.html file. (like `app/index.html`)
+- `keys`: The keys that should be registered using the [TVInputDevice](https://developer.samsung.com/smarttv/develop/api-references/tizen-web-device-api-references/tvinputdevice-api.html) API.
+
+
+## Examples
+
+See [TizenTube](https://github.com/reisxd/TizenTube) for an example of an site modification module and [Jellyfin-Tizen](https://github.com/GlenLowland/jellyfin-tizen-npm-publish) for an example of an application module.
\ No newline at end of file
diff --git a/package.exp b/package.exp
index 4ec4590..fcabe7c 100644
--- a/package.exp
+++ b/package.exp
@@ -2,16 +2,8 @@
set timeout -1
-cd tizenbrew-app/TizenBrew
+cd $env(APP_PATH)
-spawn /home/runner/work/TizenBrew/TizenBrew/tizen-studio/tools/ide/bin/tizen package -t wgt -o ./release -s TizenBrew -- .buildResult
-
-expect "Author password:"
-
-send -- "1234\r"
-
-expect "Yes: (Y), No: (N) ?"
-
-send -- "N\r"
+spawn /home/runner/work/TizenBrew/TizenBrew/tizen-studio/tools/ide/bin/tizen package -t wgt -o ./release -s $env(CERT) -- .buildResult
expect eof
\ No newline at end of file
diff --git a/tizenbrew-app/TizenBrew/.settings/.jsdtscope b/tizenbrew-app/TizenBrew/.settings/.jsdtscope
index c487c06..8fe9d0c 100644
--- a/tizenbrew-app/TizenBrew/.settings/.jsdtscope
+++ b/tizenbrew-app/TizenBrew/.settings/.jsdtscope
@@ -1,17 +1,17 @@
-
-
+
-
+
-
+
-
+
+
diff --git a/tizenbrew-app/TizenBrew/.tproject b/tizenbrew-app/TizenBrew/.tproject
index acbcd01..f9fb40d 100644
--- a/tizenbrew-app/TizenBrew/.tproject
+++ b/tizenbrew-app/TizenBrew/.tproject
@@ -7,5 +7,6 @@
+
diff --git a/tizenbrew-app/TizenBrew/config.xml b/tizenbrew-app/TizenBrew/config.xml
index 3c6173f..22a1924 100644
--- a/tizenbrew-app/TizenBrew/config.xml
+++ b/tizenbrew-app/TizenBrew/config.xml
@@ -1,40 +1,40 @@
-
-
-
-
-
-
-
- Reis Can
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TizenBrewStandalone
-
-
-
-
- TizenBrewService
- TizenBrewService
-
+
+
+
+
+
+
+
+ Reis Can
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TizenBrewStandalone
+
+
+
+
+
+
+
+
+
+
+
+
+ TizenBrewService
+ TizenBrewService
+
+
\ No newline at end of file
diff --git a/tizenbrew-app/TizenBrew/index.html b/tizenbrew-app/TizenBrew/index.html
index b9962ed..bc64f1f 100644
--- a/tizenbrew-app/TizenBrew/index.html
+++ b/tizenbrew-app/TizenBrew/index.html
@@ -110,6 +110,10 @@
var packageName = selectedItem.getAttribute("data-packagename");
var appPath = selectedItem.getAttribute("data-appPath");
+
+ var packageType = selectedItem.getAttribute("data-packageType");
+
+ var moduleType = selectedItem.getAttribute("data-moduleType");
if (!canLaunchModules) {
alert("You can't launch modules while the service hasn't connected yet.");
@@ -117,7 +121,10 @@
break;
}
- window.send({ type: "launch", packageName });
+ if (packageType === 'service') {
+ window.send({ type: "startService", package: { name: packageName, type: moduleType } });
+ }
+ window.send({ type: "launch", package: { name: packageName, type: moduleType }, isTizen3, tvIp: webapis.network.getIp() });
var keys = selectedItem.getAttribute("data-keys");
if (appPath.startsWith("http")) {
if (keys.length > 0) {
@@ -127,7 +134,9 @@
}
}
setTimeout(() => {
- location.href = appPath;
+ if (selectedItem.getAttribute('data-moddedTizenApp') === 'false') {
+ location.href = appPath;
+ }
}, 250);
}
diff --git a/tizenbrew-app/TizenBrew/js/index.js b/tizenbrew-app/TizenBrew/js/index.js
index 50408a9..43e8b24 100644
--- a/tizenbrew-app/TizenBrew/js/index.js
+++ b/tizenbrew-app/TizenBrew/js/index.js
@@ -2,7 +2,7 @@
window.begin = function() {
// Check if IP was set
localStorage.setItem("ip", "127.0.0.1");
- if (localStorage.getItem('modules') == null) localStorage.setItem('modules', '[]');
+ if (localStorage.getItem('modules') == null) localStorage.setItem('modules', '[{ "name": "@foxreis/tizentube", "type": "npm" }]');
if (localStorage.getItem('failedStartupAttempts') == null) localStorage.setItem('failedStartupAttempts', '0');
connect();
}
\ No newline at end of file
diff --git a/tizenbrew-app/TizenBrew/js/wsClient.js b/tizenbrew-app/TizenBrew/js/wsClient.js
index 0754ce9..1b27b1f 100644
--- a/tizenbrew-app/TizenBrew/js/wsClient.js
+++ b/tizenbrew-app/TizenBrew/js/wsClient.js
@@ -39,6 +39,10 @@ function onMessage(msg) {
localStorage.setItem('failedStartupAttempts', '0');
canLaunchModules = message.inDebug.webDebug;
send({ type: 'loadModules', modules: JSON.parse(localStorage.getItem('modules')) });
+ if (localStorage.getItem('autoLaunchService')) {
+ send({ type: 'startService', packageName: localStorage.getItem('autoLaunchService') });
+ }
+ send({ type: 'getServiceStatuses' });
} else {
send({ type: 'canLaunchInDebug' });
/*
@@ -60,7 +64,7 @@ function onMessage(msg) {
document.getElementById('appList').innerHTML = '';
for (const module of message.modules) {
document.getElementById('appList').innerHTML += `
-
+
${module.appName}
@@ -92,9 +96,10 @@ function onMessage(msg) {
document.getElementById('wsText').innerText = 'Connected to server.';
if (canAutoLaunch && localStorage.getItem('autoLaunch')) {
- const app = document.querySelector(`[data-packagename="${localStorage.getItem('autoLaunch')}"]`);
+ const autoLaunch = JSON.parse(localStorage.getItem('autoLaunch'));
+ const app = document.querySelector(`[data-packagename="${autoLaunch.name}"]`);
if (!app) {
- showError(`Error: Could not find the module ${localStorage.getItem('autoLaunch')}.`);
+ showError(`Error: Could not find the module ${autoLaunch.name}.`);
return;
} else {
const appPath = app.getAttribute('data-appPath');
@@ -105,10 +110,24 @@ function onMessage(msg) {
tizen.tvinputdevice.registerKey(keys[i]);
}
}
+ var packageType = selectedItem.getAttribute("data-packageType");
+
+ var moduleType = selectedItem.getAttribute("data-moduleType");
+
+ if (!canLaunchModules) {
+ alert("You can't launch modules while the service hasn't connected yet.");
+
+ break;
+ }
+ if (packageType === 'service') {
+ send({ type: "startService", package: autoLaunch });
+ }
setTimeout(() => {
- send({ type: 'launch', packageName: localStorage.getItem('autoLaunch') });
- location.href = appPath;
+ send({ type: 'launch', package: autoLaunch, isTizen3, tvIp: webapis.network.getIp() });
+ if (app.getAttribute('data-moddedTizenApp') === 'false') {
+ location.href = appPath;
+ }
}, 250);
}
}
@@ -128,6 +147,13 @@ function onMessage(msg) {
}
break;
}
+ case 'serviceStatuses': {
+ const crashedServices = message.services.filter(service => service.hasCrashed);
+ if (crashedServices.length > 0) {
+ showError(`Error: The following services have crashed: ${crashedServices.map(service => service.name).join(', ')}`);
+ }
+ break;
+ }
default: {
// This should never happen.
}
diff --git a/tizenbrew-app/TizenBrew/moduleManager.html b/tizenbrew-app/TizenBrew/moduleManager.html
index 48a47ce..ffaeb63 100644
--- a/tizenbrew-app/TizenBrew/moduleManager.html
+++ b/tizenbrew-app/TizenBrew/moduleManager.html
@@ -79,15 +79,16 @@
case 13:
shouldIgnore = true;
var packageName = selectedItem.getAttribute("data-packagename");
+ var packageType = selectedItem.getAttribute("data-packagetype");
if (packageName != null) {
var hasConfirmined = confirm(`Do you want to remove ${packageName}?`);
if (hasConfirmined) {
var modules = JSON.parse(localStorage.getItem("modules"));
- var newModules = modules.filter((module) => module != packageName);
+ var newModules = modules.filter((module) => module.name != packageName && module.type != packageType);
localStorage.setItem("modules", JSON.stringify(newModules));
location.reload();
}
- } else newModuleDialog();
+ } else newModuleDialog(packageType);
break;
case 404:
location.href = "/index.html";
@@ -95,7 +96,11 @@
case 65376:
var input = document.getElementById("appName");
var modules = JSON.parse(localStorage.getItem("modules"));
- modules.push(input.value);
+ var type = selectedItem.getAttribute("data-packagetype");
+ modules.push({
+ name: input.value,
+ type
+ });
localStorage.setItem("modules", JSON.stringify(modules));
location.reload();
break;
@@ -111,8 +116,8 @@
document.getElementById("appList").innerHTML = "";
for (const module of modules) {
document.getElementById("appList").innerHTML += `
-
-
${module}
+
+
${module.name}
`;
firstOne = false;
@@ -131,8 +136,13 @@
${module}
function addButton(primary) {
document.getElementById("appList").innerHTML += `
-
-
Add
+
+
Add NPM Module
+
This is primarily for production and for users.
+
+
+
Add GitHub Module
+
This is primarily for development and for developers.
`;
if (primary) {
@@ -141,12 +151,12 @@
Add
}
}
- function newModuleDialog() {
+ function newModuleDialog(type) {
document.getElementById("appList").innerHTML = "";
document.getElementById("appList").innerHTML += `
-