mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-14 05:20:22 +00:00
fix: banner cycling causing scrolling jumps
breaking: remove recommendations [too much bandwidth wasted] fix: remove all cors perf: decrease load times by removing preflight, and caching user data
This commit is contained in:
parent
6abc544d01
commit
d68aa05a0b
14 changed files with 96 additions and 99 deletions
|
|
@ -5,7 +5,7 @@
|
|||
export const logout = writable(false)
|
||||
|
||||
function confirm () {
|
||||
localStorage.removeItem('ALtoken')
|
||||
localStorage.removeItem('ALviewer')
|
||||
location.hash = ''
|
||||
location.reload()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
const links = [
|
||||
{
|
||||
click: () => {
|
||||
if (anilistClient.userID) {
|
||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
||||
$logout = true
|
||||
} else {
|
||||
IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
||||
|
|
@ -85,13 +85,9 @@
|
|||
text: 'Settings'
|
||||
}
|
||||
]
|
||||
if (anilistClient.userID) {
|
||||
anilistClient.userID.then(result => {
|
||||
if (result?.data?.Viewer) {
|
||||
links[0].image = result.data.Viewer.avatar.medium
|
||||
links[0].text = 'Logout'
|
||||
}
|
||||
})
|
||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
||||
links[0].image = anilistClient.userID.viewer.data.Viewer.avatar.medium
|
||||
links[0].text = 'Logout'
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
</script>
|
||||
|
||||
<div class='w-full h-450 position-relative gradient'>
|
||||
<!-- really shit and hacky way of fixing scroll position jumping when banner changes height -->
|
||||
<div class='position-absolute top-0 transparent h-450 opacity-0'>.</div>
|
||||
{#await data}
|
||||
<SkeletonBanner />
|
||||
{:then { data }}
|
||||
|
|
@ -33,4 +35,7 @@
|
|||
.gradient {
|
||||
background: linear-gradient(0deg, #17191D 0%, #0000 15%, #0000 100%), linear-gradient(90deg, #17191D 0%, rgba(23, 25, 29, 0.885417) 15%, rgba(25, 28, 32, 0) 72%);
|
||||
}
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
30
common/modules/al.d.ts
vendored
30
common/modules/al.d.ts
vendored
|
|
@ -92,21 +92,21 @@ export type Media = {
|
|||
}
|
||||
}[]
|
||||
}
|
||||
recommendations?: {
|
||||
edges?: {
|
||||
node: {
|
||||
media: {
|
||||
id: number
|
||||
title: {
|
||||
userPreferred: string
|
||||
}
|
||||
coverImage?: {
|
||||
medium: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}[]
|
||||
}
|
||||
// recommendations?: {
|
||||
// edges?: {
|
||||
// node: {
|
||||
// media: {
|
||||
// id: number
|
||||
// title: {
|
||||
// userPreferred: string
|
||||
// }
|
||||
// coverImage?: {
|
||||
// medium: string
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }[]
|
||||
// }
|
||||
}
|
||||
|
||||
export type Following = {
|
||||
|
|
|
|||
|
|
@ -150,23 +150,24 @@ relations {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
recommendations{
|
||||
edges{
|
||||
node{
|
||||
mediaRecommendation{
|
||||
id,
|
||||
title{
|
||||
userPreferred
|
||||
},
|
||||
coverImage{
|
||||
medium
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// recommendations{
|
||||
// edges{
|
||||
// node{
|
||||
// mediaRecommendation{
|
||||
// id,
|
||||
// title{
|
||||
// userPreferred
|
||||
// },
|
||||
// coverImage{
|
||||
// medium
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
class AnilistClient {
|
||||
limiter = new Bottleneck({
|
||||
reservoir: 90,
|
||||
|
|
@ -181,7 +182,7 @@ class AnilistClient {
|
|||
/** @type {import('simple-store-svelte').Writable<ReturnType<AnilistClient['getUserLists']>>} */
|
||||
userLists = writable()
|
||||
|
||||
userID
|
||||
userID = alToken
|
||||
|
||||
/** @type {Record<number, import('./al.d.ts').Media>} */
|
||||
mediaCache = {}
|
||||
|
|
@ -201,14 +202,7 @@ class AnilistClient {
|
|||
return time
|
||||
})
|
||||
|
||||
if (alToken) {
|
||||
this.userID = this.viewer({ token: alToken }).then(result => {
|
||||
const lists = result?.data?.Viewer?.mediaListOptions?.animeList?.customLists || []
|
||||
if (!lists.includes('Watched using Miru')) {
|
||||
this.customList({ lists })
|
||||
}
|
||||
return result
|
||||
})
|
||||
if (this.userID?.viewer?.data?.Viewer) {
|
||||
this.userLists.value = this.getUserLists()
|
||||
// update userLists every 15 mins
|
||||
setInterval(() => { this.userLists.value = this.getUserLists() }, 1000 * 60 * 15)
|
||||
|
|
@ -250,8 +244,10 @@ class AnilistClient {
|
|||
* @param {Record<string, any>} variables
|
||||
*/
|
||||
alRequest (query, variables) {
|
||||
/** @type {RequestInit} */
|
||||
const options = {
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
|
|
@ -267,7 +263,8 @@ class AnilistClient {
|
|||
}
|
||||
})
|
||||
}
|
||||
if (alToken) options.headers.Authorization = alToken
|
||||
// @ts-ignore
|
||||
if (alToken?.token) options.headers.Authorization = alToken.token
|
||||
|
||||
return this.handleRequest(options)
|
||||
}
|
||||
|
|
@ -440,7 +437,6 @@ class AnilistClient {
|
|||
|
||||
/** @returns {Promise<import('./al.d.ts').Query<{ Viewer: import('./al.d.ts').Viewer }>>} */
|
||||
viewer (variables = {}) {
|
||||
variables.id = alToken
|
||||
const query = /* js */`
|
||||
query{
|
||||
Viewer{
|
||||
|
|
@ -462,7 +458,7 @@ class AnilistClient {
|
|||
|
||||
/** @returns {Promise<import('./al.d.ts').Query<{ MediaListCollection: import('./al.d.ts').MediaListCollection }>>} */
|
||||
async getUserLists (variables = {}) {
|
||||
const userId = (await this.userID)?.data?.Viewer.id
|
||||
const userId = this.userID?.viewer?.data?.Viewer.id
|
||||
variables.id = userId
|
||||
const query = /* js */`
|
||||
query($id: Int){
|
||||
|
|
@ -499,7 +495,7 @@ class AnilistClient {
|
|||
|
||||
/** @returns {Promise<import('./al.d.ts').Query<{ MediaList: { status: string, progress: number, repeat: number }}>>} */
|
||||
async searchIDStatus (variables = {}) {
|
||||
const userId = (await this.userID)?.data?.Viewer.id
|
||||
const userId = this.userID?.viewer?.data?.Viewer.id
|
||||
variables.id = userId
|
||||
const query = /* js */`
|
||||
query($id: Int, $mediaId: Int){
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { writable } from 'simple-store-svelte'
|
||||
import { defaults } from './util.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
export let alToken = localStorage.getItem('ALtoken') || null
|
||||
import { anilistClient } from './anilist.js'
|
||||
import { toast } from 'svelte-sonner'
|
||||
/** @type {{viewer: import('./al').Query<{Viewer: import('./al').Viewer}>, token: string} | null} */
|
||||
export let alToken = JSON.parse(localStorage.getItem('ALviewer')) || null
|
||||
|
||||
let storedSettings = { ...defaults }
|
||||
|
||||
|
|
@ -46,8 +49,18 @@ window.addEventListener('paste', ({ clipboardData }) => {
|
|||
}
|
||||
})
|
||||
IPC.on('altoken', handleToken)
|
||||
function handleToken (data) {
|
||||
localStorage.setItem('ALtoken', data)
|
||||
alToken = data
|
||||
async function handleToken (token) {
|
||||
alToken = { token, viewer: null }
|
||||
const viewer = await anilistClient.viewer({ token })
|
||||
if (!viewer.data?.Viewer) {
|
||||
toast.error('Failed to sign in with AniList. Please try again.', { description: JSON.stringify(viewer) })
|
||||
console.error(viewer)
|
||||
return
|
||||
}
|
||||
const lists = viewer?.data?.Viewer?.mediaListOptions?.animeList?.customLists || []
|
||||
if (!lists.includes('Watched using Miru')) {
|
||||
await anilistClient.customList({ lists })
|
||||
}
|
||||
localStorage.setItem('ALviewer', JSON.stringify({ token, viewer }))
|
||||
location.reload()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script context='module'>
|
||||
import SectionsManager, { sections } from '@/modules/sections.js'
|
||||
import { alToken, settings } from '@/modules/settings.js'
|
||||
import { settings } from '@/modules/settings.js'
|
||||
import { anilistClient, currentSeason, currentYear } from '@/modules/anilist.js'
|
||||
|
||||
const bannerData = anilistClient.search({ method: 'Search', sort: 'POPULARITY_DESC', perPage: 15, onList: false, season: currentSeason, year: currentYear })
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
for (const sectionTitle of settings.value.homeSections) manager.add(mappedSections[sectionTitle])
|
||||
|
||||
if (alToken) {
|
||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
||||
const userSections = ['Continue Watching', 'Sequels You Missed', 'Your List', 'Completed List', 'Paused List', 'Dropped List', 'Currently Watching List']
|
||||
|
||||
anilistClient.userLists.subscribe(value => {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
}
|
||||
|
||||
function loginButton () {
|
||||
if (anilistClient.userID) {
|
||||
if (anilistClient.userID?.viewer?.data?.Viewer) {
|
||||
$logout = true
|
||||
} else {
|
||||
IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
||||
|
|
@ -126,16 +126,13 @@
|
|||
</div>
|
||||
<div class='pointer my-5 rounded' use:click={loginButton}>
|
||||
<div class='px-20 py-10 d-flex'>
|
||||
{#if anilistClient.userID}
|
||||
{#await anilistClient.userID}
|
||||
<span class='material-symbols-outlined font-size-24 pr-10 d-inline-flex justify-content-center align-items-center'>login</span>
|
||||
<div class='font-size-16'>Login With AniList</div>
|
||||
{:then result}
|
||||
<span class='material-symbols-outlined rounded mr-10'>
|
||||
<img src={result.data.Viewer.avatar.medium} class='h-30 rounded' alt='logo' />
|
||||
</span>
|
||||
<div class='font-size-16 login-image-text'>Logout</div>
|
||||
{/await}
|
||||
{#if anilistClient.userID?.viewer?.data?.Viewer}
|
||||
<span class='material-symbols-outlined font-size-24 pr-10 d-inline-flex justify-content-center align-items-center'>login</span>
|
||||
<div class='font-size-16'>Login With AniList</div>
|
||||
<span class='material-symbols-outlined rounded mr-10'>
|
||||
<img src={anilistClient.userID.viewer.data.Viewer.avatar.medium} class='h-30 rounded' alt='logo' />
|
||||
</span>
|
||||
<div class='font-size-16 login-image-text'>Logout</div>
|
||||
{:else}
|
||||
<span class='material-symbols-outlined font-size-24 pr-10 d-inline-flex justify-content-center align-items-center'>login</span>
|
||||
<div class='font-size-16'>Login With AniList</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
<script>
|
||||
import { alToken } from '@/modules/settings.js'
|
||||
import { anilistClient } from '@/modules/anilist.js'
|
||||
import { click } from '@/modules/click.js'
|
||||
import IPC from '@/modules/ipc.js'
|
||||
|
||||
/** @type {import('@/modules/al.d.ts').Media} */
|
||||
export let media
|
||||
$: following = anilistClient.following({ id: media.id })
|
||||
$: following = anilistClient.userID?.viewer?.data?.Viewer && anilistClient.following({ id: media.id })
|
||||
</script>
|
||||
|
||||
{#await following then res}
|
||||
{@const following = res.data.Page.mediaList}
|
||||
{#if following?.length && alToken}
|
||||
{@const following = res?.data?.Page?.mediaList}
|
||||
{#if following?.length}
|
||||
<div class='w-full d-flex flex-row align-items-center pt-20 mt-10'>
|
||||
<hr class='w-full' />
|
||||
<div class='font-size-18 font-weight-semi-bold px-20 text-white'>Following</div>
|
||||
|
|
|
|||
|
|
@ -166,13 +166,13 @@
|
|||
</div>
|
||||
</ToggleList>
|
||||
<Following {media} />
|
||||
<ToggleList list={media.recommendations.edges.filter(edge => edge.node.mediaRecommendation)} let:item title='Recommendations'>
|
||||
<!-- <ToggleList list={media.recommendations.edges.filter(edge => edge.node.mediaRecommendation)} let:item title='Recommendations'>
|
||||
<div class='w-150 mx-15 my-10 rel pointer'
|
||||
use:click={async () => { $view = null; $view = (await anilistClient.searchIDSingle({ id: item.node.mediaRecommendation.id })).data.Media }}>
|
||||
<img loading='lazy' src={item.node.mediaRecommendation.coverImage.medium || ''} alt='cover' class='cover-img w-full h-200 rel-img rounded' />
|
||||
<h5 class='font-weight-bold text-white mb-5'>{item.node.mediaRecommendation.title.userPreferred}</h5>
|
||||
</div>
|
||||
</ToggleList>
|
||||
</ToggleList> -->
|
||||
<div class='w-full d-flex d-lg-none flex-row align-items-center pt-20 mt-10 pointer'>
|
||||
<hr class='w-full' />
|
||||
<div class='font-size-18 font-weight-semi-bold px-20 text-white'>Episodes</div>
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@
|
|||
atob('d3NzOi8vdHJhY2tlci5maWxlcy5mbTo3MDczL2Fubm91bmNl'),
|
||||
atob('d3NzOi8vdHJhY2tlci5idG9ycmVudC54eXov')
|
||||
], code)
|
||||
p2pt.on('peerconnect', async peer => {
|
||||
p2pt.on('peerconnect', peer => {
|
||||
console.log(peer.id)
|
||||
console.log('connect')
|
||||
const user = (await anilistClient.userID)?.data?.Viewer || {}
|
||||
const user = anilistClient.userID?.viewer?.data?.Viewer || {}
|
||||
p2pt.send(peer,
|
||||
JSON.stringify({
|
||||
type: 'init',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Miru",
|
||||
"version": "5.0.7",
|
||||
"version": "5.0.8",
|
||||
"private": true,
|
||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ function createWindow () {
|
|||
webtorrentWindow = new BrowserWindow({
|
||||
show: development,
|
||||
webPreferences: {
|
||||
webSecurity: false,
|
||||
allowRunningInsecureContent: false,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
backgroundThrottling: false
|
||||
|
|
@ -36,6 +38,8 @@ function createWindow () {
|
|||
backgroundColor: '#17191c',
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
webSecurity: false,
|
||||
allowRunningInsecureContent: false,
|
||||
enableBlinkFeatures: 'FontAccess, AudioVideoTracks',
|
||||
backgroundThrottling: false,
|
||||
preload: path.join(__dirname, '/preload.js')
|
||||
|
|
@ -53,12 +57,6 @@ function createWindow () {
|
|||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived(({ responseHeaders }, fn) => {
|
||||
delete responseHeaders['Access-Control-Allow-Origin']
|
||||
responseHeaders['access-control-allow-origin'] = ['*']
|
||||
fn({ responseHeaders })
|
||||
})
|
||||
|
||||
const torrentLoad = webtorrentWindow.loadURL(development ? 'http://localhost:5000/background.html' : `file://${path.join(__dirname, '/background.html')}`)
|
||||
mainWindow.loadURL(development ? 'http://localhost:5000/app.html' : `file://${path.join(__dirname, '/app.html')}`)
|
||||
|
||||
|
|
@ -94,8 +92,8 @@ function createWindow () {
|
|||
mainWindow.webContents.on('render-process-gone', (e, { reason }) => {
|
||||
if (reason === 'crashed') {
|
||||
if (++crashcount > 10) {
|
||||
dialog.showMessageBox({ message: 'Crashed too many times.', title: 'Miru', detail: 'App crashed too many times. For a fix visit https://github.com/ThaUnknown/miru/blob/master/docs/faq.md#miru-crashed-too-many-times', icon: '/renderer/public/logo_filled.png' }).then(() => {
|
||||
shell.openExternal('https://github.com/ThaUnknown/miru/blob/master/docs/faq.md#miru-crashed-too-many-times')
|
||||
dialog.showMessageBox({ message: 'Crashed too many times.', title: 'Miru', detail: 'App crashed too many times. For a fix visit https://miru.watch/faq/', icon: '/renderer/public/logo_filled.png' }).then(() => {
|
||||
shell.openExternal('https://miru.watch/faq/')
|
||||
app.quit()
|
||||
})
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,13 @@ export const development = process.env.NODE_ENV?.trim() === 'development'
|
|||
|
||||
const flags = [
|
||||
// not sure if safe?
|
||||
['disable-gpu-sandbox'],
|
||||
// not sure if safe?
|
||||
['disable-direct-composition-video-overlays'],
|
||||
// not sure if safe?
|
||||
['double-buffer-compositing'],
|
||||
// not sure if safe?
|
||||
['enable-zero-copy'],
|
||||
// not sure if safe?
|
||||
['ignore-gpu-blocklist'],
|
||||
['disable-gpu-sandbox'], ['disable-direct-composition-video-overlays'], ['double-buffer-compositing'], ['enable-zero-copy'], ['ignore-gpu-blocklist'],
|
||||
// should be safe
|
||||
['enable-hardware-overlays', 'single-fullscreen,single-on-top,underlay'],
|
||||
// dv, drdc display compositor uses 2 gpu threads instead of 1, rest is safe performance
|
||||
['enable-features', 'PlatformEncryptedDolbyVision,EnableDrDc,CanvasOopRasterization,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilation'],
|
||||
// disabling shit, vulkan rendering, native window occlusion calculation, widget layering aka right click context menus [I think] for macOS [I think]
|
||||
['disable-features', 'Vulkan,CalculateNativeWinOcclusion,WidgetLayering'],
|
||||
// safe performance stuff
|
||||
['enable-features', 'PlatformEncryptedDolbyVision,CanvasOopRasterization,ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes,UseSkiaRenderer,WebAssemblyLazyCompilation'],
|
||||
// disabling shit, vulkan rendering, widget layering aka right click context menus [I think] for macOS [I think]
|
||||
['disable-features', 'Vulkan,WidgetLayering'],
|
||||
// utility stuff, aka website security that's useless for a native app:
|
||||
['autoplay-policy', 'no-user-gesture-required'], ['disable-notifications'], ['disable-logging'], ['disable-permissions-api'], ['no-sandbox'], ['no-zygote'], ['bypasscsp-schemes']
|
||||
]
|
||||
|
|
|
|||
Loading…
Reference in a new issue