updated electron and changed video token invalidation process
This commit is contained in:
parent
38d58a7dd7
commit
4cb0c59ccf
@ -29,7 +29,7 @@
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"concurrently": "^8.2.2",
|
||||
"dotenv": "^16.4.5",
|
||||
"electron": "^30.0.6",
|
||||
"electron": "^30.0.8",
|
||||
"electron-builder": "^24.13.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
|
647
pnpm-lock.yaml
generated
647
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -502,7 +502,7 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
|
||||
throw new Error(e as string)
|
||||
}
|
||||
|
||||
if (isProxyActive) await deactivateVideoToken(q, playlist.token)
|
||||
if (isProxyActive) await deleteVideoToken(q, playlist.token)
|
||||
decrementPlaylistCounter()
|
||||
|
||||
for (const p of proxies) {
|
||||
@ -599,7 +599,7 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
|
||||
}
|
||||
|
||||
// Crunchyroll Delete Video Token Fetch
|
||||
export async function deleteVideoToken(content: string, token: string) {
|
||||
export async function removeVideoToken(content: string, token: string) {
|
||||
const account = await loggedInCheck('CR')
|
||||
|
||||
if (!account) return
|
||||
@ -644,7 +644,7 @@ export async function deleteVideoToken(content: string, token: string) {
|
||||
}
|
||||
|
||||
// Crunchyroll Deactivate Video Token Fetch
|
||||
export async function deactivateVideoToken(content: string, token: string) {
|
||||
export async function deleteVideoToken(content: string, token: string) {
|
||||
const account = await loggedInCheck('CR')
|
||||
|
||||
if (!account) return
|
||||
@ -748,6 +748,15 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
||||
'User-Agent': 'Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27'
|
||||
}
|
||||
|
||||
const regex = /\/manifest\/([A-Z0-9]+)\/.*\?playbackGuid=([^&]+)/
|
||||
|
||||
const match = q.match(regex)
|
||||
|
||||
if (!match) return
|
||||
|
||||
const contentID = match[1]
|
||||
const playlistID = match[2]
|
||||
|
||||
try {
|
||||
const response = await fetch(q, {
|
||||
method: 'GET',
|
||||
@ -755,6 +764,7 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
await deleteVideoToken(contentID, playlistID)
|
||||
const raw = await response.text()
|
||||
|
||||
const parsed = mpdParse(raw)
|
||||
@ -762,6 +772,19 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
||||
return parsed
|
||||
} else {
|
||||
const error = await response.text()
|
||||
|
||||
const errorJSON: {
|
||||
error: string
|
||||
} = await JSON.parse(error)
|
||||
|
||||
if (errorJSON.error === 'Invalid streaming token') {
|
||||
decrementPlaylistCounter()
|
||||
|
||||
await incrementPlaylistCounter()
|
||||
await activateVideoToken(contentID, playlistID)
|
||||
return await crunchyGetPlaylistMPD(q, geo)
|
||||
}
|
||||
|
||||
messageBox('error', ['Cancel'], 2, 'Failed to get Crunchyroll MPD', 'Failed to get Crunchyroll MPD', error)
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
@ -770,7 +793,7 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'mpdCrunchyrollFetch'
|
||||
})
|
||||
throw new Error(await response.text())
|
||||
throw new Error(error)
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e as string)
|
||||
|
@ -4,7 +4,7 @@ import { concatenateTSFiles } from '../../services/concatenate'
|
||||
import { createFolder, createFolderName, deleteFolder, deleteTemporaryFolders } from '../../services/folder'
|
||||
import { downloadADNSub, downloadCRSub } from '../../services/subs'
|
||||
import { CrunchyEpisode } from '../../types/crunchyroll'
|
||||
import { checkAccountMaxStreams, crunchyGetPlaylist, crunchyGetPlaylistMPD, deleteVideoToken } from '../crunchyroll/crunchyroll.service'
|
||||
import { checkAccountMaxStreams, crunchyGetPlaylist, crunchyGetPlaylistMPD } from '../crunchyroll/crunchyroll.service'
|
||||
import fs from 'fs'
|
||||
var cron = require('node-cron')
|
||||
import { Readable } from 'stream'
|
||||
@ -490,7 +490,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
if (playlist.data.audioLocale !== subs[0]) {
|
||||
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
||||
if (found) {
|
||||
await deleteVideoToken(episodeID, playlist.data.token)
|
||||
playlist = await crunchyGetPlaylist(found.guid, found.geo)
|
||||
} else {
|
||||
console.log('Exact Playlist not found, taking what crunchy gives.')
|
||||
@ -518,8 +517,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
return
|
||||
}
|
||||
|
||||
await deleteVideoToken(episodeID, playlist.data.token)
|
||||
|
||||
const subFolder = await createFolder()
|
||||
|
||||
const audioFolder = await createFolder()
|
||||
@ -609,8 +606,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
section: 'crunchyrollDownloadProcessSubtitles'
|
||||
})
|
||||
}
|
||||
|
||||
await deleteVideoToken(episodeID, subPlaylist.data.token)
|
||||
}
|
||||
|
||||
await updatePlaylistByID(downloadID, 'waiting for dub playlist')
|
||||
@ -624,8 +619,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
if (found) {
|
||||
const list = await crunchyGetPlaylist(found.guid, found.geo)
|
||||
if (list) {
|
||||
await deleteVideoToken(episodeID, list.data.token)
|
||||
|
||||
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
|
||||
if (foundSub) {
|
||||
subDownloadList.push({ ...foundSub, isDub: true })
|
||||
@ -672,17 +665,18 @@ export async function downloadCrunchyrollPlaylist(
|
||||
const sbs: Array<string> = []
|
||||
for (const sub of subDownloadList) {
|
||||
const name = await downloadCRSub(sub, subFolder, quality)
|
||||
if (!name) return
|
||||
sbs.push(name)
|
||||
}
|
||||
return sbs
|
||||
}
|
||||
|
||||
const audioDownload = async () => {
|
||||
const audios: Array<string> = []
|
||||
const audios: Array<string | undefined> = new Array(dubDownloadList.length).fill(undefined)
|
||||
const concurrentDownloads = 2
|
||||
const tasks: Array<Promise<void>> = []
|
||||
|
||||
const downloadTask = async (v: any) => {
|
||||
const downloadTask = async (v: any, index: number) => {
|
||||
const list = await crunchyGetPlaylist(v.guid, v.geo)
|
||||
|
||||
if (!list) return
|
||||
@ -691,8 +685,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
|
||||
if (!playlist) return
|
||||
|
||||
await deleteVideoToken(episodeID, list.data.token)
|
||||
|
||||
const assetId = playlist.mediaGroups.AUDIO.audio.main.playlists[0].segments[0].resolvedUri.match(/\/assets\/(?:p\/)?([^_,]+)/)
|
||||
|
||||
if (!assetId) {
|
||||
@ -764,12 +756,12 @@ export async function downloadCrunchyrollPlaylist(
|
||||
const path = await downloadMPDAudio(p, audioFolder, list.data.audioLocale, downloadID, keys ? keys : undefined)
|
||||
|
||||
if (path) {
|
||||
audios.push(path as string)
|
||||
audios[index] = path as string
|
||||
}
|
||||
}
|
||||
|
||||
for (const v of dubDownloadList) {
|
||||
const task = downloadTask(v).finally(() => {
|
||||
for (const [index, v] of dubDownloadList.entries()) {
|
||||
const task = downloadTask(v, index).finally(() => {
|
||||
tasks.splice(tasks.indexOf(task), 1)
|
||||
})
|
||||
|
||||
@ -782,7 +774,7 @@ export async function downloadCrunchyrollPlaylist(
|
||||
|
||||
await Promise.all(tasks)
|
||||
|
||||
return audios
|
||||
return audios.filter((path) => path !== undefined) as string[]
|
||||
}
|
||||
|
||||
const downloadVideo = async () => {
|
||||
@ -866,8 +858,6 @@ export async function downloadCrunchyrollPlaylist(
|
||||
|
||||
if (!mdp) return
|
||||
|
||||
await deleteVideoToken(episodeID, play.data.token)
|
||||
|
||||
var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.height === quality)
|
||||
|
||||
if (!hq) {
|
||||
@ -987,6 +977,8 @@ export async function downloadCrunchyrollPlaylist(
|
||||
|
||||
if (!audios) return
|
||||
|
||||
if (!subss) return
|
||||
|
||||
await updatePlaylistByID(downloadID, 'merging video & audio')
|
||||
|
||||
var episodeNaming = (await settings.get('EpisodeTemp')) as string
|
||||
@ -1308,8 +1300,25 @@ export async function checkProxies() {
|
||||
|
||||
if (response.ok) {
|
||||
p.status = 'online'
|
||||
server.logger.log({
|
||||
level: 'info',
|
||||
message: 'Proxy fetch successful, marking as online',
|
||||
proxy: p.name,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'checkProxyFetch'
|
||||
})
|
||||
} else {
|
||||
const data = await response.text()
|
||||
|
||||
p.status = 'offline'
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
message: 'Proxy fetch failed, marking as offline',
|
||||
proxy: p.name,
|
||||
error: data,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'checkProxyFetch'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,17 +42,23 @@ export async function downloadMPDAudio(
|
||||
audio: name
|
||||
})
|
||||
|
||||
const dn = downloading.find((i) => i.id === downloadID && i.audio === name)
|
||||
|
||||
for (const [index, part] of parts.entries()) {
|
||||
let success = false
|
||||
while (!success) {
|
||||
try {
|
||||
var stream
|
||||
|
||||
const response = await fetch(part.url)
|
||||
|
||||
if (!response.ok) {
|
||||
throw Error(await response.text())
|
||||
}
|
||||
|
||||
stream = fs.createWriteStream(`${path}/${part.filename}`)
|
||||
|
||||
const { body } = await fetch(part.url)
|
||||
|
||||
const readableStream = Readable.from(body as any)
|
||||
const readableStream = Readable.from(response.body as any)
|
||||
|
||||
await finished(readableStream.pipe(stream))
|
||||
|
||||
@ -60,6 +66,9 @@ export async function downloadMPDAudio(
|
||||
|
||||
success = true
|
||||
} catch (error) {
|
||||
if (dn) {
|
||||
dn.status = 'failed'
|
||||
}
|
||||
console.error(`Error occurred during download of fragment ${index + 1}:`, error)
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
@ -138,6 +147,17 @@ async function mergePartsAudio(
|
||||
.input(concatenatedFile)
|
||||
.outputOptions('-c copy')
|
||||
.save(`${dir}/${name}.aac`)
|
||||
.on('error', (error) => {
|
||||
console.log(error)
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
message: `Error merging audio fragments of Download ${downloadID} Audio ${name}`,
|
||||
error: error,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'crunchyrollDownloadProcessAudioMergingFFMPEG'
|
||||
})
|
||||
reject(error)
|
||||
})
|
||||
.on('end', async () => {
|
||||
console.log('Merging finished')
|
||||
await deleteFolder(tmp)
|
||||
|
@ -1,10 +1,18 @@
|
||||
import fs from 'fs'
|
||||
import { server } from '../api'
|
||||
|
||||
export async function concatenateTSFiles(inputFiles: Array<string>, outputFile: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const writeStream = fs.createWriteStream(outputFile)
|
||||
|
||||
writeStream.on('error', (error) => {
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
message: `Error while concatenating`,
|
||||
error: error,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'crunchyrollDownloadProcessConcatenate'
|
||||
})
|
||||
reject(error)
|
||||
})
|
||||
|
||||
@ -22,6 +30,13 @@ export async function concatenateTSFiles(inputFiles: Array<string>, outputFile:
|
||||
const readStream = fs.createReadStream(inputFiles[index])
|
||||
|
||||
readStream.on('error', (error) => {
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
message: `Error while concatenating`,
|
||||
error: error,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'crunchyrollDownloadProcessConcatenate'
|
||||
})
|
||||
reject(error)
|
||||
})
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { parse, stringify } from 'ass-compiler'
|
||||
import { Readable } from 'stream'
|
||||
import { finished } from 'stream/promises'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { server } from '../api'
|
||||
|
||||
export async function downloadCRSub(
|
||||
sub: {
|
||||
@ -14,61 +15,72 @@ export async function downloadCRSub(
|
||||
dir: string,
|
||||
qual: 1080 | 720 | 480 | 360 | 240
|
||||
) {
|
||||
const path = `${dir}/${sub.language}${sub.isDub ? `-FORCED` : ''}.${sub.format}`
|
||||
var qualX
|
||||
var qualY
|
||||
try {
|
||||
const path = `${dir}/${sub.language}${sub.isDub ? `-FORCED` : ''}.${sub.format}`
|
||||
var qualX
|
||||
var qualY
|
||||
|
||||
switch (qual) {
|
||||
case 1080:
|
||||
qualX = 1920
|
||||
qualY = 1080
|
||||
break
|
||||
case 720:
|
||||
qualX = 1280
|
||||
qualY = 720
|
||||
break
|
||||
case 480:
|
||||
qualX = 720
|
||||
qualY = 480
|
||||
break
|
||||
case 360:
|
||||
qualX = 640
|
||||
qualY = 360
|
||||
break
|
||||
case 240:
|
||||
qualX = 426
|
||||
qualY = 240
|
||||
break
|
||||
switch (qual) {
|
||||
case 1080:
|
||||
qualX = 1920
|
||||
qualY = 1080
|
||||
break
|
||||
case 720:
|
||||
qualX = 1280
|
||||
qualY = 720
|
||||
break
|
||||
case 480:
|
||||
qualX = 720
|
||||
qualY = 480
|
||||
break
|
||||
case 360:
|
||||
qualX = 640
|
||||
qualY = 360
|
||||
break
|
||||
case 240:
|
||||
qualX = 426
|
||||
qualY = 240
|
||||
break
|
||||
}
|
||||
|
||||
const stream = fs.createWriteStream(path)
|
||||
const response = await fetch(sub.url)
|
||||
|
||||
var parsedASS = parse(await response.text())
|
||||
|
||||
parsedASS.info['Original Script'] = 'crd [https://github.com/stratuma/]'
|
||||
|
||||
for (const s of parsedASS.styles.style) {
|
||||
;(s.Fontsize = String(Math.round((parseInt(s.Fontsize) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.Outline = String(Math.round((parseInt(s.Outline) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginL = String(Math.round((parseInt(s.MarginL) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginR = String(Math.round((parseInt(s.MarginR) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginV = String(Math.round((parseInt(s.MarginV) / parseInt(parsedASS.info.PlayResY)) * qualY)))
|
||||
}
|
||||
|
||||
parsedASS.info.PlayResX = String(qualX)
|
||||
parsedASS.info.PlayResY = String(qualY)
|
||||
|
||||
const fixed = stringify(parsedASS)
|
||||
|
||||
const resampledSubs = resamplePOSSubtitle(fixed, 640, 360, qualX, qualY)
|
||||
|
||||
const readableStream = Readable.from([resampledSubs])
|
||||
|
||||
await finished(readableStream.pipe(stream))
|
||||
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
||||
|
||||
return path
|
||||
} catch (e) {
|
||||
console.log('Failed to download and parse subs')
|
||||
server.logger.log({
|
||||
level: 'error',
|
||||
message: 'Failed to download and parse subs',
|
||||
error: e,
|
||||
timestamp: new Date().toISOString(),
|
||||
section: 'subDownloadProcess'
|
||||
})
|
||||
}
|
||||
|
||||
const stream = fs.createWriteStream(path)
|
||||
const response = await fetch(sub.url)
|
||||
|
||||
var parsedASS = parse(await response.text())
|
||||
|
||||
parsedASS.info['Original Script'] = 'crd [https://github.com/stratuma/]'
|
||||
|
||||
for (const s of parsedASS.styles.style) {
|
||||
;(s.Fontsize = String(Math.round((parseInt(s.Fontsize) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.Outline = String(Math.round((parseInt(s.Outline) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginL = String(Math.round((parseInt(s.MarginL) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginR = String(Math.round((parseInt(s.MarginR) / parseInt(parsedASS.info.PlayResY)) * qualY))),
|
||||
(s.MarginV = String(Math.round((parseInt(s.MarginV) / parseInt(parsedASS.info.PlayResY)) * qualY)))
|
||||
}
|
||||
|
||||
parsedASS.info.PlayResX = String(qualX)
|
||||
parsedASS.info.PlayResY = String(qualY)
|
||||
|
||||
const fixed = stringify(parsedASS)
|
||||
|
||||
const resampledSubs = resamplePOSSubtitle(fixed, 640, 360, qualX, qualY)
|
||||
|
||||
const readableStream = Readable.from([resampledSubs])
|
||||
|
||||
await finished(readableStream.pipe(stream))
|
||||
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
function resamplePOSSubtitle(subtitle: string, ox: number, oy: number, nx: number, ny: number) {
|
||||
|
Reference in New Issue
Block a user