@zsukim/ctv-run 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/platforms/tizen.js +33 -19
- package/dist/platforms/webos.js +41 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
| `appId` | | Tizen 앱 고유 ID (없으면 자동 생성) |
|
|
91
91
|
| `tizenPath` | | tizen CLI 실행 경로 (기본값: `tizen`) |
|
|
92
92
|
| `sdbPath` | | sdb 실행 경로 (기본값: `sdb`) |
|
|
93
|
+
| `configXmlPath` | | 사용할 config.xml 경로 (우선순위: dist > 지정경로 > 루트 > 기본 생성) |
|
|
93
94
|
|
|
94
95
|
**WebOS**
|
|
95
96
|
|
|
@@ -99,6 +100,7 @@
|
|
|
99
100
|
| `url` | | TV가 로드할 URL |
|
|
100
101
|
| `port` | | TV가 로드할 URL의 포트 |
|
|
101
102
|
| `appId` | | webOS 앱 ID (기본값: `com.ctvrun.app`) |
|
|
103
|
+
| `appInfoPath` | | 사용할 appinfo.json 경로 (우선순위: dist > 지정경로 > 루트 > 기본 생성) |
|
|
102
104
|
|
|
103
105
|
**Common**
|
|
104
106
|
|
package/dist/platforms/tizen.js
CHANGED
|
@@ -19,9 +19,13 @@ const path_1 = __importDefault(require("path"));
|
|
|
19
19
|
const config_1 = require("../utils/config");
|
|
20
20
|
const ip_1 = require("../utils/ip");
|
|
21
21
|
const platform_1 = require("../utils/platform");
|
|
22
|
+
const DEFAULT_APP_ID = 'A123456789.CtvRunApp';
|
|
23
|
+
const DEFAULT_APP_NAME = 'CtvRunApp';
|
|
24
|
+
const DEFAULT_CERT_FILE = 'ctv-run-cert';
|
|
25
|
+
const DEFAULT_CERT_PROFILE = 'ctv-run-profile';
|
|
22
26
|
function runTizen() {
|
|
23
27
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
28
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
25
29
|
const config = (0, config_1.loadConfig)();
|
|
26
30
|
const args = process.argv.slice(2);
|
|
27
31
|
const getArg = (0, platform_1.makeArgParser)(args);
|
|
@@ -79,10 +83,11 @@ function runTizen() {
|
|
|
79
83
|
const appId = getArg('appId') ||
|
|
80
84
|
(xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appId) ||
|
|
81
85
|
((_g = config.tizen) === null || _g === void 0 ? void 0 : _g.appId) ||
|
|
82
|
-
|
|
83
|
-
const safeAppName = ((xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appName) ||
|
|
86
|
+
DEFAULT_APP_ID;
|
|
87
|
+
const safeAppName = ((xmlInfo === null || xmlInfo === void 0 ? void 0 : xmlInfo.appName) || DEFAULT_APP_NAME).replace(/\s+/g, '');
|
|
84
88
|
// static mode는 dist의 config.xml 유지, live mode는 개발서버 URL로 새로 생성
|
|
85
|
-
|
|
89
|
+
const configXmlPath = getArg('configXmlPath') || ((_h = config.tizen) === null || _h === void 0 ? void 0 : _h.configXmlPath);
|
|
90
|
+
yield ensureTizenFiles(appId, tempAppDir, safeAppName, !isLiveMode, isLiveMode ? appUrl : '', configXmlPath);
|
|
86
91
|
// Tizen 인증서 프로필 확인/생성/활성화
|
|
87
92
|
console.log('\n🎫 Tizen 인증서 프로필 확인 중...');
|
|
88
93
|
const profilesXmlPath = path_1.default.join(process.env.HOME || '', 'tizen-studio-data/profile/profiles.xml');
|
|
@@ -122,7 +127,7 @@ function runTizen() {
|
|
|
122
127
|
existingProfiles.forEach((p, i) => {
|
|
123
128
|
console.log(` ${i + 1}. ${p}`);
|
|
124
129
|
});
|
|
125
|
-
const answer = askQuestion(
|
|
130
|
+
const answer = askQuestion(`🔑 사용할 프로필 번호를 입력하세요 (새로운 앱 전용 인증서 ${DEFAULT_CERT_FILE} 생성): `);
|
|
126
131
|
const idx = parseInt(answer, 10) - 1;
|
|
127
132
|
if (!isNaN(idx) && idx >= 0 && idx < existingProfiles.length) {
|
|
128
133
|
const chosen = existingProfiles[idx];
|
|
@@ -132,8 +137,8 @@ function runTizen() {
|
|
|
132
137
|
}
|
|
133
138
|
if (!activeProfile) {
|
|
134
139
|
console.log('⚠️ 활성화된 프로필이 없습니다. 앱 전용 인증서를 세팅합니다.');
|
|
135
|
-
const certFileName =
|
|
136
|
-
const profileName =
|
|
140
|
+
const certFileName = DEFAULT_CERT_FILE;
|
|
141
|
+
const profileName = DEFAULT_CERT_PROFILE;
|
|
137
142
|
const homeDir = process.env.HOME || '';
|
|
138
143
|
const defaultCertDir = path_1.default.join(homeDir, 'tizen-studio-data/keystore/author');
|
|
139
144
|
const p12Path = path_1.default.join(defaultCertDir, `${certFileName}.p12`);
|
|
@@ -232,21 +237,28 @@ function getInfoFromXml(dir) {
|
|
|
232
237
|
return null;
|
|
233
238
|
}
|
|
234
239
|
function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
|
|
235
|
-
return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '') {
|
|
236
|
-
const
|
|
237
|
-
if (keepExistingConfig && fs_extra_1.default.existsSync(
|
|
240
|
+
return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '', specifiedPath) {
|
|
241
|
+
const destConfigXml = path_1.default.join(tempDir, 'config.xml');
|
|
242
|
+
if (keepExistingConfig && fs_extra_1.default.existsSync(destConfigXml)) {
|
|
238
243
|
// static mode: dist의 config.xml을 그대로 유지
|
|
239
244
|
(0, platform_1.resolveIcon)(tempDir);
|
|
240
245
|
return;
|
|
241
246
|
}
|
|
242
|
-
//
|
|
243
|
-
if (fs_extra_1.default.existsSync(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
// config.xml 없으면 지정경로 → 루트 → 기본값 (우선순위: dist > 지정경로 > 루트 > 기본값)
|
|
248
|
+
if (!fs_extra_1.default.existsSync(destConfigXml)) {
|
|
249
|
+
const candidates = [
|
|
250
|
+
specifiedPath && path_1.default.resolve(process.cwd(), specifiedPath),
|
|
251
|
+
path_1.default.join(process.cwd(), 'config.xml'),
|
|
252
|
+
].filter(Boolean);
|
|
253
|
+
const found = candidates.find((p) => fs_extra_1.default.existsSync(p));
|
|
254
|
+
if (found) {
|
|
255
|
+
fs_extra_1.default.copyFileSync(found, destConfigXml);
|
|
256
|
+
console.log(`📝 config.xml 복사: ${found}`);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
console.log(`📝 config.xml 생성 중... (Name: ${appName}, ID: ${appId})`);
|
|
260
|
+
const packageId = appId.split('.')[0] || 'TizenApp';
|
|
261
|
+
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
250
262
|
<widget xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets" id="http://yourdomain/tizen/${appName}" version="1.0.0" viewmodes="maximized">
|
|
251
263
|
<tizen:application id="${appId}" package="${packageId}" required_version="2.1"/>
|
|
252
264
|
<content src="index.html"/>
|
|
@@ -260,7 +272,9 @@ function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
|
|
|
260
272
|
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice" />
|
|
261
273
|
<tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
|
|
262
274
|
</widget>`;
|
|
263
|
-
|
|
275
|
+
fs_extra_1.default.writeFileSync(destConfigXml, xmlContent, 'utf8');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
264
278
|
if (liveUrl) {
|
|
265
279
|
const redirectHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Loading...</title><meta http-equiv="refresh" content="0; url=${liveUrl}"></head><body style="background:#000;"></body></html>`;
|
|
266
280
|
fs_extra_1.default.writeFileSync(path_1.default.join(tempDir, 'index.html'), redirectHtml);
|
package/dist/platforms/webos.js
CHANGED
|
@@ -19,20 +19,23 @@ const path_1 = __importDefault(require("path"));
|
|
|
19
19
|
const config_1 = require("../utils/config");
|
|
20
20
|
const ip_1 = require("../utils/ip");
|
|
21
21
|
const platform_1 = require("../utils/platform");
|
|
22
|
+
const DEFAULT_APP_ID = 'com.ctvrun.app';
|
|
22
23
|
function runWebOS() {
|
|
23
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
var _a, _b, _c, _d, _e;
|
|
25
|
+
var _a, _b, _c, _d, _e, _f;
|
|
25
26
|
const config = (0, config_1.loadConfig)();
|
|
26
27
|
const args = process.argv.slice(2);
|
|
27
28
|
const getArg = (0, platform_1.makeArgParser)(args);
|
|
28
29
|
// 기본 설정값 로드
|
|
29
30
|
const deviceName = getArg('device') || ((_a = config.webos) === null || _a === void 0 ? void 0 : _a.deviceName);
|
|
30
|
-
const appId = getArg('appId') || ((_b = config.webos) === null || _b === void 0 ? void 0 : _b.appId) ||
|
|
31
|
+
const appId = getArg('appId') || ((_b = config.webos) === null || _b === void 0 ? void 0 : _b.appId) || DEFAULT_APP_ID;
|
|
32
|
+
const appInfoPath = getArg('appInfoPath') || ((_c = config.webos) === null || _c === void 0 ? void 0 : _c.appInfoPath);
|
|
31
33
|
const targetArg = getArg('target');
|
|
32
|
-
const port = getArg('port') ||
|
|
33
|
-
|
|
34
|
-
((_e = config.
|
|
35
|
-
|
|
34
|
+
const port = getArg('port') ||
|
|
35
|
+
((_d = config.webos) === null || _d === void 0 ? void 0 : _d.port) ||
|
|
36
|
+
((_e = config.common) === null || _e === void 0 ? void 0 : _e.devServerPort) ||
|
|
37
|
+
3000;
|
|
38
|
+
const appUrl = getArg('url') || ((_f = config.webos) === null || _f === void 0 ? void 0 : _f.url) || `http://${(0, ip_1.getLocalIp)()}:${port}`;
|
|
36
39
|
// 등록된 기기가 없는 경우
|
|
37
40
|
if (!deviceName) {
|
|
38
41
|
console.log('\n📺 연결된 WebOS TV 정보가 없습니다.');
|
|
@@ -97,12 +100,12 @@ function runWebOS() {
|
|
|
97
100
|
`;
|
|
98
101
|
fs_extra_1.default.writeFileSync(path_1.default.join(tempAppDir, 'index.html'), redirectHtml);
|
|
99
102
|
}
|
|
100
|
-
// appinfo.json 생성 (dist
|
|
101
|
-
yield ensureWebosAppInfo(appId, tempAppDir);
|
|
103
|
+
// appinfo.json 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
|
|
104
|
+
yield ensureWebosAppInfo(appId, tempAppDir, appInfoPath);
|
|
102
105
|
// 실제 appId를 appinfo.json에서 읽음 (dist에 있던 값 우선)
|
|
103
|
-
const
|
|
104
|
-
const resolvedAppId = fs_extra_1.default.existsSync(
|
|
105
|
-
? fs_extra_1.default.readJsonSync(
|
|
106
|
+
const resolvedAppInfoPath = path_1.default.join(tempAppDir, 'appinfo.json');
|
|
107
|
+
const resolvedAppId = fs_extra_1.default.existsSync(resolvedAppInfoPath)
|
|
108
|
+
? fs_extra_1.default.readJsonSync(resolvedAppInfoPath).id || appId
|
|
106
109
|
: appId;
|
|
107
110
|
// 아이콘
|
|
108
111
|
(0, platform_1.resolveIcon)(tempAppDir);
|
|
@@ -120,7 +123,9 @@ function runWebOS() {
|
|
|
120
123
|
});
|
|
121
124
|
// 런치
|
|
122
125
|
console.log(`▶️ Launching App...`);
|
|
123
|
-
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${resolvedAppId}`, {
|
|
126
|
+
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${resolvedAppId}`, {
|
|
127
|
+
stdio: 'inherit',
|
|
128
|
+
});
|
|
124
129
|
// 파일 정리
|
|
125
130
|
fs_extra_1.default.removeSync(path_1.default.resolve(process.cwd(), ipkFile));
|
|
126
131
|
fs_extra_1.default.removeSync(tempAppDir);
|
|
@@ -132,26 +137,31 @@ function runWebOS() {
|
|
|
132
137
|
}
|
|
133
138
|
});
|
|
134
139
|
}
|
|
135
|
-
// appinfo.json이 없으면 자동 생성
|
|
136
|
-
function ensureWebosAppInfo(appId, tempDir) {
|
|
140
|
+
// appinfo.json이 없으면 자동 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
|
|
141
|
+
function ensureWebosAppInfo(appId, tempDir, specifiedPath) {
|
|
137
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
title: 'CTV-Run-App',
|
|
151
|
-
icon: 'icon.png',
|
|
152
|
-
uiRevision: 2,
|
|
153
|
-
};
|
|
154
|
-
fs_extra_1.default.writeJsonSync(appInfoPath, defaultAppInfo, { spaces: 2 });
|
|
143
|
+
const destPath = path_1.default.join(tempDir, 'appinfo.json');
|
|
144
|
+
if (fs_extra_1.default.existsSync(destPath))
|
|
145
|
+
return;
|
|
146
|
+
const candidates = [
|
|
147
|
+
specifiedPath && path_1.default.resolve(process.cwd(), specifiedPath),
|
|
148
|
+
path_1.default.resolve(process.cwd(), 'appinfo.json'),
|
|
149
|
+
].filter(Boolean);
|
|
150
|
+
for (const candidate of candidates) {
|
|
151
|
+
if (fs_extra_1.default.existsSync(candidate)) {
|
|
152
|
+
fs_extra_1.default.copySync(candidate, destPath);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
155
|
}
|
|
156
|
+
fs_extra_1.default.writeJsonSync(destPath, {
|
|
157
|
+
id: appId,
|
|
158
|
+
version: '1.0.0',
|
|
159
|
+
vendor: 'CtvRun',
|
|
160
|
+
type: 'web',
|
|
161
|
+
main: 'index.html',
|
|
162
|
+
title: 'CTV-Run-App',
|
|
163
|
+
icon: 'icon.png',
|
|
164
|
+
uiRevision: 2,
|
|
165
|
+
}, { spaces: 2 });
|
|
156
166
|
});
|
|
157
167
|
}
|