@zsukim/ctv-run 1.0.11 → 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 +35 -20
- package/dist/platforms/webos.js +45 -30
- 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`);
|
|
@@ -204,7 +209,8 @@ function runTizen() {
|
|
|
204
209
|
console.error(`\n❌ 오류 발생: ${err.message}`);
|
|
205
210
|
}
|
|
206
211
|
finally {
|
|
207
|
-
|
|
212
|
+
if (fs_extra_1.default.existsSync(tempAppDir))
|
|
213
|
+
fs_extra_1.default.removeSync(tempAppDir);
|
|
208
214
|
}
|
|
209
215
|
});
|
|
210
216
|
}
|
|
@@ -231,21 +237,28 @@ function getInfoFromXml(dir) {
|
|
|
231
237
|
return null;
|
|
232
238
|
}
|
|
233
239
|
function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
|
|
234
|
-
return __awaiter(this, arguments, void 0, function* (appId, tempDir, appName, keepExistingConfig = false, liveUrl = '') {
|
|
235
|
-
const
|
|
236
|
-
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)) {
|
|
237
243
|
// static mode: dist의 config.xml을 그대로 유지
|
|
238
244
|
(0, platform_1.resolveIcon)(tempDir);
|
|
239
245
|
return;
|
|
240
246
|
}
|
|
241
|
-
//
|
|
242
|
-
if (fs_extra_1.default.existsSync(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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"?>
|
|
249
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">
|
|
250
263
|
<tizen:application id="${appId}" package="${packageId}" required_version="2.1"/>
|
|
251
264
|
<content src="index.html"/>
|
|
@@ -259,7 +272,9 @@ function ensureTizenFiles(appId_1, tempDir_1, appName_1) {
|
|
|
259
272
|
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice" />
|
|
260
273
|
<tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto" hwkey-event="enable"/>
|
|
261
274
|
</widget>`;
|
|
262
|
-
|
|
275
|
+
fs_extra_1.default.writeFileSync(destConfigXml, xmlContent, 'utf8');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
263
278
|
if (liveUrl) {
|
|
264
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>`;
|
|
265
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 정보가 없습니다.');
|
|
@@ -70,8 +73,6 @@ function runWebOS() {
|
|
|
70
73
|
if (fs_extra_1.default.existsSync(tempAppDir))
|
|
71
74
|
fs_extra_1.default.removeSync(tempAppDir);
|
|
72
75
|
fs_extra_1.default.ensureDirSync(tempAppDir);
|
|
73
|
-
// appinfo.json 생성
|
|
74
|
-
yield ensureWebosAppInfo(appId, tempAppDir);
|
|
75
76
|
// 콘텐츠 모드 설정
|
|
76
77
|
if (targetArg) {
|
|
77
78
|
const distPath = path_1.default.resolve(process.cwd(), targetArg);
|
|
@@ -99,14 +100,21 @@ function runWebOS() {
|
|
|
99
100
|
`;
|
|
100
101
|
fs_extra_1.default.writeFileSync(path_1.default.join(tempAppDir, 'index.html'), redirectHtml);
|
|
101
102
|
}
|
|
103
|
+
// appinfo.json 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
|
|
104
|
+
yield ensureWebosAppInfo(appId, tempAppDir, appInfoPath);
|
|
105
|
+
// 실제 appId를 appinfo.json에서 읽음 (dist에 있던 값 우선)
|
|
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
|
|
109
|
+
: appId;
|
|
102
110
|
// 아이콘
|
|
103
111
|
(0, platform_1.resolveIcon)(tempAppDir);
|
|
104
112
|
// 패키징
|
|
105
|
-
console.log(`🔨 Packaging [${
|
|
113
|
+
console.log(`🔨 Packaging [${resolvedAppId}]...`);
|
|
106
114
|
(0, child_process_1.execSync)(`ares-package ${tempAppDir} -o .`, { stdio: 'inherit' });
|
|
107
115
|
const ipkFile = fs_extra_1.default
|
|
108
116
|
.readdirSync(process.cwd())
|
|
109
|
-
.find((f) => f.startsWith(
|
|
117
|
+
.find((f) => f.startsWith(resolvedAppId) && f.endsWith('.ipk'));
|
|
110
118
|
if (ipkFile) {
|
|
111
119
|
// 설치
|
|
112
120
|
console.log(`🚀 Installing to ${deviceName}...`);
|
|
@@ -115,7 +123,9 @@ function runWebOS() {
|
|
|
115
123
|
});
|
|
116
124
|
// 런치
|
|
117
125
|
console.log(`▶️ Launching App...`);
|
|
118
|
-
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${
|
|
126
|
+
(0, child_process_1.execSync)(`ares-launch -d ${deviceName} ${resolvedAppId}`, {
|
|
127
|
+
stdio: 'inherit',
|
|
128
|
+
});
|
|
119
129
|
// 파일 정리
|
|
120
130
|
fs_extra_1.default.removeSync(path_1.default.resolve(process.cwd(), ipkFile));
|
|
121
131
|
fs_extra_1.default.removeSync(tempAppDir);
|
|
@@ -127,26 +137,31 @@ function runWebOS() {
|
|
|
127
137
|
}
|
|
128
138
|
});
|
|
129
139
|
}
|
|
130
|
-
// appinfo.json이 없으면 자동 생성
|
|
131
|
-
function ensureWebosAppInfo(appId, tempDir) {
|
|
140
|
+
// appinfo.json이 없으면 자동 생성 (우선순위: dist > 지정경로 > 루트 > 기본값)
|
|
141
|
+
function ensureWebosAppInfo(appId, tempDir, specifiedPath) {
|
|
132
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
title: 'CTV-Run-App',
|
|
146
|
-
icon: 'icon.png',
|
|
147
|
-
uiRevision: 2,
|
|
148
|
-
};
|
|
149
|
-
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
|
+
}
|
|
150
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 });
|
|
151
166
|
});
|
|
152
167
|
}
|