mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-19 07:32:05 +00:00
Merge branch 'master' of https://github.com/RockinChaos/miru
This commit is contained in:
commit
d576ccded4
18 changed files with 655 additions and 386 deletions
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
|
|
@ -39,4 +39,9 @@ jobs:
|
|||
- name: Build and Publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# CSC_LINK: ${{ secrets.APPLE_SIGNING_CERT }}
|
||||
# API_KEY: ${{ secrets.APPLE_API_KEY }}
|
||||
# APPLE_API_KEY: apple.p8
|
||||
# APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
# APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: cd electron && npm run publish
|
||||
|
|
|
|||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"npm.exclude": "**/git_modules/**"
|
||||
"npm.exclude": "**/git_modules/**",
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
}
|
||||
1
capacitor/.gitignore
vendored
1
capacitor/.gitignore
vendored
|
|
@ -9,6 +9,7 @@ android/app/src/*
|
|||
!android/app/src/main
|
||||
android/app/src/main/*
|
||||
!android/app/src/main/AndroidManifest.xml
|
||||
!android/app/src/main/java
|
||||
ios/
|
||||
*.jks
|
||||
*.pepk
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ versions.each (code) -> {
|
|||
|
||||
android {
|
||||
namespace "watch.miru"
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
defaultConfig {
|
||||
applicationId "watch.miru"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
<!-- Permissions -->
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package watch.miru;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.webkit.ServiceWorkerClient;
|
||||
import android.webkit.ServiceWorkerController;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
public class MainActivity extends BridgeActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
ServiceWorkerController swController = null;
|
||||
swController = ServiceWorkerController.getInstance();
|
||||
|
||||
swController.setServiceWorkerClient(new ServiceWorkerClient() {
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
|
||||
if (request.getUrl().toString().contains("index.html")) {
|
||||
request.getRequestHeaders().put("Accept", "text/html");
|
||||
}
|
||||
return bridge.getLocalServer().shouldInterceptRequest(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,13 @@ ext {
|
|||
minSdkVersion = 22
|
||||
compileSdkVersion = 34
|
||||
targetSdkVersion = 34
|
||||
androidxActivityVersion = '1.7.0'
|
||||
androidxActivityVersion = '1.8.0'
|
||||
androidxAppCompatVersion = '1.6.1'
|
||||
androidxCoordinatorLayoutVersion = '1.2.0'
|
||||
androidxCoreVersion = '1.10.0'
|
||||
androidxFragmentVersion = '1.5.6'
|
||||
coreSplashScreenVersion = '1.0.0'
|
||||
androidxWebkitVersion = '1.6.1'
|
||||
androidxCoreVersion = '1.12.0'
|
||||
androidxFragmentVersion = '1.6.2'
|
||||
coreSplashScreenVersion = '1.0.1'
|
||||
androidxWebkitVersion = '1.9.0'
|
||||
junitVersion = '4.13.2'
|
||||
androidxJunitVersion = '1.1.5'
|
||||
androidxEspressoCoreVersion = '3.5.1'
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const config = {
|
|||
keystorePath: './watch.miru',
|
||||
keystorePassword: '',
|
||||
keystoreAlias: 'watch.miru'
|
||||
}
|
||||
},
|
||||
webContentsDebuggingEnabled: true
|
||||
},
|
||||
plugins: {
|
||||
SplashScreen: { launchShowDuration: 0 },
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/assets": "github:thaunknown/capacitor-assets",
|
||||
"@capacitor/cli": "^6.1.1",
|
||||
"cordova-res": "^0.15.4",
|
||||
"nodejs-mobile-gyp": "^0.3.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
|
|
@ -25,16 +26,15 @@
|
|||
"webpack-merge": "^5.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@capacitor/android": "^5.5.1",
|
||||
"@capacitor/app": "^5.0.6",
|
||||
"@capacitor/browser": "^5.1.0",
|
||||
"@capacitor/cli": "^5.5.1",
|
||||
"@capacitor/core": "^5.5.1",
|
||||
"@capacitor/ios": "^5.5.1",
|
||||
"@capacitor/local-notifications": "5.0.8",
|
||||
"@capacitor/status-bar": "^5.0.6",
|
||||
"@capacitor/android": "^6.1.1",
|
||||
"@capacitor/app": "^6.0.0",
|
||||
"@capacitor/browser": "^6.0.1",
|
||||
"@capacitor/core": "^6.1.1",
|
||||
"@capacitor/ios": "^6.1.1",
|
||||
"@capacitor/local-notifications": "^6.0.0",
|
||||
"@capacitor/status-bar": "^6.0.0",
|
||||
"capacitor-nodejs": "https://github.com/funniray/Capacitor-NodeJS/releases/download/nodejs-18/capacitor-nodejs-1.0.0-beta.6.tgz",
|
||||
"capacitor-plugin-safe-area": "^2.0.5",
|
||||
"capacitor-plugin-safe-area": "^2.0.6",
|
||||
"common": "workspace:*",
|
||||
"cordova-plugin-navigationbar": "^1.0.31",
|
||||
"cordova-plugin-pip": "^0.0.2",
|
||||
|
|
|
|||
|
|
@ -284,19 +284,19 @@ class AnilistClient {
|
|||
// isAdult doesn't need an extra variable, as the title is the same regardless of type, so we re-use the same variable for adult and non-adult requests
|
||||
/** @type {Record<`v${number}`, string>} */
|
||||
const requestVariables = flattenedTitles.reduce((obj, { title, isAdult }, i) => {
|
||||
if (isAdult) return obj
|
||||
if (isAdult && i !== 0) return obj
|
||||
obj[`v${i}`] = title
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
const queryVariables = flattenedTitles.reduce((arr, { isAdult }, i) => {
|
||||
if (isAdult) return arr
|
||||
if (isAdult && i !== 0) return arr
|
||||
arr.push(`$v${i}: String`)
|
||||
return arr
|
||||
}, []).join(', ')
|
||||
const fragmentQueries = flattenedTitles.map(({ year, isAdult }, i) => /* js */`
|
||||
v${i}: Page(perPage: 10) {
|
||||
media(type: ANIME, search: $v${isAdult ? i - 1 : i}, status_in: [RELEASING, FINISHED], isAdult: ${!!isAdult} ${year ? `, seasonYear: ${year}` : ''}) {
|
||||
media(type: ANIME, search: $v${(isAdult && i !== 0) ? i - 1 : i}, status_in: [RELEASING, FINISHED], isAdult: ${!!isAdult} ${year ? `, seasonYear: ${year}` : ''}) {
|
||||
...med
|
||||
}
|
||||
}`)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default new class AnimeResolver {
|
|||
* @returns {string[]}
|
||||
*/
|
||||
alternativeTitles (title) {
|
||||
const titles = []
|
||||
const titles = new Set()
|
||||
|
||||
let modified = title
|
||||
// preemptively change S2 into Season 2 or 2nd Season, otherwise this will have accuracy issues
|
||||
|
|
@ -33,31 +33,31 @@ export default new class AnimeResolver {
|
|||
if (seasonMatch) {
|
||||
if (Number(seasonMatch[1]) === 1) { // if this is S1, remove the " S1" or " S01"
|
||||
modified = title.replace(/ S(\d+)/, '')
|
||||
titles.push(modified)
|
||||
titles.add(modified)
|
||||
} else {
|
||||
modified = title.replace(/ S(\d+)/, ` ${Number(seasonMatch[1])}${postfix[Number(seasonMatch[1])] || 'th'} Season`)
|
||||
titles.push(modified)
|
||||
titles.push(title.replace(/ S(\d+)/, ` Season ${Number(seasonMatch[1])}`))
|
||||
titles.add(modified)
|
||||
titles.add(title.replace(/ S(\d+)/, ` Season ${Number(seasonMatch[1])}`))
|
||||
}
|
||||
} else {
|
||||
titles.push(title)
|
||||
titles.add(title)
|
||||
}
|
||||
|
||||
// remove - :
|
||||
const specialMatch = modified.match(/[-:]/g)
|
||||
if (specialMatch) {
|
||||
modified = modified.replace(/[-:]/g, '')
|
||||
titles.push(modified)
|
||||
modified = modified.replace(/[-:]/g, '').replace(/[ ]{2,}/, ' ')
|
||||
titles.add(modified)
|
||||
}
|
||||
|
||||
// remove (TV)
|
||||
const tvMatch = modified.match(/\(TV\)/)
|
||||
if (tvMatch) {
|
||||
modified = modified.replace('(TV)', '')
|
||||
titles.push(modified)
|
||||
titles.add(modified)
|
||||
}
|
||||
|
||||
return titles
|
||||
return [...titles]
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -73,8 +73,10 @@ export default new class AnimeResolver {
|
|||
return titleObjects
|
||||
}).flat()
|
||||
|
||||
for (const [key, media] of await anilistClient.alSearchCompound(titleObjects)) {
|
||||
this.animeNameCache[key] = media
|
||||
for (const chunk of chunks(titleObjects, 62)) { // single title has a complexity of 8.1, al limits complexity to 500
|
||||
for (const [key, media] of await anilistClient.alSearchCompound(chunk)) {
|
||||
this.animeNameCache[key] = media
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,16 +99,17 @@ export default new class AnimeResolver {
|
|||
if (!fileName) return [{}]
|
||||
const parseObjs = await anitomyscript(fileName)
|
||||
|
||||
const TYPE_EXCLUSIONS = ['ED', 'ENDING', 'NCED', 'NCOP', 'OP', 'OPENING', 'PREVIEW', 'PV']
|
||||
|
||||
/** @type {Record<string, import('anitomyscript').AnitomyResult>} */
|
||||
const uniq = {}
|
||||
for (const obj of parseObjs) {
|
||||
const key = this.getCacheKeyForTitle(obj)
|
||||
if (key in this.animeNameCache) continue
|
||||
if (key in this.animeNameCache) continue // skip already resolved
|
||||
if (obj.anime_type && TYPE_EXCLUSIONS.includes(obj.anime_type.toUpperCase())) continue // skip non-episode media
|
||||
uniq[key] = obj
|
||||
}
|
||||
for (const chunk of chunks(Object.values(uniq), 50)) {
|
||||
await this.findAnimesByTitle(chunk)
|
||||
}
|
||||
await this.findAnimesByTitle(Object.values(uniq))
|
||||
|
||||
const fileAnimes = []
|
||||
for (const parseObj of parseObjs) {
|
||||
|
|
|
|||
|
|
@ -231,11 +231,12 @@ export default class TorrentClient extends WebTorrent {
|
|||
return
|
||||
}
|
||||
localStorage.setItem('lastFinished', 'false')
|
||||
if (this.torrents.length) await this.remove(this.torrents[0])
|
||||
if (this.torrents.length) {
|
||||
await this.remove(this.torrents[0], { destroyStore: !this.settings.torrentPersist })
|
||||
}
|
||||
const torrent = await this.add(data, {
|
||||
private: this.settings.torrentPeX,
|
||||
path: this.torrentPath || undefined,
|
||||
destroyStoreOnDestroy: !this.settings.torrentPersist,
|
||||
skipVerify,
|
||||
announce,
|
||||
deselect: this.settings.torrentStreamedDownload
|
||||
|
|
|
|||
|
|
@ -1524,6 +1524,12 @@
|
|||
.seekbar {
|
||||
font-size: 2rem !important;
|
||||
}
|
||||
.miniplayer .mobile-focus-target {
|
||||
display: block !important;
|
||||
}
|
||||
.miniplayer .mobile-focus-target:focus-visible {
|
||||
background: hsla(209, 100%, 55%, 0.3);
|
||||
}
|
||||
|
||||
@media (pointer: none), (pointer: coarse) {
|
||||
.bottom .ctrl[data-name='playPause'],
|
||||
|
|
@ -1548,12 +1554,6 @@
|
|||
.toggle-fullscreen {
|
||||
display: none !important;
|
||||
}
|
||||
.miniplayer .mobile-focus-target {
|
||||
display: block !important;
|
||||
}
|
||||
.miniplayer .mobile-focus-target:focus-visible {
|
||||
background: hsla(209, 100%, 55%, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
const { notarize } = require('@electron/notarize')
|
||||
const path = require('path')
|
||||
|
||||
exports.default = async function notarizing (context) {
|
||||
if (context.electronPlatformName !== 'darwin' || process.env.CSC_IDENTITY_AUTO_DISCOVERY === 'false') {
|
||||
console.log('Skipping notarization')
|
||||
return
|
||||
}
|
||||
console.log('Notarizing...')
|
||||
|
||||
const appBundleId = context.packager.appInfo.info._configuration.appId
|
||||
const appName = context.packager.appInfo.productFilename
|
||||
const appPath = path.normalize(path.join(context.appOutDir, `${appName}.app`))
|
||||
const appleId = process.env.APPLE_ID
|
||||
const appleIdPassword = process.env.APPLE_ID_PASSWORD
|
||||
if (!appleId) {
|
||||
console.warn('Not notarizing: Missing APPLE_ID environment variable')
|
||||
return
|
||||
}
|
||||
if (!appleIdPassword) {
|
||||
console.warn('Not notarizing: Missing APPLE_ID_PASSWORD environment variable')
|
||||
return
|
||||
}
|
||||
return notarize({
|
||||
appBundleId,
|
||||
appPath,
|
||||
appleId,
|
||||
appleIdPassword
|
||||
})
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Miru",
|
||||
"version": "5.2.7",
|
||||
"version": "5.2.14",
|
||||
"private": true,
|
||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
||||
|
|
@ -20,9 +20,9 @@
|
|||
"discord-rpc": "4.0.1",
|
||||
"electron": "29.1.4",
|
||||
"electron-builder": "^24.13.3",
|
||||
"electron-log": "^5.1.5",
|
||||
"electron-log": "^5.1.7",
|
||||
"electron-updater": "^6.2.1",
|
||||
"webpack-merge": "^5.10.0"
|
||||
"webpack-merge": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paymoapp/electron-shutdown-handler": "^1.1.2",
|
||||
|
|
@ -61,7 +61,6 @@
|
|||
"repo": "miru"
|
||||
}
|
||||
],
|
||||
"afterSign": "./buildResources/notarize.js",
|
||||
"appId": "com.github.thaunknown.miru",
|
||||
"productName": "Miru",
|
||||
"files": [
|
||||
|
|
@ -73,8 +72,10 @@
|
|||
"defaultArch": "universal",
|
||||
"singleArchFiles": "node_modules/+(register-scheme|utp-native|fs-native-extensions)/**",
|
||||
"category": "public.app-category.video",
|
||||
"darkModeSupport": true,
|
||||
"icon": "buildResources/icon.icns",
|
||||
"hardenedRuntime": true,
|
||||
"notarize": false,
|
||||
"entitlements": "buildResources/entitlements.mac.plist",
|
||||
"target": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default class App {
|
|||
|
||||
discord = new Discord(this.mainWindow)
|
||||
protocol = new Protocol(this.mainWindow)
|
||||
updater = new Updater(this.mainWindow)
|
||||
updater = new Updater(this.mainWindow, this.webtorrentWindow)
|
||||
dialog = new Dialog(this.webtorrentWindow)
|
||||
|
||||
constructor () {
|
||||
|
|
@ -134,11 +134,17 @@ export default class App {
|
|||
this.webtorrentWindow.webContents.postMessage('torrentPath', store.get('torrentPath'))
|
||||
sender.postMessage('port', null, [port2])
|
||||
})
|
||||
|
||||
ipcMain.on('quit-and-install', () => {
|
||||
if (this.updater.hasUpdate) {
|
||||
this.destroy(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
destroyed = false
|
||||
|
||||
async destroy () {
|
||||
async destroy (forceRunAfter = false) {
|
||||
if (this.destroyed) return
|
||||
this.webtorrentWindow.webContents.postMessage('destroy', null)
|
||||
await new Promise(resolve => {
|
||||
|
|
@ -146,6 +152,6 @@ export default class App {
|
|||
setTimeout(resolve, 5000).unref?.()
|
||||
})
|
||||
this.destroyed = true
|
||||
if (!this.updater.install()) app.quit()
|
||||
if (!this.updater.install(forceRunAfter)) app.quit()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import log from 'electron-log'
|
||||
import { autoUpdater } from 'electron-updater'
|
||||
import { ipcMain } from 'electron'
|
||||
import { ipcMain, shell } from 'electron'
|
||||
|
||||
let hasUpdate = false
|
||||
|
||||
|
|
@ -13,10 +13,15 @@ ipcMain.on('update', () => {
|
|||
|
||||
autoUpdater.checkForUpdatesAndNotify()
|
||||
export default class Updater {
|
||||
window
|
||||
torrentWindow
|
||||
/**
|
||||
* @param {import('electron').BrowserWindow} window
|
||||
* @param {import('electron').BrowserWindow} torrentWindow
|
||||
*/
|
||||
constructor (window) {
|
||||
constructor (window, torrentWindow) {
|
||||
this.window = window
|
||||
this.torrentWindow = torrentWindow
|
||||
autoUpdater.on('update-available', () => {
|
||||
window.webContents.send('update-available', true)
|
||||
})
|
||||
|
|
@ -24,19 +29,18 @@ export default class Updater {
|
|||
hasUpdate = true
|
||||
window.webContents.send('update-downloaded', true)
|
||||
})
|
||||
ipcMain.on('quit-and-install', () => {
|
||||
if (hasUpdate) {
|
||||
autoUpdater.quitAndInstall()
|
||||
hasUpdate = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
install () {
|
||||
install (forceRunAfter = false) {
|
||||
if (hasUpdate) {
|
||||
autoUpdater.quitAndInstall()
|
||||
setImmediate(() => {
|
||||
this.window.close()
|
||||
this.torrentWindow.close()
|
||||
autoUpdater.quitAndInstall(true, forceRunAfter)
|
||||
})
|
||||
if (process.platform === 'darwin') shell.openExternal('https://miru.watch/download')
|
||||
hasUpdate = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
834
pnpm-lock.yaml
834
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue