diff --git a/docs/en/program-update.md b/docs/en/program-update.md index e174eaa5b..16cf42096 100644 --- a/docs/en/program-update.md +++ b/docs/en/program-update.md @@ -16,6 +16,20 @@ pageClass: routes +## App Center + +### Release + + + +::: tip + +The parameters can be extracted from the Release page URL: `https://install.appcenter.ms/users/:user/apps/:app/distribution_groups/:distribution_group` + +::: + + + ## Apkpure ### Versions diff --git a/docs/program-update.md b/docs/program-update.md index eac0cd60c..397c1860c 100644 --- a/docs/program-update.md +++ b/docs/program-update.md @@ -32,6 +32,20 @@ pageClass: routes +## App Center + +### Release + + + +::: tip 提示 + +参数可从 Release 页的 URL 中提取: `https://install.appcenter.ms/users/:user/apps/:app/distribution_groups/:distribution_group` + +::: + + + ## App Store/Mac App Store ### 应用更新 diff --git a/lib/v2/app-center/maintainer.js b/lib/v2/app-center/maintainer.js new file mode 100644 index 000000000..42dcdbe44 --- /dev/null +++ b/lib/v2/app-center/maintainer.js @@ -0,0 +1,3 @@ +module.exports = { + '/release/:user/:app/:distribution_group': ['Rongronggg9'], +}; diff --git a/lib/v2/app-center/radar.js b/lib/v2/app-center/radar.js new file mode 100644 index 000000000..f05c6d8e4 --- /dev/null +++ b/lib/v2/app-center/radar.js @@ -0,0 +1,13 @@ +module.exports = { + 'appcenter.ms': { + _name: 'App Center', + install: [ + { + title: 'App Center Release', + docs: 'https://docs.rsshub.app/program-update.html#app-center', + source: ['/users/:user/apps/:app/distribution_groups/:distribution_group', '/orgs/:user/apps/:app/distribution_groups/:distribution_group'], + target: '/app-center/release/:user/:app/:distribution_group', + }, + ], + }, +}; diff --git a/lib/v2/app-center/release.js b/lib/v2/app-center/release.js new file mode 100644 index 000000000..cdd370313 --- /dev/null +++ b/lib/v2/app-center/release.js @@ -0,0 +1,121 @@ +const got = require('@/utils/got'); +const { parseDate } = require('@/utils/parse-date'); +const { art } = require('@/utils/render'); +const path = require('path'); +const MarkdownIt = require('markdown-it'); + +module.exports = async (ctx) => { + const user = ctx.params.user; + const app = ctx.params.app; + const distribution_group = ctx.params.distribution_group; + + const baseUrl = 'https://install.appcenter.ms/api/v0.1/apps'; + const apiUrl = `${baseUrl}/${user}/${app}/distribution_groups/${distribution_group}`; + const releasesListUrl = `${apiUrl}/public_releases?scope=tester`; + // const releaseUrl = `${apiUrl}/releases/${release_id}?is_install_page=true`; + + const response = await got(releasesListUrl); + + let title; + const link = `https://install.appcenter.ms/users/${user}/apps/${app}/distribution_groups/${distribution_group}`; + let icon; + + let items = response.data.map((item) => ({ + // item: + // { + // "id": 504, + // "short_version": "8.5.0.0", + // "version": "18558", + // "origin": "appcenter", + // "uploaded_at": "2022-02-02T11:36:06.044Z", + // "mandatory_update": false, + // "enabled": true, + // "is_external_build": false + // } + + pubDate: parseDate(item.uploaded_at), + link: `${apiUrl}/releases/${item.id}?is_install_page=true`, + })); + + // Release info examples: + // Android: https://install.appcenter.ms/api/v0.1/apps/rafalense-70ux/plus-release/distribution_groups/public/releases/42?is_install_page=true + // iOS: https://install.appcenter.ms/api/v0.1/apps/gameonline/baitomobile/distribution_groups/baito/releases/26?is_install_page=true + // Windows: https://install.appcenter.ms/api/v0.1/apps/remitano/remitano-windows/distribution_groups/beta/releases/5?is_install_page=true + // macOS: https://install.appcenter.ms/api/v0.1/apps/rdmacios-k2vy/microsoft-remote-desktop-for-mac/distribution_groups/all-users-of-microsoft-remote-desktop-for-mac/releases/635?is_install_page=true + + const md = new MarkdownIt(); + items = await Promise.all( + items.map((item) => + ctx.cache.tryGet(item.link, async () => { + const releaseResponse = await got(item.link); + const releaseInfo = releaseResponse.data; + + const userName = releaseInfo.owner.display_name; + const appOS = releaseInfo.app_os; + const shortVersion = releaseInfo.short_version; // will be an empty string for Windows + const versionCode = releaseInfo.version; + const isExternalBuild = releaseInfo.is_external_build; + const isMandatoryUpdate = releaseInfo.mandatory_update; + // const isLatest = releaseInfo.is_latest; // this is not representing the latest release, but the latest version of a certain release + const sizeInMBytes = (releaseInfo.size / (1024 * 1024)).toFixed(2); + const releaseDate = releaseInfo.uploaded_at; // use original text here because it is already an ISO 8601 time + const fingerprint = releaseInfo.fingerprint; + const minOS = releaseInfo.min_os; // `null` for Windows + const androidMinApiLevel = releaseInfo.android_min_api_level; // only for Android + const deviceFamily = releaseInfo.device_family; // only for iOS, `null` for others + const bundleId = releaseInfo.bundle_identifier; // can be a hash or a package name + const releaseNotes = releaseInfo.release_notes; // markdown, can be an empty string + const downloadUrl = releaseInfo.download_url; + const installUrl = releaseInfo.install_url; + const fileExtension = releaseInfo.fileExtension; + + if (!(title && icon)) { + const appName = releaseInfo.app_display_name; + const distributionGroupId = releaseInfo.distribution_group_id; + const distributionGroupName = releaseInfo.distribution_groups.filter((group) => group.id === distributionGroupId)[0].display_name; + title = `${appName} (${distributionGroupName}) for ${appOS} by ${userName} - App Center Releases`; + icon = releaseInfo.app_icon_url; + } + + const version = shortVersion && versionCode ? `${shortVersion} (${versionCode})` : shortVersion || versionCode; + + item.title = + (isMandatoryUpdate ? '[Mandatory]' : '') + + // + (isLatest ? "[Latest]" : "") + (isExternalBuild ? '[External Build]' : '') + + `Version ${version}`; + item.link = link; // replace the link with the release page + item.author = userName; + item.description = art( + path.join(__dirname, 'templates/description.art'), + { + releaseDate, + sizeInMBytes, + minOS, + deviceFamily, + androidMinApiLevel, + bundleId, + downloadUrl, + installUrl, + fingerprint, + appOS, + fileExtension, + releaseNotes: releaseNotes && md.render(releaseNotes), + }, + { minimize: true } + ); + item.guid = fingerprint; + + return item; + }) + ) + ); + + ctx.state.data = { + title, + link, + description: title, + image: icon, + item: items, + }; +}; diff --git a/lib/v2/app-center/router.js b/lib/v2/app-center/router.js new file mode 100644 index 000000000..d1558874b --- /dev/null +++ b/lib/v2/app-center/router.js @@ -0,0 +1,3 @@ +module.exports = function (router) { + router.get('/release/:user/:app/:distribution_group', require('./release')); +}; diff --git a/lib/v2/app-center/templates/description.art b/lib/v2/app-center/templates/description.art new file mode 100644 index 000000000..3e7a8d8d3 --- /dev/null +++ b/lib/v2/app-center/templates/description.art @@ -0,0 +1,24 @@ +

+Release Date: {{releaseDate}}
+Fingerprint: {{fingerprint}}
+OS: {{appOS}}
+{{if minOS}}Minimum OS Version: {{minOS}}
{{/if}} +{{if androidMinApiLevel}}Android Minimum API Level: {{androidMinApiLevel}}
{{/if}} +{{if deviceFamily}}Device Family: {{deviceFamily}}
{{/if}} +{{if bundleId}}Bundle ID: {{bundleId}}
{{/if}} +{{if fileExtension}}File Extension: {{fileExtension}}
{{/if}} +Size: {{sizeInMBytes}} MB +

+{{if releaseNotes}} +

+Release Notes: +

+{{@ releaseNotes }} +{{/if}} +

+{{if downloadUrl===installUrl}} +[ Download ] +{{else}} +[ Download | Install ] +{{/if}} +