updated electron and changed video token invalidation process

This commit is contained in:
stratuma 2024-05-24 15:48:17 +02:00
parent 38d58a7dd7
commit 4cb0c59ccf
7 changed files with 479 additions and 405 deletions

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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'
})
}
}

View File

@ -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)

View File

@ -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)
})

View File

@ -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) {