mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-21 00:22:08 +00:00
feat: nicer notifications
This commit is contained in:
parent
2597e00f45
commit
4ca90751e6
16 changed files with 114 additions and 168 deletions
|
|
@ -48,6 +48,7 @@
|
||||||
"svelte-keybinds": "1.0.5",
|
"svelte-keybinds": "1.0.5",
|
||||||
"svelte-loader": "^3.1.9",
|
"svelte-loader": "^3.1.9",
|
||||||
"svelte-miniplayer": "1.0.3",
|
"svelte-miniplayer": "1.0.3",
|
||||||
|
"svelte-sonner": "^0.1.1",
|
||||||
"webpack": "^5.85.0",
|
"webpack": "^5.85.0",
|
||||||
"webpack-cli": "^5.1.3",
|
"webpack-cli": "^5.1.3",
|
||||||
"webpack-dev-server": "^4.15.0",
|
"webpack-dev-server": "^4.15.0",
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,9 @@ devDependencies:
|
||||||
svelte-miniplayer:
|
svelte-miniplayer:
|
||||||
specifier: 1.0.3
|
specifier: 1.0.3
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
|
svelte-sonner:
|
||||||
|
specifier: ^0.1.1
|
||||||
|
version: 0.1.1(svelte@4.0.4)
|
||||||
webpack:
|
webpack:
|
||||||
specifier: ^5.85.0
|
specifier: ^5.85.0
|
||||||
version: 5.86.0(webpack-cli@5.1.4)
|
version: 5.86.0(webpack-cli@5.1.4)
|
||||||
|
|
@ -5485,6 +5488,14 @@ packages:
|
||||||
resolution: {integrity: sha512-++IvuENs/3x0SbHZ/jWNqfq+hmU/ZT73V/ETwn6TqCbHF7asXsZFxVcgyIL1lMaQMKxe227HrYghYRVNJXeFVw==}
|
resolution: {integrity: sha512-++IvuENs/3x0SbHZ/jWNqfq+hmU/ZT73V/ETwn6TqCbHF7asXsZFxVcgyIL1lMaQMKxe227HrYghYRVNJXeFVw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/svelte-sonner@0.1.1(svelte@4.0.4):
|
||||||
|
resolution: {integrity: sha512-jQ/coEZvJxImxxsT5FPnHeXitGx5wQRhAkAz6C8DY8yM1qFPvFowG6mDtC0KPPDEKlHCmocbWigk6zrG27OO/Q==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^4.0.0
|
||||||
|
dependencies:
|
||||||
|
svelte: 4.0.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
/svelte@4.0.4:
|
/svelte@4.0.4:
|
||||||
resolution: {integrity: sha512-DDJavyX1mpNFLZ7jU9FwBKouemh6CJHZXwePBa5GXSaW5GuHZ361L2/1uznBqOCxu2UsUoWu8wRsB2iB8QG5sQ==}
|
resolution: {integrity: sha512-DDJavyX1mpNFLZ7jU9FwBKouemh6CJHZXwePBa5GXSaW5GuHZ361L2/1uznBqOCxu2UsUoWu8wRsB2iB8QG5sQ==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@
|
||||||
import ViewTrailer from './views/ViewAnime/ViewTrailer.svelte'
|
import ViewTrailer from './views/ViewAnime/ViewTrailer.svelte'
|
||||||
import RSSView from './views/RSSView.svelte'
|
import RSSView from './views/RSSView.svelte'
|
||||||
import Menubar from './components/Menubar.svelte'
|
import Menubar from './components/Menubar.svelte'
|
||||||
import Toasts from './components/Toasts.svelte'
|
|
||||||
import IspBlock from './views/IspBlock.svelte'
|
import IspBlock from './views/IspBlock.svelte'
|
||||||
|
import { Toaster, toast } from 'svelte-sonner'
|
||||||
|
|
||||||
setContext('view', view)
|
setContext('view', view)
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id='player' />
|
<div id='player' />
|
||||||
<Toasts />
|
<Toaster visibleToasts={3} position='top-right' theme='dark' richColors duration={10000} />
|
||||||
<div class='page-wrapper with-sidebar with-transitions bg-dark' data-sidebar-type='overlayed-all'>
|
<div class='page-wrapper with-sidebar with-transitions bg-dark' data-sidebar-type='overlayed-all'>
|
||||||
<div class='sticky-alerts' />
|
<div class='sticky-alerts' />
|
||||||
<IspBlock />
|
<IspBlock />
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { alID } from '@/modules/anilist.js'
|
import { alID } from '@/modules/anilist.js'
|
||||||
import { media } from '../views/Player/MediaHandler.svelte'
|
import { media } from '../views/Player/MediaHandler.svelte'
|
||||||
import { platformMap } from '../views/Settings.svelte'
|
import { platformMap } from '../views/Settings.svelte'
|
||||||
import { addToast } from './Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
const view = getContext('view')
|
const view = getContext('view')
|
||||||
export let page
|
export let page
|
||||||
|
|
@ -17,11 +17,9 @@
|
||||||
} else {
|
} else {
|
||||||
window.IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
window.IPC.emit('open', 'https://anilist.co/api/v2/oauth/authorize?client_id=4254&response_type=token') // Change redirect_url to miru://auth
|
||||||
if (platformMap[window.version.platform] === 'Linux') {
|
if (platformMap[window.version.platform] === 'Linux') {
|
||||||
addToast({
|
toast('Support Notification', {
|
||||||
text: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.",
|
description: "If your linux distribution doesn't support custom protocol handlers, you can simply paste the full URL into the app.",
|
||||||
title: 'Support Notification',
|
duration: 300000
|
||||||
type: 'secondary',
|
|
||||||
duration: '300000'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
<script context='module'>
|
|
||||||
import { writable } from 'svelte/store'
|
|
||||||
import { click } from '@/modules/click.js'
|
|
||||||
const toasts = writable({})
|
|
||||||
let index = 0
|
|
||||||
export function addToast (opts) {
|
|
||||||
// type, click, title, text
|
|
||||||
toasts.update(toasts => {
|
|
||||||
const i = ++index
|
|
||||||
toasts[i] = opts
|
|
||||||
setTimeout(() => {
|
|
||||||
close(i)
|
|
||||||
}, opts.duration || 10000)
|
|
||||||
return toasts
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function close (index) {
|
|
||||||
toasts.update(toasts => {
|
|
||||||
if (toasts[index]) {
|
|
||||||
delete toasts[index]
|
|
||||||
}
|
|
||||||
return toasts
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class='sticky-alerts d-flex flex-column-reverse'>
|
|
||||||
{#each Object.entries($toasts) as [index, toast] (index)}
|
|
||||||
<div class='alert alert-{toast.type} filled' class:pointer={toast.click} use:click={toast.click}>
|
|
||||||
<button class='close' type='button' use:click={() => close(index)}><span aria-hidden='true'>×</span></button>
|
|
||||||
<h4 class='alert-heading'>{toast.title}</h4>
|
|
||||||
{@html toast.text}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.alert {
|
|
||||||
display: block !important;
|
|
||||||
animation: 0.3s ease 0s 1 fly-in;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.sticky-alerts {
|
|
||||||
top: 2.5rem
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fly-in {
|
|
||||||
from {
|
|
||||||
right: -50rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -28,6 +28,20 @@
|
||||||
--dm-button-secondary-bg-color-hover: #ddd !important;
|
--dm-button-secondary-bg-color-hover: #ddd !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-sonner-toaster][data-theme='dark'] {
|
||||||
|
--normal-bg: var(--dark-color) !important;
|
||||||
|
--normal-border: none !important;
|
||||||
|
--normal-text: var(--dm-base-text-color) !important;
|
||||||
|
|
||||||
|
/* --success-bg: var(--success-color) !important; */
|
||||||
|
--success-border: none !important;
|
||||||
|
/* --success-text: var(--lm-base-text-color) !important; */
|
||||||
|
|
||||||
|
/* --error-bg: hsl(358, 76%, 10%); */
|
||||||
|
--error-border: none !important;
|
||||||
|
/* --error-text: hsl(358, 100%, 81%); */
|
||||||
|
}
|
||||||
|
|
||||||
.material-symbols-outlined {
|
.material-symbols-outlined {
|
||||||
font-family: "Material Symbols Outlined Variable";
|
font-family: "Material Symbols Outlined Variable";
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { writable } from 'simple-store-svelte'
|
||||||
import Bottleneck from 'bottleneck'
|
import Bottleneck from 'bottleneck'
|
||||||
|
|
||||||
import { alToken } from '../views/Settings.svelte'
|
import { alToken } from '../views/Settings.svelte'
|
||||||
import { addToast } from '../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { sleep } from './util.js'
|
import { sleep } from './util.js'
|
||||||
|
|
||||||
const codes = {
|
const codes = {
|
||||||
|
|
@ -106,10 +106,8 @@ if (alToken) {
|
||||||
|
|
||||||
function printError (error) {
|
function printError (error) {
|
||||||
console.warn(error)
|
console.warn(error)
|
||||||
addToast({
|
toast.error('Search Failed', {
|
||||||
text: /* html */`Failed making request to anilist!<br>Try again in a minute.<br>${error.status || 429} - ${error.message || codes[error.status || 429]}`,
|
description: `Failed making request to anilist!\nTry again in a minute.\n${error.status || 429} - ${error.message || codes[error.status || 429]}`,
|
||||||
title: 'Search Failed',
|
|
||||||
type: 'danger',
|
|
||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { DOMPARSER, PromiseBatch, binarySearch } from './util.js'
|
||||||
import { alRequest, alSearch } from './anilist.js'
|
import { alRequest, alSearch } from './anilist.js'
|
||||||
import anitomyscript from 'anitomyscript'
|
import anitomyscript from 'anitomyscript'
|
||||||
import { media } from '../views/Player/MediaHandler.svelte'
|
import { media } from '../views/Player/MediaHandler.svelte'
|
||||||
import { addToast } from '../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { view } from '@/App.svelte'
|
import { view } from '@/App.svelte'
|
||||||
|
|
||||||
import { playAnime } from '../views/RSSView.svelte'
|
import { playAnime } from '../views/RSSView.svelte'
|
||||||
|
|
@ -15,7 +15,17 @@ window.addEventListener('paste', ({ clipboardData }) => { // WAIT image lookup o
|
||||||
const item = clipboardData.items[0]
|
const item = clipboardData.items[0]
|
||||||
if (!item) return
|
if (!item) return
|
||||||
const { type } = item
|
const { type } = item
|
||||||
if (type.startsWith('image')) return traceAnime(item.getAsFile())
|
if (type.startsWith('image')) {
|
||||||
|
const promise = traceAnime(item.getAsFile())
|
||||||
|
toast.promise(promise, {
|
||||||
|
description: 'You can also paste an URL to an image.',
|
||||||
|
loading: 'Looking up anime for image...',
|
||||||
|
success: 'Found anime for image!',
|
||||||
|
error: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.'
|
||||||
|
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!type.startsWith('text')) return
|
if (!type.startsWith('text')) return
|
||||||
item.getAsString(text => {
|
item.getAsString(text => {
|
||||||
if (torrentRx.exec(text)) {
|
if (torrentRx.exec(text)) {
|
||||||
|
|
@ -28,26 +38,19 @@ window.addEventListener('paste', ({ clipboardData }) => { // WAIT image lookup o
|
||||||
} else if (imageRx.exec(text)) {
|
} else if (imageRx.exec(text)) {
|
||||||
src = text
|
src = text
|
||||||
}
|
}
|
||||||
if (src) traceAnime(src)
|
if (src) {
|
||||||
|
const promise = traceAnime(src)
|
||||||
|
toast.promise(promise, {
|
||||||
|
description: 'You can also paste an URL to an image.',
|
||||||
|
loading: 'Looking up anime for image...',
|
||||||
|
success: 'Found anime for image!',
|
||||||
|
error: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
export async function traceAnime (image) { // WAIT lookup logic
|
export async function traceAnime (image) { // WAIT lookup logic
|
||||||
if (image instanceof Blob) {
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = e => {
|
|
||||||
addToast({
|
|
||||||
title: 'Looking up anime for image',
|
|
||||||
text: /* html */`You can also paste an URL to an image!<br><img class="w-200 rounded pt-5" src="${e.target.result}">`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(image)
|
|
||||||
} else {
|
|
||||||
addToast({
|
|
||||||
title: 'Looking up anime for image',
|
|
||||||
text: /* html */`<img class="w-200 rounded pt-5" src="${image}">`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
let options
|
let options
|
||||||
let url = `https://api.trace.moe/search?cutBorders&url=${image}`
|
let url = `https://api.trace.moe/search?cutBorders&url=${image}`
|
||||||
if (image instanceof Blob) {
|
if (image instanceof Blob) {
|
||||||
|
|
@ -60,14 +63,12 @@ export async function traceAnime (image) { // WAIT lookup logic
|
||||||
}
|
}
|
||||||
const res = await fetch(url, options)
|
const res = await fetch(url, options)
|
||||||
const { result } = await res.json()
|
const { result } = await res.json()
|
||||||
if (result && result[0].similarity >= 0.85) {
|
if (result?.[0].similarity >= 0.85) {
|
||||||
const res = await alRequest({ method: 'SearchIDSingle', id: result[0].anilist })
|
const res = await alRequest({ method: 'SearchIDSingle', id: result[0].anilist })
|
||||||
view.set(res.data.Media)
|
view.set(res.data.Media)
|
||||||
} else {
|
} else {
|
||||||
addToast({
|
throw new Error('Search Failed', {
|
||||||
text: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.',
|
message: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.'
|
||||||
title: 'Search Failed',
|
|
||||||
type: 'danger'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,10 +316,8 @@ export async function resolveSeason (opts) {
|
||||||
const obj = { media, episode: episode - offset, offset, increment, rootMedia, failed: true }
|
const obj = { media, episode: episode - offset, offset, increment, rootMedia, failed: true }
|
||||||
if (!force) {
|
if (!force) {
|
||||||
console.warn('Error in parsing!', obj)
|
console.warn('Error in parsing!', obj)
|
||||||
addToast({
|
toast('Parsing Error', {
|
||||||
text: /* html */`Failed resolving anime episode!<br>${media.title.userPreferred} - ${episode - offset}`,
|
description: `Failed resolving anime episode!\n${media.title.userPreferred} - ${episode - offset}`
|
||||||
title: 'Parsing Error',
|
|
||||||
type: 'secondary'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ export default async function tosho ({ media, episode }) {
|
||||||
found.seeders = complete
|
found.seeders = complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mapped?.length) throw new Error('no entries found')
|
||||||
|
|
||||||
return mapped
|
return mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { DOMPARSER } from '@/modules/util.js'
|
import { DOMPARSER } from '@/modules/util.js'
|
||||||
import { set } from '@/views/Settings.svelte'
|
import { set } from '@/views/Settings.svelte'
|
||||||
import { addToast } from '@/components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { add } from '@/modules/torrent.js'
|
import { add } from '@/modules/torrent.js'
|
||||||
import { resolveFileMedia, getEpisodeMetadataForMedia } from './anime.js'
|
import { resolveFileMedia, getEpisodeMetadataForMedia } from './anime.js'
|
||||||
import { hasNextPage } from '@/modules/sections.js'
|
import { hasNextPage } from '@/modules/sections.js'
|
||||||
|
|
@ -49,19 +49,15 @@ export async function getRSSContent (url) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
addToast({
|
toast.error('Search Failed', {
|
||||||
text: 'Failed fetching RSS!<br>' + res.statusText,
|
description: 'Failed fetching RSS!\n' + res.statusText
|
||||||
title: 'Search Failed',
|
|
||||||
type: 'danger'
|
|
||||||
})
|
})
|
||||||
console.error('Failed to fetch rss', res.statusText)
|
console.error('Failed to fetch rss', res.statusText)
|
||||||
}
|
}
|
||||||
return DOMPARSER(await res.text(), 'text/xml')
|
return DOMPARSER(await res.text(), 'text/xml')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addToast({
|
toast.error('Search Failed', {
|
||||||
text: 'Failed fetching RSS!<br>' + e.message,
|
description: 'Failed fetching RSS!\n' + e.message
|
||||||
title: 'Search Failed',
|
|
||||||
type: 'danger'
|
|
||||||
})
|
})
|
||||||
console.error('Failed to fetch rss', e)
|
console.error('Failed to fetch rss', e)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { set } from '@/views/Settings.svelte'
|
import { set } from '@/views/Settings.svelte'
|
||||||
|
|
||||||
export default function scroll (t, { speed = 120, smooth = 10 } = {}) {
|
export default function (t, { speed = 120, smooth = 10 } = {}) {
|
||||||
if (!set.smoothScroll) return
|
if (!set.smoothScroll) return
|
||||||
let moving = false
|
let moving = false
|
||||||
let pos = 0
|
let pos = 0
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import { alEntry } from '@/modules/anilist.js'
|
import { alEntry } from '@/modules/anilist.js'
|
||||||
import Subtitles from '@/modules/subtitles.js'
|
import Subtitles from '@/modules/subtitles.js'
|
||||||
import { toTS, videoRx, fastPrettyBytes } from '@/modules/util.js'
|
import { toTS, videoRx, fastPrettyBytes } from '@/modules/util.js'
|
||||||
import { addToast } from '../../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { getChaptersAniSkip } from '@/modules/anime.js'
|
import { getChaptersAniSkip } from '@/modules/anime.js'
|
||||||
import Seekbar from 'perfect-seekbar'
|
import Seekbar from 'perfect-seekbar'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
|
@ -71,10 +71,8 @@
|
||||||
function checkAudio () {
|
function checkAudio () {
|
||||||
if ('audioTracks' in HTMLVideoElement.prototype) {
|
if ('audioTracks' in HTMLVideoElement.prototype) {
|
||||||
if (!video.audioTracks.length) {
|
if (!video.audioTracks.length) {
|
||||||
addToast({
|
toast.error('Audio Codec Unsupported', {
|
||||||
text: "This torrent's audio codec is not supported, try a different release by disabling Autoplay Torrents in RSS settings.",
|
description: "This torrent's audio codec is not supported, try a different release by disabling Autoplay Torrents in RSS settings."
|
||||||
title: 'Audio Codec Unsupported',
|
|
||||||
type: 'danger'
|
|
||||||
})
|
})
|
||||||
} else if (video.audioTracks.length > 1) {
|
} else if (video.audioTracks.length > 1) {
|
||||||
const preferredTrack = [...video.audioTracks].find(({ language }) => language === set.audioLanguage)
|
const preferredTrack = [...video.audioTracks].find(({ language }) => language === set.audioLanguage)
|
||||||
|
|
@ -335,10 +333,8 @@
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
canvas.remove()
|
canvas.remove()
|
||||||
addToast({
|
toast.success('Screenshot', {
|
||||||
text: 'Saved screenshot to clipboard.',
|
description: 'Saved screenshot to clipboard.'
|
||||||
title: 'Screenshot',
|
|
||||||
type: 'success'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -830,31 +826,25 @@
|
||||||
break
|
break
|
||||||
case target.error.MEDIA_ERR_NETWORK:
|
case target.error.MEDIA_ERR_NETWORK:
|
||||||
console.warn('A network error caused the video download to fail part-way.', target.error)
|
console.warn('A network error caused the video download to fail part-way.', target.error)
|
||||||
addToast({
|
toast.error('Video Network Error', {
|
||||||
text: 'A network error caused the video download to fail part-way. Click here to reload the video.',
|
description: 'A network error caused the video download to fail part-way. Click here to reload the video.',
|
||||||
title: 'Video Network Error',
|
|
||||||
type: 'danger',
|
|
||||||
duration: 1000000,
|
duration: 1000000,
|
||||||
click: () => target.load()
|
onClick: () => target.load()
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case target.error.MEDIA_ERR_DECODE:
|
case target.error.MEDIA_ERR_DECODE:
|
||||||
console.warn('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.', target.error)
|
console.warn('The video playback was aborted due to a corruption problem or because the video used features your browser did not support.', target.error)
|
||||||
addToast({
|
toast.error('Video Decode Error', {
|
||||||
text: 'The video playback was aborted due to a corruption problem. Click here to reload the video.',
|
description: 'The video playback was aborted due to a corruption problem. Click here to reload the video.',
|
||||||
title: 'Video Decode Error',
|
|
||||||
type: 'danger',
|
|
||||||
duration: 1000000,
|
duration: 1000000,
|
||||||
click: () => target.load()
|
onClick: () => target.load()
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
case target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
||||||
if (target.error.message !== 'MEDIA_ELEMENT_ERROR: Empty src attribute') {
|
if (target.error.message !== 'MEDIA_ELEMENT_ERROR: Empty src attribute') {
|
||||||
console.warn('The video could not be loaded, either because the server or network failed or because the format is not supported.', target.error)
|
console.warn('The video could not be loaded, either because the server or network failed or because the format is not supported.', target.error)
|
||||||
addToast({
|
toast.error('Video Codec Unsupported', {
|
||||||
text: 'The video could not be loaded, either because the server or network failed or because the format is not supported. Try a different release by disabling Autoplay Torrents in RSS settings.',
|
description: 'The video could not be loaded, either because the server or network failed or because the format is not supported. Try a different release by disabling Autoplay Torrents in RSS settings.',
|
||||||
title: 'Video Codec Unsupported',
|
|
||||||
type: 'danger',
|
|
||||||
duration: 300000
|
duration: 300000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -881,8 +871,7 @@
|
||||||
on:keypress={resetImmerse}
|
on:keypress={resetImmerse}
|
||||||
on:mouseleave={immersePlayer}>
|
on:mouseleave={immersePlayer}>
|
||||||
{#if showKeybinds && !miniplayer}
|
{#if showKeybinds && !miniplayer}
|
||||||
<div class='position-absolute bg-tp w-full h-full z-50 font-size-12 p-20 d-flex align-items-center justify-content-center'>
|
<div class='position-absolute bg-tp w-full h-full z-50 font-size-12 p-20 d-flex align-items-center justify-content-center pointer' on:pointerup|self={() => (showKeybinds = false)} tabindex='-1' role='button'>
|
||||||
<button class='btn btn-square rounded-circle bg-dark font-size-16 top-0 right-0 m-10 position-absolute' type='button' use:click={() => (showKeybinds = false)}>×</button>
|
|
||||||
<Keybinds let:prop={item} autosave={true} clickable={true}>
|
<Keybinds let:prop={item} autosave={true} clickable={true}>
|
||||||
<div class:material-symbols-outlined={item?.type} class='bind'>{item?.id || ''}</div>
|
<div class:material-symbols-outlined={item?.type} class='bind'>{item?.id || ''}</div>
|
||||||
</Keybinds>
|
</Keybinds>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script context='module'>
|
<script context='module'>
|
||||||
import { since } from '@/modules/util.js'
|
import { since } from '@/modules/util.js'
|
||||||
import { set } from './Settings.svelte'
|
import { set } from './Settings.svelte'
|
||||||
import { addToast } from '../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { findInCurrent } from './Player/MediaHandler.svelte'
|
import { findInCurrent } from './Player/MediaHandler.svelte'
|
||||||
import getRSSEntries from '@/modules/providers/tosho.js'
|
import getRSSEntries from '@/modules/providers/tosho.js'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
|
@ -73,15 +73,19 @@
|
||||||
|
|
||||||
async function loadRss ({ media, episode }) {
|
async function loadRss ({ media, episode }) {
|
||||||
if (!media) return
|
if (!media) return
|
||||||
const entries = await getRSSEntries({ media, episode })
|
const promise = getRSSEntries({ media, episode })
|
||||||
if (!entries?.length) {
|
toast.promise(promise, {
|
||||||
addToast({
|
loading: `Looking for torrents for ${media.title.userPreferred} Episode ${parseInt(episode)}...`,
|
||||||
text: /* html */`Couldn't find torrent for ${media.title.userPreferred} Episode ${parseInt(episode)}! Try specifying a torrent manually.`,
|
success: `Found torrents for ${media.title.userPreferred} Episode ${parseInt(episode)}.`,
|
||||||
title: 'Search Failed',
|
error: `Couldn't find torrents for ${media.title.userPreferred} Episode ${parseInt(episode)}! Try specifying a torrent manually.`
|
||||||
type: 'danger'
|
})
|
||||||
})
|
let entries
|
||||||
return
|
try {
|
||||||
|
entries = await promise
|
||||||
|
} catch (e) {
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.sort((a, b) => b.seeders - a.seeders)
|
entries.sort((a, b) => b.seeders - a.seeders)
|
||||||
if (settings.rssAutoplay) {
|
if (settings.rssAutoplay) {
|
||||||
const best = entries.find(entry => entry.best)
|
const best = entries.find(entry => entry.best)
|
||||||
|
|
@ -103,10 +107,8 @@
|
||||||
function play (entry) {
|
function play (entry) {
|
||||||
$media = $rss
|
$media = $rss
|
||||||
if (entry.seeders !== '?' && entry.seeders <= 15) {
|
if (entry.seeders !== '?' && entry.seeders <= 15) {
|
||||||
addToast({
|
toast('Availability Warning', {
|
||||||
text: 'This release is poorly seeded and likely will have playback issues such as buffering!',
|
description: 'This release is poorly seeded and likely will have playback issues such as buffering!'
|
||||||
title: 'Availability Warning',
|
|
||||||
type: 'secondary'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
add(entry.link)
|
add(entry.link)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script context='module'>
|
<script context='module'>
|
||||||
import { addToast } from '../components/Toasts.svelte'
|
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
export let alToken = localStorage.getItem('ALtoken') || null
|
export let alToken = localStorage.getItem('ALtoken') || null
|
||||||
const defaults = {
|
const defaults = {
|
||||||
|
|
@ -64,17 +65,14 @@
|
||||||
window.IPC.on('update-available', () => {
|
window.IPC.on('update-available', () => {
|
||||||
if (!wasUpdated) {
|
if (!wasUpdated) {
|
||||||
wasUpdated = true
|
wasUpdated = true
|
||||||
addToast({
|
toast('Auto Updater', {
|
||||||
title: 'Auto Updater',
|
description: 'A new version of Miru is available. Downloading!'
|
||||||
text: 'A new version of Miru is available. Downloading!'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
window.IPC.on('update-downloaded', () => {
|
window.IPC.on('update-downloaded', () => {
|
||||||
addToast({
|
toast.success('Auto Updater', {
|
||||||
title: 'Auto Updater',
|
description: 'A new version of Miru has downloaded. You can restart to update!'
|
||||||
text: 'A new version of Miru has downloaded. You can restart to update!',
|
|
||||||
type: 'success'
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
function checkUpdate () {
|
function checkUpdate () {
|
||||||
|
|
@ -158,10 +156,8 @@
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(error)
|
console.warn(error)
|
||||||
addToast({
|
toast.error('File Error', {
|
||||||
text: /* html */`${error.message}<br>Try using a different font.`,
|
description: `${error.message}<br>Try using a different font.`,
|
||||||
title: 'File Error',
|
|
||||||
type: 'secondary',
|
|
||||||
duration: 8000
|
duration: 8000
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { getContext, setStatus } from 'svelte'
|
import { getContext, setStatus } from 'svelte'
|
||||||
import { getMediaMaxEp, formatMap, playMedia } from '@/modules/anime.js'
|
import { getMediaMaxEp, formatMap, playMedia } from '@/modules/anime.js'
|
||||||
import { playAnime } from '../RSSView.svelte'
|
import { playAnime } from '../RSSView.svelte'
|
||||||
import { addToast } from '../../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { alRequest } from '@/modules/anilist.js'
|
import { alRequest } from '@/modules/anilist.js'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
import Details from './Details.svelte'
|
import Details from './Details.svelte'
|
||||||
|
|
@ -64,11 +64,9 @@
|
||||||
}
|
}
|
||||||
function copyToClipboard (text) {
|
function copyToClipboard (text) {
|
||||||
navigator.clipboard.writeText(text)
|
navigator.clipboard.writeText(text)
|
||||||
addToast({
|
toast('Copied to clipboard', {
|
||||||
title: 'Copied to clipboard',
|
description: 'Copied share URL to clipboard',
|
||||||
text: 'Copied share URL to clipboard',
|
duration: 5000
|
||||||
type: 'primary',
|
|
||||||
duration: '5000'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function openInBrowser (url) {
|
function openInBrowser (url) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { alID } from '@/modules/anilist.js'
|
import { alID } from '@/modules/anilist.js'
|
||||||
import { add, client } from '@/modules/torrent.js'
|
import { add, client } from '@/modules/torrent.js'
|
||||||
import { generateRandomHexCode } from '@/modules/util.js'
|
import { generateRandomHexCode } from '@/modules/util.js'
|
||||||
import { addToast } from '../../components/Toasts.svelte'
|
import { toast } from 'svelte-sonner'
|
||||||
import { page } from '@/App.svelte'
|
import { page } from '@/App.svelte'
|
||||||
import P2PT from 'p2pt'
|
import P2PT from 'p2pt'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
|
@ -148,11 +148,9 @@
|
||||||
function invite () {
|
function invite () {
|
||||||
if (p2pt) {
|
if (p2pt) {
|
||||||
navigator.clipboard.writeText(`https://miru.watch/w2g/${p2pt.identifierString}`)
|
navigator.clipboard.writeText(`https://miru.watch/w2g/${p2pt.identifierString}`)
|
||||||
addToast({
|
toast('Copied to clipboard', {
|
||||||
title: 'Copied to clipboard',
|
description: 'Copied invite URL to clipboard',
|
||||||
text: 'Copied invite URL to clipboard',
|
duration: 5000
|
||||||
type: 'primary',
|
|
||||||
duration: '5000'
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue