mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-13 21:10:21 +00:00
feat: resolve anime by year
fix: remove dead findSubFiles legacy code chore: improve type declarations
This commit is contained in:
parent
fb6bdcab8a
commit
e92341ef9a
15 changed files with 101 additions and 62 deletions
|
|
@ -7,7 +7,9 @@
|
|||
"checkJs": true,
|
||||
"target": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext"
|
||||
"module": "ESNext",
|
||||
"types": ["./types.d.ts"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": ["node_modules/**", "**/node_modules", "dist", "build"]
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Miru",
|
||||
"version": "4.4.6",
|
||||
"version": "4.4.7",
|
||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
||||
"main": "build/main.js",
|
||||
|
|
|
|||
|
|
@ -306,12 +306,12 @@ export async function alRequest (opts) {
|
|||
case 'SearchName': {
|
||||
variables.search = opts.name
|
||||
query = /* js */`
|
||||
query($page: Int, $perPage: Int, $sort: [MediaSort], $search: String, $status: [MediaStatus]){
|
||||
query($page: Int, $perPage: Int, $sort: [MediaSort], $search: String, $status: [MediaStatus], $year: Int){
|
||||
Page(page: $page, perPage: $perPage){
|
||||
pageInfo{
|
||||
hasNextPage
|
||||
},
|
||||
media(type: ANIME, search: $search, sort: $sort, status_in: $status, isAdult: false, format_not: MUSIC){
|
||||
media(type: ANIME, search: $search, sort: $sort, status_in: $status, isAdult: false, format_not: MUSIC, seasonYear: $year){
|
||||
${queryObjects}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DOMPARSER, PromiseBatch, binarySearch } from './util.js'
|
||||
import { alRequest, alSearch } from './anilist.js'
|
||||
import anitomyscript from 'anitomyscript'
|
||||
import _anitomyscript from 'anitomyscript'
|
||||
import { toast } from 'svelte-sonner'
|
||||
import Sections from './sections.js'
|
||||
import { page } from '@/App.svelte'
|
||||
|
|
@ -86,9 +86,7 @@ export async function traceAnime (image) { // WAIT lookup logic
|
|||
key.value = {}
|
||||
page.value = 'search'
|
||||
} else {
|
||||
throw new Error('Search Failed', {
|
||||
message: 'Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.'
|
||||
})
|
||||
throw new Error('Search Failed \n Couldn\'t find anime for specified image! Try to remove black bars, or use a more detailed image.')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,8 +168,10 @@ const postfix = {
|
|||
3: 'rd'
|
||||
}
|
||||
|
||||
async function resolveTitle (name) {
|
||||
async function resolveTitle (parseObject) {
|
||||
const name = parseObject.anime_title
|
||||
const method = { name, method: 'SearchName', perPage: 10, status: ['RELEASING', 'FINISHED'], sort: 'SEARCH_MATCH' }
|
||||
if (parseObject.anime_year) method.year = parseObject.anime_year
|
||||
|
||||
// inefficient but readable
|
||||
|
||||
|
|
@ -212,50 +212,64 @@ async function resolveTitle (name) {
|
|||
media = (await alSearch(method)).data.Page.media[0]
|
||||
}
|
||||
}
|
||||
// remove 2020
|
||||
if (!media) {
|
||||
const match = method.name.match(/ (19[5-9]\d|20\d{2})/)
|
||||
if (match) {
|
||||
method.name = method.name.replace(/ (19[5-9]\d|20\d{2})/, '')
|
||||
media = (await alSearch(method)).data.Page.media[0]
|
||||
}
|
||||
}
|
||||
} catch (e) { }
|
||||
|
||||
if (media) relations[name] = media
|
||||
if (media) relations[getRelationKey(parseObject)] = media
|
||||
}
|
||||
|
||||
function getParseObjTitle (obj) {
|
||||
let title = obj.anime_title
|
||||
if (obj.anime_year) title += ` ${obj.anime_year}`
|
||||
if (obj.anime_season > 1) title += ' S' + obj.anime_season
|
||||
const match = title.match(/S(\d{2})E(\d{2})/)
|
||||
if (match) {
|
||||
obj.anime_season = Number(match[1])
|
||||
obj.episode_number = Number(match[2])
|
||||
title = title.replace(/S(\d{2})E(\d{2})/, '')
|
||||
// utility method for correcting anitomyscript woes for what's needed
|
||||
export async function anitomyscript (...args) {
|
||||
// @ts-ignore
|
||||
const res = await _anitomyscript(...args)
|
||||
|
||||
const parseObjs = Array.isArray(res) ? res : [res]
|
||||
|
||||
for (const obj of parseObjs) {
|
||||
const seasonMatch = obj.anime_title.match(/S(\d{2})E(\d{2})/)
|
||||
if (seasonMatch) {
|
||||
obj.anime_season = seasonMatch[1]
|
||||
obj.episode_number = seasonMatch[2]
|
||||
obj.anime_title = obj.anime_title.replace(/S(\d{2})E(\d{2})/, '')
|
||||
}
|
||||
const yearMatch = obj.anime_title.match(/ (19[5-9]\d|20\d{2})/)
|
||||
if (yearMatch && Number(yearMatch[1]) <= (new Date().getUTCFullYear() + 1)) {
|
||||
obj.anime_year = yearMatch[1]
|
||||
obj.anime_title = obj.anime_title.replace(/ (19[5-9]\d|20\d{2})/, '')
|
||||
}
|
||||
if (Number(obj.anime_season) > 1) obj.anime_title += ' S' + obj.anime_season
|
||||
}
|
||||
return title
|
||||
|
||||
return parseObjs
|
||||
}
|
||||
|
||||
window.xd = resolveFileMedia
|
||||
function getRelationKey (obj) {
|
||||
let key = obj.anime_title
|
||||
if (obj.anime_year) key += obj.anime_year
|
||||
return key
|
||||
}
|
||||
|
||||
// TODO: anidb aka true episodes need to be mapped to anilist episodes a bit better
|
||||
export async function resolveFileMedia (fileName) {
|
||||
let parseObjs = await anitomyscript(fileName)
|
||||
const parseObjs = await anitomyscript(fileName)
|
||||
|
||||
if (parseObjs.constructor !== Array) parseObjs = [parseObjs]
|
||||
// batches promises in 10 at a time, because of CF burst protection, which still sometimes gets triggered :/
|
||||
await PromiseBatch(resolveTitle, [...new Set(parseObjs.map(obj => getParseObjTitle(obj)))].filter(title => !(title in relations)), 10)
|
||||
const uniq = {}
|
||||
for (const obj of parseObjs) {
|
||||
const key = getRelationKey(obj)
|
||||
if (key in relations) continue
|
||||
uniq[key] = obj
|
||||
}
|
||||
await PromiseBatch(resolveTitle, Object.values(uniq), 10)
|
||||
|
||||
const fileMedias = []
|
||||
for (const parseObj of parseObjs) {
|
||||
let failed = false
|
||||
let episode
|
||||
let media = relations[getParseObjTitle(parseObj)]
|
||||
let media = relations[getRelationKey(parseObj)]
|
||||
// resolve episode, if movie, dont.
|
||||
const maxep = media?.nextAiringEpisode?.episode || media?.episodes
|
||||
if ((media?.format !== 'MOVIE' || maxep) && parseObj.episode_number) {
|
||||
if (parseObj.episode_number.constructor === Array) {
|
||||
if (Array.isArray(parseObj.episode_number)) {
|
||||
// is an episode range
|
||||
if (parseInt(parseObj.episode_number[0]) === 1) {
|
||||
// if it starts with #1 and overflows then it includes more than 1 season in a batch, cant fix this cleanly, name is parsed per file basis so this shouldnt be an issue
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
let lastTapElement = null
|
||||
|
||||
const noop = () => {}
|
||||
const noop = _ => {}
|
||||
|
||||
document.addEventListener('pointerup', () => {
|
||||
lastTapElement?.dispatchEvent(new Event('custom-pointerleave'))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { alRequest } from '@/modules/anilist.js'
|
||||
import { set } from '@/views/Settings.svelte'
|
||||
import { findEdge, resolveSeason, getMediaMaxEp } from './anime.js'
|
||||
import { findEdge, resolveSeason, getMediaMaxEp, mapBestRelease } from '../anime.js'
|
||||
import { exclusions, getRSSContent, parseRSSNodes } from '../rss.js'
|
||||
import { mapBestRelease } from '../anime.js'
|
||||
|
||||
export default async function getRSSEntries ({ media, episode, mode, ignoreQuality }) {
|
||||
// mode cuts down on the amt of queries made 'check' || 'batch'
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { mapBestRelease } from '../anime.js'
|
||||
import { mapBestRelease, anitomyscript } from '../anime.js'
|
||||
import { fastPrettyBytes } from '../util.js'
|
||||
import { exclusions } from '../rss.js'
|
||||
import { set } from '@/views/Settings.svelte'
|
||||
import { alRequest } from '../anilist.js'
|
||||
import { client } from '@/modules/torrent.js'
|
||||
|
||||
import anitomyscript from 'anitomyscript'
|
||||
|
||||
export default async function ({ media, episode }) {
|
||||
const json = await getAniDBFromAL(media)
|
||||
|
||||
|
|
@ -101,8 +99,8 @@ export function getEpisodeNumberByAirDate (alDate, episodes, episode) {
|
|||
// ineffcient but reliable
|
||||
const closestEpisodes = Object.values(episodes).reduce((prev, curr) => {
|
||||
if (!prev[0]) return [curr]
|
||||
const prevDate = Math.abs(new Date(prev[0]?.airdate) - alDate)
|
||||
const currDate = Math.abs(new Date(curr.airdate) - alDate)
|
||||
const prevDate = Math.abs(+new Date(prev[0]?.airdate) - alDate)
|
||||
const currDate = Math.abs(+new Date(curr.airdate) - alDate)
|
||||
if (prevDate === currDate) {
|
||||
prev.push(curr)
|
||||
return prev
|
||||
|
|
@ -176,10 +174,10 @@ function isTitleSplitCour (media) {
|
|||
}
|
||||
|
||||
const seasons = ['WINTER', 'SPRING', 'SUMMER', 'FALL']
|
||||
const getDate = ({ seasonYear, season }) => new Date(`${seasonYear}-${seasons.indexOf(season) * 4 || 1}-01`)
|
||||
const getDate = ({ seasonYear, season }) => +new Date(`${seasonYear}-${seasons.indexOf(season) * 4 || 1}-01`)
|
||||
|
||||
function getMediaDate (media) {
|
||||
if (media.startDate) return new Date(Object.values(media.startDate).join(' '))
|
||||
if (media.startDate) return +new Date(Object.values(media.startDate).join(' '))
|
||||
return getDate(media)
|
||||
}
|
||||
|
||||
|
|
@ -242,7 +240,7 @@ function buildQuery (quality) {
|
|||
return query
|
||||
}
|
||||
|
||||
async function fetchBatches ({ episodeCount, id, quality, movie }) {
|
||||
async function fetchBatches ({ episodeCount, id, quality, movie = null }) {
|
||||
try {
|
||||
const queryString = buildQuery(quality)
|
||||
const torrents = await fetch(set.toshoURL + 'json?order=size-d&aid=' + id + queryString)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class RSSMediaManager {
|
|||
|
||||
if (!content) return false
|
||||
|
||||
const pubDate = new Date(content.querySelector('pubDate').textContent) * page * perPage
|
||||
const pubDate = +(new Date(content.querySelector('pubDate').textContent)) * page * perPage
|
||||
if (this.resultMap[url]?.date === pubDate) return false
|
||||
return { content, pubDate }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,6 @@ export default function (t, { speed = 120, smooth = 10 } = {}) {
|
|||
scrollTop += delta
|
||||
|
||||
t.scrollTo(0, scrollTop < 1.3 ? 0 : scrollTop)
|
||||
moving = Math.abs(delta) > 0.1 && requestAnimationFrame(update)
|
||||
moving = Math.abs(delta) > 0.1 && !!requestAnimationFrame(update)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,19 +122,6 @@ export default class Subtitles {
|
|||
clipboard.on('files', this.handleClipboardFiles)
|
||||
}
|
||||
|
||||
findSubtitleFiles (targetFile) {
|
||||
const videoName = targetFile.name.substring(0, targetFile.name.lastIndexOf('.')) || targetFile.name
|
||||
// array of subtitle files that match video name, or all subtitle files when only 1 vid file
|
||||
const subfiles = this.files.filter(file => {
|
||||
return !this.subtitleFiles.some(sub => { // exclude already existing files
|
||||
return sub.lastModified === file.lastModified && sub.name === file.name && sub.size === file.size
|
||||
}) && subRx.test(file.name) && (this.videoFiles.length === 1 ? true : file.name.includes(videoName))
|
||||
})
|
||||
for (const file of subfiles) {
|
||||
this.addSingleSubtitleFile(file)
|
||||
}
|
||||
}
|
||||
|
||||
async addSingleSubtitleFile (file) {
|
||||
const index = this.headers.length
|
||||
this.subtitleFiles[index] = file
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class TorrentWorker extends EventTarget {
|
|||
this.ready = new Promise(resolve => {
|
||||
window.IPC.once('port', () => {
|
||||
this.port = window.port
|
||||
this.port.onmessage((...args) => this.handleMessage(...args))
|
||||
this.port.onmessage(this.handleMessage.bind(this))
|
||||
resolve()
|
||||
})
|
||||
window.IPC.emit('portRequest')
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ export function fastPrettyBytes (num) {
|
|||
return Number((num / Math.pow(1000, exponent)).toFixed(2)) + units[exponent]
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {DOMParser['parseFromString']}
|
||||
*/
|
||||
export const DOMPARSER = DOMParser.prototype.parseFromString.bind(new DOMParser())
|
||||
|
||||
export const sleep = t => new Promise(resolve => setTimeout(resolve, t))
|
||||
|
|
@ -59,7 +62,13 @@ export function toTS (sec, full) {
|
|||
}
|
||||
}
|
||||
const hours = Math.floor(sec / 3600)
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let minutes = Math.floor(sec / 60) - hours * 60
|
||||
/**
|
||||
* @type {any}
|
||||
*/
|
||||
let seconds = full === 1 ? (sec % 60).toFixed(2) : Math.floor(sec % 60)
|
||||
if (minutes < 10 && (hours > 0 || full)) minutes = '0' + minutes
|
||||
if (seconds < 10) seconds = '0' + seconds
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@
|
|||
if (videos?.length) {
|
||||
if (subs) {
|
||||
subs.files = files || []
|
||||
subs.findSubtitleFiles(current)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/renderer/*"],
|
||||
},
|
||||
"checkJs": true,
|
||||
"target": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext",
|
||||
"types": ["./types.d.ts"],
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": ["node_modules/**", "**/node_modules", "dist", "build"]
|
||||
}
|
||||
16
types.d.ts
vendored
Normal file
16
types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export {}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
IPC: any;
|
||||
port: MessagePort
|
||||
}
|
||||
interface EventTarget {
|
||||
on: (type: string, callback: (any) => void, options?: boolean | {}) => void
|
||||
once: (type: string, callback: (any) => void, options?: boolean | {}) => void
|
||||
emit: (type: string, data?: any) => void
|
||||
dispatch: (type: string, data?: any) => void
|
||||
removeListener: (type: string, callback: (any) => void) => void
|
||||
off: (type: string, callback: (any) => void) => void
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue