mirror of https://github.com/NobyDa/Script.git
Merge branch 'ipa-installer'
This commit is contained in:
commit
be5185e08c
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* IPA-installer JSBox script. This script is not available stand alone, checkout the demo from TG channel @NobyDa
|
||||||
|
*
|
||||||
|
* Modified from https://github.com/axelburks/JSBox/blob/master/IPA%20Installer.js by @NobyDa
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port_number = 8070
|
||||||
|
var plist_url = `itms-services://?action=download-manifest&url=https://nobyda.app/install%3Fclient%3Djsbox%26url%3Dhttp%253A%252F%252F127.0.0.1%253A${port_number}%252Fdownload%253Fpath%253D%25252Fapp.ipa`
|
||||||
|
|
||||||
|
$app.strings = {
|
||||||
|
"en": {
|
||||||
|
"starterror": "Not support running in this way",
|
||||||
|
"ftypeerror": " is not ipa file",
|
||||||
|
"installtitle": "Installing...",
|
||||||
|
"installmsg": "\n\nYou can check on Homescreen.\nPlease tap \"Done\" button after finished",
|
||||||
|
"inerrtitle": "IPA file import error",
|
||||||
|
"inerrmsg": "Please rerun the script"
|
||||||
|
},
|
||||||
|
"zh-Hans": {
|
||||||
|
"starterror": "不支持此方式运行!",
|
||||||
|
"ftypeerror": " 非 ipa 文件!",
|
||||||
|
"installtitle": "正在安装…",
|
||||||
|
"installmsg": "\n\n可前往桌面查看安装进度\n完成后请点击\"Done\"按钮",
|
||||||
|
"inerrtitle": "IPA文件导入失败",
|
||||||
|
"inerrmsg": "请重新运行此脚本"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从应用内启动
|
||||||
|
if ($app.env == $env.app) {
|
||||||
|
$drive.open({
|
||||||
|
handler: function(data) {
|
||||||
|
fileCheck(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 从 Action Entension 启动
|
||||||
|
else if ($app.env == $env.action) {
|
||||||
|
fileCheck($context.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
$ui.error($l10n("starterror"))
|
||||||
|
delayClose(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function startServer(port) {
|
||||||
|
$http.startServer({
|
||||||
|
port: port,
|
||||||
|
path: "",
|
||||||
|
handler: function(result) {
|
||||||
|
console.info(result.url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileCheck(data) {
|
||||||
|
if (data && data.fileName) {
|
||||||
|
var fileName = data.fileName;
|
||||||
|
if (fileName.indexOf(".ipa") == -1) {
|
||||||
|
$ui.error(fileName + $l10n("ftypeerror"))
|
||||||
|
delayClose(2)
|
||||||
|
} else {
|
||||||
|
install(fileName, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function install(fileName, file) {
|
||||||
|
var result = $file.write({
|
||||||
|
data: file,
|
||||||
|
path: "app.ipa"
|
||||||
|
})
|
||||||
|
if (result) {
|
||||||
|
startServer(port_number)
|
||||||
|
$location.startUpdates({
|
||||||
|
handler: function(resp) {
|
||||||
|
console.info(resp.lat + " " + resp.lng + " " + resp.alt)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var preResult = $app.openURL(plist_url);
|
||||||
|
if (preResult) {
|
||||||
|
$ui.alert({
|
||||||
|
title: $l10n("installtitle"),
|
||||||
|
message: "\n" + fileName + $l10n("installmsg"),
|
||||||
|
actions: [{
|
||||||
|
title: "Cancel",
|
||||||
|
style: "Cancel",
|
||||||
|
handler: function() {
|
||||||
|
$http.stopServer()
|
||||||
|
$file.delete("app.ipa")
|
||||||
|
delayClose(0.2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Done",
|
||||||
|
handler: function() {
|
||||||
|
$http.stopServer()
|
||||||
|
$file.delete("app.ipa")
|
||||||
|
delayClose(0.2)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
$ui.alert({
|
||||||
|
title: "Open itms-services scheme failed",
|
||||||
|
message: "Please rerun the script or restart device",
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
title: "OK",
|
||||||
|
handler: function() {
|
||||||
|
delayClose(0.2)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ui.alert({
|
||||||
|
title: $l10n("inerrtitle"),
|
||||||
|
message: $l10n("inerrmsg"),
|
||||||
|
actions: [{
|
||||||
|
title: "OK",
|
||||||
|
style: "Cancel",
|
||||||
|
handler: function() {
|
||||||
|
delayClose(0.2)
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function delayClose(time) {
|
||||||
|
$location.stopUpdates()
|
||||||
|
$thread.main({
|
||||||
|
delay: time,
|
||||||
|
handler: function() {
|
||||||
|
if ($app.env == $env.action || $app.env == $env.safari) {
|
||||||
|
$context.close()
|
||||||
|
}
|
||||||
|
$app.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
# IPA-installer pythonista script. This script is not available stand alone, checkout the demo from TG channel @NobyDa
|
||||||
|
#
|
||||||
|
# Modified from https://github.com/axelburks/Pythonista/blob/master/IPA%20Installer.py by @NobyDa
|
||||||
|
|
||||||
|
import os, appex, console, shutil, http.server, webbrowser, time
|
||||||
|
from os import path
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
port_number = 8090
|
||||||
|
plist_url = f'itms-services://?action=download-manifest&url=https://nobyda.app/install%3Fclient%3Dpythonista%26url%3Dhttp%253A%252F%252F127.0.0.1%253A{port_number}%252Fipa%252Fapp.ipa'
|
||||||
|
save_dir = path.expanduser('./ipa')
|
||||||
|
if not path.exists(save_dir):
|
||||||
|
os.makedirs(save_dir)
|
||||||
|
|
||||||
|
httpd = None
|
||||||
|
def startServer(port):
|
||||||
|
Handler = http.server.SimpleHTTPRequestHandler
|
||||||
|
|
||||||
|
global httpd
|
||||||
|
httpd = http.server.HTTPServer(("", port), Handler)
|
||||||
|
|
||||||
|
print("Start server at port", port)
|
||||||
|
httpd.serve_forever()
|
||||||
|
|
||||||
|
def start(port):
|
||||||
|
thread = Thread(target=startServer, args=[port])
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
startTime = int(time.time())
|
||||||
|
while not httpd:
|
||||||
|
if int(time.time()) > startTime + 60:
|
||||||
|
print("Time out")
|
||||||
|
break
|
||||||
|
return httpd
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
if httpd:
|
||||||
|
httpd.shutdown()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if appex.is_running_extension():
|
||||||
|
get_path = appex.get_file_path()
|
||||||
|
file_name = path.basename(get_path)
|
||||||
|
file_ext = path.splitext(file_name)[-1]
|
||||||
|
if file_ext == '.ipa':
|
||||||
|
dstpath = path.join(save_dir, 'app.ipa')
|
||||||
|
try:
|
||||||
|
shutil.copy(get_path, dstpath)
|
||||||
|
|
||||||
|
except Exception as eer:
|
||||||
|
print(eer)
|
||||||
|
console.hud_alert('导入失败!','error',1)
|
||||||
|
start(port_number)
|
||||||
|
if httpd:
|
||||||
|
webbrowser.open(plist_url)
|
||||||
|
try:
|
||||||
|
finish = console.alert(file_name, '\n正在安装...请返回桌面查看进度...\n\n安装完成后请返回点击已完成','已完成', hide_cancel_button=False)
|
||||||
|
if finish == 1:
|
||||||
|
stop()
|
||||||
|
shutil.rmtree('./ipa')
|
||||||
|
print("Server stopped")
|
||||||
|
except:
|
||||||
|
stop()
|
||||||
|
shutil.rmtree('./ipa')
|
||||||
|
print("Cancelled")
|
||||||
|
appex.finish()
|
||||||
|
else:
|
||||||
|
console.hud_alert('非 ipa 文件无法导入安装', 'error', 2)
|
||||||
|
appex.finish()
|
||||||
|
else:
|
||||||
|
console.hud_alert('请在分享扩展中打开本脚本','error',2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
* iOS IPA应用辅助安装脚本.
|
||||||
|
*
|
||||||
|
* 兼容: QuantumultX、Surge5,Loon、Shadowrocket、Stash
|
||||||
|
* 作者: @NobyDa
|
||||||
|
*
|
||||||
|
* 快捷指令 + Shu配合安装:
|
||||||
|
* 导入IPA文件至Shu -> Shu长按IPA文件 -> 导出文件 -> WiFi传输 -> 本机 -> 系统共享 -> 分享至IPA-Installer快捷指令
|
||||||
|
*
|
||||||
|
* 快捷指令 + JSBox/Pythonista配合安装:
|
||||||
|
* IPA文件长按分享至IPA-Installer快捷指令,完成后再分享至Jsbox/pythonista分享扩展.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* QuanX重写: https://raw.githubusercontent.com/NobyDa/Script/master/QuantumultX/IPA-Installer.snippet
|
||||||
|
*
|
||||||
|
* Surge模块: https://raw.githubusercontent.com/NobyDa/Script/master/Surge/Module/IPA_install.sgmodule
|
||||||
|
*
|
||||||
|
* loon插件: https://raw.githubusercontent.com/NobyDa/Script/master/Loon/Loon_IPA_Installer.plugin
|
||||||
|
*
|
||||||
|
* Stash覆写: https://raw.githubusercontent.com/NobyDa/Script/master/Stash/IPA-Installer.stoverride
|
||||||
|
*
|
||||||
|
* 快捷指令: https://www.icloud.com/shortcuts/a89979261837465aa3b4cae3527cf5f9
|
||||||
|
*
|
||||||
|
* JSBox脚本: https://xteko.com/redir?url=https%3A%2F%2Fraw.githubusercontent.com%2FNobyDa%2FScript%2Fmaster%2FIPA-Installer%2FIPA-Installer-JSBox.js&name=IPA%20Installer%20%28NobyDa%29
|
||||||
|
*
|
||||||
|
* Pythonista脚本: https://github.com/NobyDa/Script/blob/master/IPA-Installer/IPA-Installer-Pythonista.py
|
||||||
|
*/
|
||||||
|
|
||||||
|
const $ = new compatible_tool();
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
const args = urlArgs($request.url);
|
||||||
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>items</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>assets</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>kind</key>
|
||||||
|
<string>software-package</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>https://nobyda.app/download?url=${encodeURIComponent(args.url)}</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>metadata</key>
|
||||||
|
<dict>
|
||||||
|
<key>bundle-identifier</key>
|
||||||
|
<string>${args.bundleId || $.read("nobyda_ipa_bundle_id") || "*"}</string>
|
||||||
|
<key>bundle-version</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>kind</key>
|
||||||
|
<string>software</string>
|
||||||
|
<key>title</key>
|
||||||
|
<string>IPA</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>`;
|
||||||
|
if ($request.url.includes("/install?")) {
|
||||||
|
if (args.bundleId) {
|
||||||
|
$.write(args.bundleId, "nobyda_ipa_bundle_id");
|
||||||
|
};
|
||||||
|
$.resp = { response: { status: 200, body: args.client && plist || "{}" } };
|
||||||
|
} else {
|
||||||
|
if ($request.method == "GET") {
|
||||||
|
const size = await ipaSize(args.url);
|
||||||
|
$.notify(`IPA Installer`, ``, size && `Installing IPA, Size: ${size} MB` || `HTTP local server read failed!`);
|
||||||
|
}
|
||||||
|
$.resp = { response: { status: 307, headers: { Location: args.url }, body: "{}" } };
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
.catch((e) => $.notify(`IPA Installer`, ``, `ERROR: ${e.message || e}\nPATH: ${e.stack}`))
|
||||||
|
.finally(() => $.done($.resp))
|
||||||
|
|
||||||
|
|
||||||
|
function ipaSize(url) {
|
||||||
|
return new Promise((r, e) => {
|
||||||
|
$.http({ method: "head", url: url, policy: "DIRECT", }, (e, h, d) => {
|
||||||
|
r(h && h.status == 200 && `${((h.headers["Content-Length"] || 0) / 1000 / 1000).toFixed(2)}`)
|
||||||
|
});
|
||||||
|
setTimeout(() => r(), 1000)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function urlArgs(str) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
(str.startsWith("http") && str.split("?")[1] || str).split("&")
|
||||||
|
.map((item) => item.split("="))
|
||||||
|
.map(([k, v]) => [k, decodeURIComponent(v)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compatible_tool() {
|
||||||
|
const isSurge = typeof $httpClient != "undefined";
|
||||||
|
const isQuanX = typeof $task != "undefined";
|
||||||
|
const isStash = typeof $environment == "object" && $environment["stash-version"];
|
||||||
|
const adapterStatus = (response) => {
|
||||||
|
if (response && response.statusCode) {
|
||||||
|
response.status = response.statusCode;
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
};
|
||||||
|
this.read = (key) => {
|
||||||
|
if (isQuanX) return $prefs.valueForKey(key);
|
||||||
|
if (isSurge) return $persistentStore.read(key);
|
||||||
|
};
|
||||||
|
this.write = (value, key) => {
|
||||||
|
if (isQuanX) return $prefs.setValueForKey(value, key);
|
||||||
|
if (isSurge) return $persistentStore.write(value, key);
|
||||||
|
};
|
||||||
|
this.notify = (title, subtitle, message) => {
|
||||||
|
if (isQuanX) $notify(title, subtitle, message);
|
||||||
|
if (isSurge) $notification.post(title, subtitle, message);
|
||||||
|
};
|
||||||
|
this.http = (options, callback) => {
|
||||||
|
if (options.policy) {
|
||||||
|
options.node = options.policy;
|
||||||
|
options.opts = { policy: options.policy };
|
||||||
|
if (isStash) options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
...{ "X-Stash-Selected-Proxy": encodeURIComponent(options.policy) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (isQuanX) {
|
||||||
|
$task.fetch(options).then(response => {
|
||||||
|
callback(null, adapterStatus(response), response.body)
|
||||||
|
}, reason => callback(reason.error, null, null))
|
||||||
|
}
|
||||||
|
if (isSurge) {
|
||||||
|
$httpClient[options.method](options, (error, response, body) => {
|
||||||
|
callback(error, adapterStatus(response), body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.done = (value = {}) => {
|
||||||
|
if (value.response && isQuanX) {
|
||||||
|
value.response.status = `HTTP/1.1 ${value.response.status}`;
|
||||||
|
}
|
||||||
|
$done((value.response && isQuanX) ? value.response : value)
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!name=IPA应用辅助安装器
|
||||||
|
#!desc=该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa
|
||||||
|
#!author=NobyDa
|
||||||
|
#!homepage=https://github.com/NobyDa/Script/tree/master
|
||||||
|
#!icon=https://cdn.jsdelivr.net/gh/NobyDa/mini@master/Color/Apple.png
|
||||||
|
|
||||||
|
|
||||||
|
[Script]
|
||||||
|
http-request ^https:\/\/nobyda.app/(install|download) requires-body=true, script-path=https://raw.githubusercontent.com/NobyDa/Script/master/IPA-Installer/IPA-Installer.js, timeout=10, tag=IPA-Installer
|
||||||
|
|
||||||
|
[MITM]
|
||||||
|
hostname = nobyda.app
|
|
@ -0,0 +1,6 @@
|
||||||
|
# 该文件为 "IPA应用辅助安装脚本" QuantumultX远端重写资源.
|
||||||
|
# 该资源可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa
|
||||||
|
|
||||||
|
^https:\/\/nobyda.app/(install|download) url script-analyze-echo-response https://raw.githubusercontent.com/NobyDa/Script/master/IPA-Installer/IPA-Installer.js
|
||||||
|
|
||||||
|
hostname = nobyda.app
|
|
@ -0,0 +1,16 @@
|
||||||
|
name: IPA应用辅助安装器
|
||||||
|
desc: 该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa
|
||||||
|
|
||||||
|
http:
|
||||||
|
mitm:
|
||||||
|
- nobyda.app
|
||||||
|
script:
|
||||||
|
- match: ^https:\/\/nobyda.app/(install|download)
|
||||||
|
name: IPA-Installer
|
||||||
|
type: request
|
||||||
|
require-body: true
|
||||||
|
timeout: 10
|
||||||
|
script-providers:
|
||||||
|
IPA-Installer:
|
||||||
|
url: https://raw.githubusercontent.com/NobyDa/Script/master/IPA-Installer/IPA-Installer.js
|
||||||
|
interval: 86400
|
|
@ -1,8 +1,9 @@
|
||||||
#!name=iOS14 IPA应用安装
|
#!name=IPA应用辅助安装器
|
||||||
#!desc=该模块可解决iOS14或IPadOS14无法在移动端安装IPA的问题. 注: 需要使用Shu+捷径或Jsbox辅助安装.
|
#!desc=该模块可在iOS端辅助安装商店版或已签名IPA(需使用快捷指令 + Shu/Jsbox/pythonista), 查看脚本注释以了解具体方法; 安装演示可查看TG频道 @NobyDa
|
||||||
|
|
||||||
|
|
||||||
[Script]
|
[Script]
|
||||||
IPA应用安装 = type=http-request,pattern=^https:\/\/nobyda/(install|download)(\/jsbox)?$,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Surge/JS/IPA_install.js
|
IPA Installer = type=http-request,pattern=^https:\/\/nobyda.app/(install|download),requires-body=1,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/IPA-Installer/IPA-Installer.js
|
||||||
|
|
||||||
[MITM]
|
[MITM]
|
||||||
hostname = %APPEND% nobyda
|
hostname = %APPEND% nobyda.app
|
Loading…
Reference in New Issue