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",
|
"@types/node-cron": "^3.0.11",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"electron": "^30.0.6",
|
"electron": "^30.0.8",
|
||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^24.13.3",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.10.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)
|
throw new Error(e as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isProxyActive) await deactivateVideoToken(q, playlist.token)
|
if (isProxyActive) await deleteVideoToken(q, playlist.token)
|
||||||
decrementPlaylistCounter()
|
decrementPlaylistCounter()
|
||||||
|
|
||||||
for (const p of proxies) {
|
for (const p of proxies) {
|
||||||
@ -599,7 +599,7 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll Delete Video Token Fetch
|
// 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')
|
const account = await loggedInCheck('CR')
|
||||||
|
|
||||||
if (!account) return
|
if (!account) return
|
||||||
@ -644,7 +644,7 @@ export async function deleteVideoToken(content: string, token: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll Deactivate Video Token Fetch
|
// 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')
|
const account = await loggedInCheck('CR')
|
||||||
|
|
||||||
if (!account) return
|
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'
|
'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 {
|
try {
|
||||||
const response = await fetch(q, {
|
const response = await fetch(q, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@ -755,6 +764,7 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
await deleteVideoToken(contentID, playlistID)
|
||||||
const raw = await response.text()
|
const raw = await response.text()
|
||||||
|
|
||||||
const parsed = mpdParse(raw)
|
const parsed = mpdParse(raw)
|
||||||
@ -762,6 +772,19 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
|||||||
return parsed
|
return parsed
|
||||||
} else {
|
} else {
|
||||||
const error = await response.text()
|
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)
|
messageBox('error', ['Cancel'], 2, 'Failed to get Crunchyroll MPD', 'Failed to get Crunchyroll MPD', error)
|
||||||
server.logger.log({
|
server.logger.log({
|
||||||
level: 'error',
|
level: 'error',
|
||||||
@ -770,7 +793,7 @@ export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined)
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
section: 'mpdCrunchyrollFetch'
|
section: 'mpdCrunchyrollFetch'
|
||||||
})
|
})
|
||||||
throw new Error(await response.text())
|
throw new Error(error)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(e as string)
|
throw new Error(e as string)
|
||||||
|
@ -4,7 +4,7 @@ import { concatenateTSFiles } from '../../services/concatenate'
|
|||||||
import { createFolder, createFolderName, deleteFolder, deleteTemporaryFolders } from '../../services/folder'
|
import { createFolder, createFolderName, deleteFolder, deleteTemporaryFolders } from '../../services/folder'
|
||||||
import { downloadADNSub, downloadCRSub } from '../../services/subs'
|
import { downloadADNSub, downloadCRSub } from '../../services/subs'
|
||||||
import { CrunchyEpisode } from '../../types/crunchyroll'
|
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'
|
import fs from 'fs'
|
||||||
var cron = require('node-cron')
|
var cron = require('node-cron')
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
@ -490,7 +490,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
if (playlist.data.audioLocale !== subs[0]) {
|
if (playlist.data.audioLocale !== subs[0]) {
|
||||||
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
||||||
if (found) {
|
if (found) {
|
||||||
await deleteVideoToken(episodeID, playlist.data.token)
|
|
||||||
playlist = await crunchyGetPlaylist(found.guid, found.geo)
|
playlist = await crunchyGetPlaylist(found.guid, found.geo)
|
||||||
} else {
|
} else {
|
||||||
console.log('Exact Playlist not found, taking what crunchy gives.')
|
console.log('Exact Playlist not found, taking what crunchy gives.')
|
||||||
@ -518,8 +517,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteVideoToken(episodeID, playlist.data.token)
|
|
||||||
|
|
||||||
const subFolder = await createFolder()
|
const subFolder = await createFolder()
|
||||||
|
|
||||||
const audioFolder = await createFolder()
|
const audioFolder = await createFolder()
|
||||||
@ -609,8 +606,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
section: 'crunchyrollDownloadProcessSubtitles'
|
section: 'crunchyrollDownloadProcessSubtitles'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await deleteVideoToken(episodeID, subPlaylist.data.token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await updatePlaylistByID(downloadID, 'waiting for dub playlist')
|
await updatePlaylistByID(downloadID, 'waiting for dub playlist')
|
||||||
@ -624,8 +619,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
if (found) {
|
if (found) {
|
||||||
const list = await crunchyGetPlaylist(found.guid, found.geo)
|
const list = await crunchyGetPlaylist(found.guid, found.geo)
|
||||||
if (list) {
|
if (list) {
|
||||||
await deleteVideoToken(episodeID, list.data.token)
|
|
||||||
|
|
||||||
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
|
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
|
||||||
if (foundSub) {
|
if (foundSub) {
|
||||||
subDownloadList.push({ ...foundSub, isDub: true })
|
subDownloadList.push({ ...foundSub, isDub: true })
|
||||||
@ -672,17 +665,18 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
const sbs: Array<string> = []
|
const sbs: Array<string> = []
|
||||||
for (const sub of subDownloadList) {
|
for (const sub of subDownloadList) {
|
||||||
const name = await downloadCRSub(sub, subFolder, quality)
|
const name = await downloadCRSub(sub, subFolder, quality)
|
||||||
|
if (!name) return
|
||||||
sbs.push(name)
|
sbs.push(name)
|
||||||
}
|
}
|
||||||
return sbs
|
return sbs
|
||||||
}
|
}
|
||||||
|
|
||||||
const audioDownload = async () => {
|
const audioDownload = async () => {
|
||||||
const audios: Array<string> = []
|
const audios: Array<string | undefined> = new Array(dubDownloadList.length).fill(undefined)
|
||||||
const concurrentDownloads = 2
|
const concurrentDownloads = 2
|
||||||
const tasks: Array<Promise<void>> = []
|
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)
|
const list = await crunchyGetPlaylist(v.guid, v.geo)
|
||||||
|
|
||||||
if (!list) return
|
if (!list) return
|
||||||
@ -691,8 +685,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
if (!playlist) return
|
if (!playlist) return
|
||||||
|
|
||||||
await deleteVideoToken(episodeID, list.data.token)
|
|
||||||
|
|
||||||
const assetId = playlist.mediaGroups.AUDIO.audio.main.playlists[0].segments[0].resolvedUri.match(/\/assets\/(?:p\/)?([^_,]+)/)
|
const assetId = playlist.mediaGroups.AUDIO.audio.main.playlists[0].segments[0].resolvedUri.match(/\/assets\/(?:p\/)?([^_,]+)/)
|
||||||
|
|
||||||
if (!assetId) {
|
if (!assetId) {
|
||||||
@ -764,12 +756,12 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
const path = await downloadMPDAudio(p, audioFolder, list.data.audioLocale, downloadID, keys ? keys : undefined)
|
const path = await downloadMPDAudio(p, audioFolder, list.data.audioLocale, downloadID, keys ? keys : undefined)
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
audios.push(path as string)
|
audios[index] = path as string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const v of dubDownloadList) {
|
for (const [index, v] of dubDownloadList.entries()) {
|
||||||
const task = downloadTask(v).finally(() => {
|
const task = downloadTask(v, index).finally(() => {
|
||||||
tasks.splice(tasks.indexOf(task), 1)
|
tasks.splice(tasks.indexOf(task), 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -782,7 +774,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
await Promise.all(tasks)
|
await Promise.all(tasks)
|
||||||
|
|
||||||
return audios
|
return audios.filter((path) => path !== undefined) as string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadVideo = async () => {
|
const downloadVideo = async () => {
|
||||||
@ -866,8 +858,6 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
if (!mdp) return
|
if (!mdp) return
|
||||||
|
|
||||||
await deleteVideoToken(episodeID, play.data.token)
|
|
||||||
|
|
||||||
var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.height === quality)
|
var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.height === quality)
|
||||||
|
|
||||||
if (!hq) {
|
if (!hq) {
|
||||||
@ -987,6 +977,8 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
if (!audios) return
|
if (!audios) return
|
||||||
|
|
||||||
|
if (!subss) return
|
||||||
|
|
||||||
await updatePlaylistByID(downloadID, 'merging video & audio')
|
await updatePlaylistByID(downloadID, 'merging video & audio')
|
||||||
|
|
||||||
var episodeNaming = (await settings.get('EpisodeTemp')) as string
|
var episodeNaming = (await settings.get('EpisodeTemp')) as string
|
||||||
@ -1308,8 +1300,25 @@ export async function checkProxies() {
|
|||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
p.status = 'online'
|
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 {
|
} else {
|
||||||
|
const data = await response.text()
|
||||||
|
|
||||||
p.status = 'offline'
|
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
|
audio: name
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const dn = downloading.find((i) => i.id === downloadID && i.audio === name)
|
||||||
|
|
||||||
for (const [index, part] of parts.entries()) {
|
for (const [index, part] of parts.entries()) {
|
||||||
let success = false
|
let success = false
|
||||||
while (!success) {
|
while (!success) {
|
||||||
try {
|
try {
|
||||||
var stream
|
var stream
|
||||||
|
|
||||||
|
const response = await fetch(part.url)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw Error(await response.text())
|
||||||
|
}
|
||||||
|
|
||||||
stream = fs.createWriteStream(`${path}/${part.filename}`)
|
stream = fs.createWriteStream(`${path}/${part.filename}`)
|
||||||
|
|
||||||
const { body } = await fetch(part.url)
|
const readableStream = Readable.from(response.body as any)
|
||||||
|
|
||||||
const readableStream = Readable.from(body as any)
|
|
||||||
|
|
||||||
await finished(readableStream.pipe(stream))
|
await finished(readableStream.pipe(stream))
|
||||||
|
|
||||||
@ -60,6 +66,9 @@ export async function downloadMPDAudio(
|
|||||||
|
|
||||||
success = true
|
success = true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (dn) {
|
||||||
|
dn.status = 'failed'
|
||||||
|
}
|
||||||
console.error(`Error occurred during download of fragment ${index + 1}:`, error)
|
console.error(`Error occurred during download of fragment ${index + 1}:`, error)
|
||||||
server.logger.log({
|
server.logger.log({
|
||||||
level: 'error',
|
level: 'error',
|
||||||
@ -138,6 +147,17 @@ async function mergePartsAudio(
|
|||||||
.input(concatenatedFile)
|
.input(concatenatedFile)
|
||||||
.outputOptions('-c copy')
|
.outputOptions('-c copy')
|
||||||
.save(`${dir}/${name}.aac`)
|
.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 () => {
|
.on('end', async () => {
|
||||||
console.log('Merging finished')
|
console.log('Merging finished')
|
||||||
await deleteFolder(tmp)
|
await deleteFolder(tmp)
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import { server } from '../api'
|
||||||
|
|
||||||
export async function concatenateTSFiles(inputFiles: Array<string>, outputFile: string) {
|
export async function concatenateTSFiles(inputFiles: Array<string>, outputFile: string) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const writeStream = fs.createWriteStream(outputFile)
|
const writeStream = fs.createWriteStream(outputFile)
|
||||||
|
|
||||||
writeStream.on('error', (error) => {
|
writeStream.on('error', (error) => {
|
||||||
|
server.logger.log({
|
||||||
|
level: 'error',
|
||||||
|
message: `Error while concatenating`,
|
||||||
|
error: error,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
section: 'crunchyrollDownloadProcessConcatenate'
|
||||||
|
})
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -22,6 +30,13 @@ export async function concatenateTSFiles(inputFiles: Array<string>, outputFile:
|
|||||||
const readStream = fs.createReadStream(inputFiles[index])
|
const readStream = fs.createReadStream(inputFiles[index])
|
||||||
|
|
||||||
readStream.on('error', (error) => {
|
readStream.on('error', (error) => {
|
||||||
|
server.logger.log({
|
||||||
|
level: 'error',
|
||||||
|
message: `Error while concatenating`,
|
||||||
|
error: error,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
section: 'crunchyrollDownloadProcessConcatenate'
|
||||||
|
})
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { parse, stringify } from 'ass-compiler'
|
|||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
import { finished } from 'stream/promises'
|
import { finished } from 'stream/promises'
|
||||||
import CryptoJS from 'crypto-js'
|
import CryptoJS from 'crypto-js'
|
||||||
|
import { server } from '../api'
|
||||||
|
|
||||||
export async function downloadCRSub(
|
export async function downloadCRSub(
|
||||||
sub: {
|
sub: {
|
||||||
@ -14,6 +15,7 @@ export async function downloadCRSub(
|
|||||||
dir: string,
|
dir: string,
|
||||||
qual: 1080 | 720 | 480 | 360 | 240
|
qual: 1080 | 720 | 480 | 360 | 240
|
||||||
) {
|
) {
|
||||||
|
try {
|
||||||
const path = `${dir}/${sub.language}${sub.isDub ? `-FORCED` : ''}.${sub.format}`
|
const path = `${dir}/${sub.language}${sub.isDub ? `-FORCED` : ''}.${sub.format}`
|
||||||
var qualX
|
var qualX
|
||||||
var qualY
|
var qualY
|
||||||
@ -69,6 +71,16 @@ export async function downloadCRSub(
|
|||||||
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
||||||
|
|
||||||
return path
|
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'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resamplePOSSubtitle(subtitle: string, ox: number, oy: number, nx: number, ny: number) {
|
function resamplePOSSubtitle(subtitle: string, ox: number, oy: number, nx: number, ny: number) {
|
||||||
|
Reference in New Issue
Block a user