mirror of
https://github.com/OpenSTDL/CrunchyDL.git
synced 2024-10-18 12:25:08 +02:00
mp4decrypt -> shaka, jsencrypt -> node-forge, package update
This commit is contained in:
parent
f5403038e6
commit
1902f58f37
8
.gitignore
vendored
8
.gitignore
vendored
@ -20,11 +20,11 @@ build/
|
|||||||
crunchyroll-downloader-output-*/
|
crunchyroll-downloader-output-*/
|
||||||
|
|
||||||
# FFMPEG
|
# FFMPEG
|
||||||
ffmpeg/ffmpeg.exe
|
ffmpeg/*
|
||||||
ffmpeg/ffprobe.exe
|
ffmpeg/!.gitkeep
|
||||||
|
|
||||||
# MP4DECRYPT
|
# SHAKA
|
||||||
mp4decrypt/mp4decrypt.exe
|
shaka/shaka.exe
|
||||||
|
|
||||||
# Keys
|
# Keys
|
||||||
keys/client
|
keys/client
|
||||||
|
3
app.config.ts
Normal file
3
app.config.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default defineAppConfig({
|
||||||
|
nuxtIcon: {},
|
||||||
|
});
|
@ -244,7 +244,7 @@ export interface CrunchyEpisodeFetch {
|
|||||||
data: Array<{
|
data: Array<{
|
||||||
id: string
|
id: string
|
||||||
episode_metadata: {
|
episode_metadata: {
|
||||||
series_id: string,
|
series_id: string
|
||||||
season_id: string
|
season_id: string
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import packageJson from '../package.json';
|
import packageJson from '../package.json'
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV !== 'development'
|
const isProduction = process.env.NODE_ENV !== 'development'
|
||||||
|
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative flex flex-col items-center justify-center h-full" style="-webkit-app-region: no-drag">
|
<div class="relative flex flex-col items-center justify-center h-full" style="-webkit-app-region: no-drag">
|
||||||
<img src="/logo.png" class="h-24" />
|
<img src="/logo.png" class="h-24" />
|
||||||
<div class="text-base text-center leading-[18px]">
|
<div class="text-base text-center leading-[18px]"> CrunchyDL </div>
|
||||||
CrunchyDL
|
|
||||||
</div>
|
|
||||||
<div class="text-sm mt-1 text-gray-200"> v{{ packageJson.version }} </div>
|
<div class="text-sm mt-1 text-gray-200"> v{{ packageJson.version }} </div>
|
||||||
<div class="absolute right-0 bottom-0 text-xs text-gray-200"> Made by OpenSTDL </div>
|
<div class="absolute right-0 bottom-0 text-xs text-gray-200"> Made by OpenSTDL </div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import packageJson from '../../package.json';
|
import packageJson from '../../package.json'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
typescript: {
|
ssr: false,
|
||||||
shim: false
|
modules: ['@nuxtjs/tailwindcss', 'nuxt-icon', '@nuxtjs/google-fonts'],
|
||||||
},
|
googleFonts: {
|
||||||
ssr: false,
|
families: {
|
||||||
modules: ['@nuxtjs/tailwindcss', 'nuxt-icon', '@nuxtjs/google-fonts'],
|
'DM+Sans': ['600', '1000'],
|
||||||
googleFonts: {
|
'Protest+Riot': true
|
||||||
families: {
|
}
|
||||||
'DM+Sans': ['600', '1000'],
|
|
||||||
'Protest+Riot': true
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
23
package.json
23
package.json
@ -8,13 +8,13 @@
|
|||||||
"repository": "https://github.com/OpenSTDL/CrunchyDL",
|
"repository": "https://github.com/OpenSTDL/CrunchyDL",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt dev -o",
|
"dev": "nuxt dev -o",
|
||||||
"build": "nuxt generate",
|
"build": "nuxt build",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare && electron-builder install-app-deps",
|
"postinstall": "nuxt prepare && electron-builder install-app-deps",
|
||||||
"transpile-src": "tsc -p ./src --outDir .output/src",
|
"transpile-src": "tsc -p ./src --outDir .output/src",
|
||||||
"dev:electron": "NODE_ENV=development concurrently --kill-others \"nuxt dev\" \"tsc-watch -p ./src --outDir .output/src --onSuccess 'electron ./.output/src/electron/background.js'\"",
|
"dev:electron": "NODE_ENV=development concurrently --kill-others \"nuxt dev\" \"tsc-watch -p ./src --outDir .output/src --onSuccess 'electron ./.output/src/electron/background.js'\"",
|
||||||
"dev:electron:win": "set NODE_ENV=development& concurrently --kill-others \"nuxt dev\" \"tsc-watch -p ./src --outDir .output/src --onSuccess run.electron\"",
|
"dev:electron:win": "set NODE_ENV=development& concurrently --kill-others \"nuxt dev\" \"tsc-watch -p ./src --outDir .output/src --onSuccess run.electron\"",
|
||||||
"build:electron": "pnpm prettier:fix && pnpm build && pnpm transpile-src && node build.js",
|
"build:electron": "pnpm prettier:fix && nuxt generate && pnpm transpile-src && node build.js",
|
||||||
"prettier:fix": "pnpm prettier src --write && pnpm prettier components --write && pnpm prettier pages --write && pnpm prettier build.js --write"
|
"prettier:fix": "pnpm prettier src --write && pnpm prettier components --write && pnpm prettier pages --write && pnpm prettier build.js --write"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -27,6 +27,7 @@
|
|||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"@types/node-forge": "^1.3.11",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"electron": "^30.1.2",
|
"electron": "^30.1.2",
|
||||||
@ -34,18 +35,15 @@
|
|||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.10.0",
|
"eslint-config-prettier": "^8.10.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"long": "^5.2.3",
|
|
||||||
"modclean": "3.0.0-beta.1",
|
"modclean": "3.0.0-beta.1",
|
||||||
"nuxt": "3.11.2",
|
"nuxt": "3.11.2",
|
||||||
"nuxt-icon": "^0.6.10",
|
"nuxt-icon": "^0.6.10",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"protobufjs": "^7.3.2",
|
|
||||||
"sass": "^1.77.6",
|
"sass": "^1.77.6",
|
||||||
"sass-loader": "^13.3.3",
|
"sass-loader": "^13.3.3",
|
||||||
"tsc-watch": "^6.2.0",
|
"tsc-watch": "^6.2.0",
|
||||||
"typescript": "^5.5.2",
|
"typescript": "5.4.4",
|
||||||
"wait-on": "^7.2.0",
|
"wait-on": "^7.2.0"
|
||||||
"winston": "^3.13.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/cors": "^9.0.1",
|
"@fastify/cors": "^9.0.1",
|
||||||
@ -58,18 +56,21 @@
|
|||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"fastify": "^4.28.0",
|
"fastify": "^4.28.0",
|
||||||
"fluent-ffmpeg": "^2.1.3",
|
"fluent-ffmpeg": "^2.1.3",
|
||||||
"jsencrypt": "^3.3.2",
|
"long": "^5.2.3",
|
||||||
"mpd-parser": "^1.3.0",
|
"mpd-parser": "^1.3.0",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
|
"node-forge": "^1.3.1",
|
||||||
|
"protobufjs": "^7.3.2",
|
||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sqlite3": "5.1.6"
|
"sqlite3": "5.1.6",
|
||||||
|
"winston": "^3.13.0"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"files": [
|
"files": [
|
||||||
"!**/pages/*",
|
"!**/pages/*",
|
||||||
"!**/ffmpeg/*",
|
"!**/ffmpeg/*",
|
||||||
"!**/mp4decrypt/*",
|
"!**/shaka/*",
|
||||||
"!**/.git/*",
|
"!**/.git/*",
|
||||||
"!**/.github/*",
|
"!**/.github/*",
|
||||||
"!**/.nuxt/*",
|
"!**/.nuxt/*",
|
||||||
@ -77,7 +78,7 @@
|
|||||||
],
|
],
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
"./ffmpeg/**",
|
"./ffmpeg/**",
|
||||||
"./mp4decrypt/**"
|
"./shaka/**"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="service === 'crunchyroll'" class="relative flex flex-col select-none">
|
<div v-if="service === 'crunchyroll'" class="relative flex flex-col select-none">
|
||||||
<div @click="selectHardSub ? (selectHardSub = false) : (selectHardSub = true)" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer">
|
<div
|
||||||
|
@click="selectHardSub ? (selectHardSub = false) : (selectHardSub = true)"
|
||||||
|
class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer"
|
||||||
|
>
|
||||||
Hardsub:
|
Hardsub:
|
||||||
{{ selectedHardSub ? `${selectedHardSub.name} (${selectedHardSub.format})` : 'No Hardsub selected' }}
|
{{ selectedHardSub ? `${selectedHardSub.name} (${selectedHardSub.format})` : 'No Hardsub selected' }}
|
||||||
</div>
|
</div>
|
||||||
@ -247,7 +250,7 @@
|
|||||||
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
||||||
:class="selectedHardSub && selectedHardSub.locale === l.locale && selectedHardSub.format === 'sub' ? 'bg-[#585858]' : 'hover:bg-[#747474]'"
|
:class="selectedHardSub && selectedHardSub.locale === l.locale && selectedHardSub.format === 'sub' ? 'bg-[#585858]' : 'hover:bg-[#747474]'"
|
||||||
>
|
>
|
||||||
{{ l.name }}<br>(sub)
|
{{ l.name }}<br />(sub)
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-for="l in CRselectedShow?.Dubs.map((s) => {
|
v-for="l in CRselectedShow?.Dubs.map((s) => {
|
||||||
@ -257,7 +260,7 @@
|
|||||||
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
||||||
:class="selectedHardSub && selectedHardSub.locale === l.locale && selectedHardSub.format === 'dub' ? 'bg-[#585858]' : 'hover:bg-[#747474]'"
|
:class="selectedHardSub && selectedHardSub.locale === l.locale && selectedHardSub.format === 'dub' ? 'bg-[#585858]' : 'hover:bg-[#747474]'"
|
||||||
>
|
>
|
||||||
{{ l.name }}<br>(dub)
|
{{ l.name }}<br />(dub)
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -429,7 +432,7 @@ const selectSub = ref<boolean>(false)
|
|||||||
const selectedSubs = ref<Array<{ name: string | undefined; locale: string }>>([])
|
const selectedSubs = ref<Array<{ name: string | undefined; locale: string }>>([])
|
||||||
|
|
||||||
const selectHardSub = ref<boolean>(false)
|
const selectHardSub = ref<boolean>(false)
|
||||||
const selectedHardSub = ref<{ name: string | undefined; locale: string, format: string }>()
|
const selectedHardSub = ref<{ name: string | undefined; locale: string; format: string }>()
|
||||||
|
|
||||||
const tab = ref<number>(1)
|
const tab = ref<number>(1)
|
||||||
const search = ref<string>('')
|
const search = ref<string>('')
|
||||||
@ -779,7 +782,7 @@ const switchToSeason = async () => {
|
|||||||
|
|
||||||
if (url.value && url.value.includes('crunchyroll') && url.value.includes('/watch/') && !CRselectedShow.value) {
|
if (url.value && url.value.includes('crunchyroll') && url.value.includes('/watch/') && !CRselectedShow.value) {
|
||||||
var episodeID: string | string[] = url.value.split('/')
|
var episodeID: string | string[] = url.value.split('/')
|
||||||
episodeID = episodeID[episodeID.length-2]
|
episodeID = episodeID[episodeID.length - 2]
|
||||||
const seriesID = await getCREpisodeSeriesID(episodeID)
|
const seriesID = await getCREpisodeSeriesID(episodeID)
|
||||||
if (!seriesID) {
|
if (!seriesID) {
|
||||||
alert('Episode not found')
|
alert('Episode not found')
|
||||||
@ -798,7 +801,7 @@ const switchToSeason = async () => {
|
|||||||
isFetchingSeasons.value--
|
isFetchingSeasons.value--
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectedSeason.value = seasons.value.find(s => s.id === seriesID.season) ?? seasons.value[0]
|
selectedSeason.value = seasons.value.find((s) => s.id === seriesID.season) ?? seasons.value[0]
|
||||||
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id, CRselectedShow.value.Geo)
|
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id, CRselectedShow.value.Geo)
|
||||||
if (episodes.value) {
|
if (episodes.value) {
|
||||||
selectedStartEpisode.value = episodes.value[0]
|
selectedStartEpisode.value = episodes.value[0]
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="relative flex flex-row gap-2 h-full items-end">
|
<div class="relative flex flex-row gap-2 h-full items-end">
|
||||||
<div class="text-xs">{{ p.quality }}p</div>
|
<div class="text-xs">{{ p.quality }}p</div>
|
||||||
<div v-if="p.qualityaudio" class="text-xs">{{ audioQualities[p.qualityaudio-1] ?? '44.10 kHz' }}</div>
|
<div v-if="p.qualityaudio" class="text-xs">{{ audioQualities[p.qualityaudio - 1] ?? '44.10 kHz' }}</div>
|
||||||
<div class="text-xs uppercase">{{ p.format }}</div>
|
<div class="text-xs uppercase">{{ p.format }}</div>
|
||||||
<div class="text-xs">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div>
|
<div class="text-xs">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div>
|
||||||
<div class="text-xs">Subs: {{ p.sub.length !== 0 ? p.sub.map((t) => t.name).join(', ') : '-' }}</div>
|
<div class="text-xs">Subs: {{ p.sub.length !== 0 ? p.sub.map((t) => t.name).join(', ') : '-' }}</div>
|
||||||
@ -166,7 +166,7 @@ const playlist = ref<
|
|||||||
media: CrunchyEpisode | ADNEpisode
|
media: CrunchyEpisode | ADNEpisode
|
||||||
dub: Array<{ locale: string; name: string }>
|
dub: Array<{ locale: string; name: string }>
|
||||||
sub: Array<{ locale: string; name: string }>
|
sub: Array<{ locale: string; name: string }>
|
||||||
hardsub: { name: string | undefined; locale: string, format: string }
|
hardsub: { name: string | undefined; locale: string; format: string }
|
||||||
dir: string
|
dir: string
|
||||||
installDir: string
|
installDir: string
|
||||||
partsleft: number
|
partsleft: number
|
||||||
@ -194,7 +194,7 @@ const getPlaylist = async () => {
|
|||||||
media: CrunchyEpisode | ADNEpisode
|
media: CrunchyEpisode | ADNEpisode
|
||||||
dub: Array<{ locale: string; name: string }>
|
dub: Array<{ locale: string; name: string }>
|
||||||
sub: Array<{ locale: string; name: string }>
|
sub: Array<{ locale: string; name: string }>
|
||||||
hardsub: { name: string | undefined; locale: string, format: string }
|
hardsub: { name: string | undefined; locale: string; format: string }
|
||||||
dir: string
|
dir: string
|
||||||
installDir: string
|
installDir: string
|
||||||
partsleft: number
|
partsleft: number
|
||||||
|
918
pnpm-lock.yaml
918
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -46,7 +46,7 @@ interface PlaylistAttributes {
|
|||||||
media: CrunchyEpisode | ADNEpisode
|
media: CrunchyEpisode | ADNEpisode
|
||||||
dub: { name: string | undefined; locale: string }[]
|
dub: { name: string | undefined; locale: string }[]
|
||||||
sub: { name: string | undefined; locale: string }[]
|
sub: { name: string | undefined; locale: string }[]
|
||||||
hardsub: { name: string | undefined; locale: string, format: string }
|
hardsub: { name: string | undefined; locale: string; format: string }
|
||||||
quality: 1080 | 720 | 480 | 360 | 240
|
quality: 1080 | 720 | 480 | 360 | 240
|
||||||
qualityaudio: 1 | 2 | 3 | undefined
|
qualityaudio: 1 | 2 | 3 | undefined
|
||||||
dir: string
|
dir: string
|
||||||
@ -60,7 +60,7 @@ interface PlaylistCreateAttributes {
|
|||||||
media: CrunchyEpisode | ADNEpisode
|
media: CrunchyEpisode | ADNEpisode
|
||||||
dub: { name: string | undefined; locale: string }[]
|
dub: { name: string | undefined; locale: string }[]
|
||||||
sub: { name: string | undefined; locale: string }[]
|
sub: { name: string | undefined; locale: string }[]
|
||||||
hardsub: { name: string | undefined; locale: string, format: string } | undefined
|
hardsub: { name: string | undefined; locale: string; format: string } | undefined
|
||||||
dir: string
|
dir: string
|
||||||
quality: 1080 | 720 | 480 | 360 | 240
|
quality: 1080 | 720 | 480 | 360 | 240
|
||||||
qualityaudio: 1 | 2 | 3 | undefined
|
qualityaudio: 1 | 2 | 3 | undefined
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import JSEncrypt from 'jsencrypt'
|
|
||||||
import CryptoJS from 'crypto-js'
|
import CryptoJS from 'crypto-js'
|
||||||
import { server } from '../../api'
|
import { server } from '../../api'
|
||||||
import { ADNLink, ADNPlayerConfig } from '../../types/adn'
|
import { ADNLink, ADNPlayerConfig } from '../../types/adn'
|
||||||
import { messageBox } from '../../../electron/background'
|
import { messageBox } from '../../../electron/background'
|
||||||
import { useFetch } from '../useFetch'
|
import { useFetch } from '../useFetch'
|
||||||
import { loggedInCheck } from '../service/service.service'
|
import { loggedInCheck } from '../service/service.service'
|
||||||
import { parse as mpdParse, parse } from 'mpd-parser'
|
import * as forge from 'node-forge'
|
||||||
|
|
||||||
export async function adnLogin(user: string, passw: string) {
|
export async function adnLogin(user: string, passw: string) {
|
||||||
const cachedData:
|
const cachedData:
|
||||||
@ -178,12 +177,9 @@ async function getPlayerEncryptedToken(id: number, geo: 'de' | 'fr') {
|
|||||||
|
|
||||||
if (!token) return
|
if (!token) return
|
||||||
|
|
||||||
var key = new JSEncrypt()
|
const publicKeyPem = `-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbQrCJBRmaXM4gJidDmcpWDssgnumHinCLHAgS4buMtdH7dEGGEUfBofLzoEdt1jqcrCDT6YNhM0aFCqbLOPFtx9cg/X2G/G5bPVu8cuFM0L+ehp8s6izK1kjx3OOPH/kWzvstM5tkqgJkNyNEvHdeJl6KhS+IFEqwvZqgbBpKuwIDAQAB-----END PUBLIC KEY-----`
|
||||||
var random = randomHexaString(16)
|
|
||||||
|
|
||||||
key.setPublicKey(
|
var random = CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex)
|
||||||
'-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbQrCJBRmaXM4gJidDmcpWDssgnumHinCLHAgS4buMtdH7dEGGEUfBofLzoEdt1jqcrCDT6YNhM0aFCqbLOPFtx9cg/X2G/G5bPVu8cuFM0L+ehp8s6izK1kjx3OOPH/kWzvstM5tkqgJkNyNEvHdeJl6KhS+IFEqwvZqgbBpKuwIDAQAB-----END PUBLIC KEY-----'
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
k: random,
|
k: random,
|
||||||
@ -192,11 +188,19 @@ async function getPlayerEncryptedToken(id: number, geo: 'de' | 'fr') {
|
|||||||
|
|
||||||
const finisheddata = JSON.stringify(data)
|
const finisheddata = JSON.stringify(data)
|
||||||
|
|
||||||
const encryptedData = key.encrypt(finisheddata) || ''
|
const encryptedData = encryptWithPublicKey(publicKeyPem, finisheddata)
|
||||||
|
|
||||||
return { data: encryptedData, random: random }
|
return { data: encryptedData, random: random }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encryptWithPublicKey(publicKeyPem: string, data: string) {
|
||||||
|
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem)
|
||||||
|
const encryptedData = publicKey.encrypt(data, 'RSA-OAEP', {
|
||||||
|
md: forge.md.sha256.create()
|
||||||
|
})
|
||||||
|
return forge.util.encode64(encryptedData)
|
||||||
|
}
|
||||||
|
|
||||||
export async function adnGetPlaylist(animeid: number, geo: 'de' | 'fr') {
|
export async function adnGetPlaylist(animeid: number, geo: 'de' | 'fr') {
|
||||||
const token = await getPlayerEncryptedToken(animeid, geo)
|
const token = await getPlayerEncryptedToken(animeid, geo)
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ export async function addPlaylistController(
|
|||||||
episodes: CrunchyEpisodes
|
episodes: CrunchyEpisodes
|
||||||
dubs: { name: string | undefined; locale: string }[]
|
dubs: { name: string | undefined; locale: string }[]
|
||||||
subs: { name: string | undefined; locale: string }[]
|
subs: { name: string | undefined; locale: string }[]
|
||||||
hardsub: { name: string | undefined; locale: string, format: string } | undefined
|
hardsub: { name: string | undefined; locale: string; format: string } | undefined
|
||||||
dir: string
|
dir: string
|
||||||
quality: 1080 | 720 | 480 | 360 | 240
|
quality: 1080 | 720 | 480 | 360 | 240
|
||||||
qualityaudio: 1 | 2 | 3 | undefined
|
qualityaudio: 1 | 2 | 3 | undefined
|
||||||
|
@ -15,9 +15,9 @@ import { ADNEpisode } from '../../types/adn'
|
|||||||
import { messageBox } from '../../../electron/background'
|
import { messageBox } from '../../../electron/background'
|
||||||
import { getFFMPEGPath } from '../../services/ffmpeg'
|
import { getFFMPEGPath } from '../../services/ffmpeg'
|
||||||
import { getDRMKeys, Uint8ArrayToBase64 } from '../../services/decryption'
|
import { getDRMKeys, Uint8ArrayToBase64 } from '../../services/decryption'
|
||||||
import { getMP4DecryptPath } from '../../services/mp4decrypt'
|
import { getShakaPath } from '../../services/shaka'
|
||||||
const ffmpegP = getFFMPEGPath()
|
const ffmpegP = getFFMPEGPath()
|
||||||
const mp4e = getMP4DecryptPath()
|
const shaka = getShakaPath()
|
||||||
import util from 'util'
|
import util from 'util'
|
||||||
import settings from 'electron-settings'
|
import settings from 'electron-settings'
|
||||||
import { server } from '../../api'
|
import { server } from '../../api'
|
||||||
@ -211,7 +211,7 @@ export async function addEpisodeToPlaylist(
|
|||||||
e: CrunchyEpisode,
|
e: CrunchyEpisode,
|
||||||
s: { name: string | undefined; locale: string }[],
|
s: { name: string | undefined; locale: string }[],
|
||||||
d: { name: string | undefined; locale: string }[],
|
d: { name: string | undefined; locale: string }[],
|
||||||
hardsub: { name: string | undefined; locale: string, format: string } | undefined,
|
hardsub: { name: string | undefined; locale: string; format: string } | undefined,
|
||||||
dir: string,
|
dir: string,
|
||||||
status:
|
status:
|
||||||
| 'waiting'
|
| 'waiting'
|
||||||
@ -471,7 +471,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
e: string,
|
e: string,
|
||||||
dubs: Array<string>,
|
dubs: Array<string>,
|
||||||
subs: Array<string>,
|
subs: Array<string>,
|
||||||
hardsub: { name: string | undefined; locale: string; format: string; },
|
hardsub: { name: string | undefined; locale: string; format: string },
|
||||||
episodeID: string,
|
episodeID: string,
|
||||||
downloadID: number,
|
downloadID: number,
|
||||||
name: string,
|
name: string,
|
||||||
@ -792,7 +792,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
let p: { filename: string; url: string }[] = []
|
let p: { filename: string; url: string }[] = []
|
||||||
|
|
||||||
if (playlist.mediaGroups.AUDIO.audio.main.playlists[playlistindex].contentProtection) {
|
if (playlist.mediaGroups.AUDIO.audio.main.playlists[playlistindex].contentProtection) {
|
||||||
if (!playlist.mediaGroups.AUDIO.audio.main.playlists[playlistindex].contentProtection['com.widevine.alpha'].pssh) {
|
if (!playlist.mediaGroups.AUDIO.audio.main.playlists![playlistindex]!.contentProtection!['com.widevine.alpha']!.pssh) {
|
||||||
console.log('No PSSH found, exiting.')
|
console.log('No PSSH found, exiting.')
|
||||||
messageBox(
|
messageBox(
|
||||||
'error',
|
'error',
|
||||||
@ -811,7 +811,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pssh = Uint8ArrayToBase64(playlist.mediaGroups.AUDIO.audio.main.playlists[playlistindex].contentProtection['com.widevine.alpha'].pssh)
|
pssh = Uint8ArrayToBase64(playlist.mediaGroups.AUDIO.audio.main.playlists![playlistindex]!.contentProtection!['com.widevine.alpha'].pssh!)
|
||||||
|
|
||||||
keys = await getDRMKeys(pssh, assetId[1], list.account_id)
|
keys = await getDRMKeys(pssh, assetId[1], list.account_id)
|
||||||
|
|
||||||
@ -946,9 +946,9 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
var downloadGEO
|
var downloadGEO
|
||||||
|
|
||||||
if (hardsub && hardsub.locale) {
|
if (hardsub && hardsub.locale) {
|
||||||
var hardsubURL: string | undefined;
|
var hardsubURL: string | undefined
|
||||||
|
|
||||||
var hardsubGEO: string | undefined;;
|
var hardsubGEO: string | undefined
|
||||||
|
|
||||||
if (hardsub.format === 'dub') {
|
if (hardsub.format === 'dub') {
|
||||||
const found = play.data.versions.find((h) => h.audio_locale === hardsub.locale)
|
const found = play.data.versions.find((h) => h.audio_locale === hardsub.locale)
|
||||||
@ -989,7 +989,14 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
downloadURL = play.data.url
|
downloadURL = play.data.url
|
||||||
downloadGEO = play.data.geo
|
downloadGEO = play.data.geo
|
||||||
console.log('Hardsub Playlist not found')
|
console.log('Hardsub Playlist not found')
|
||||||
messageBox('warning', ['Cancel'], 2, 'Hardsub Playlist not found', 'Hardsub Playlist not found', `${hardsub.locale} Hardsub Playlist not found, downloading japanese playlist instead.`)
|
messageBox(
|
||||||
|
'warning',
|
||||||
|
['Cancel'],
|
||||||
|
2,
|
||||||
|
'Hardsub Playlist not found',
|
||||||
|
'Hardsub Playlist not found',
|
||||||
|
`${hardsub.locale} Hardsub Playlist not found, downloading japanese playlist instead.`
|
||||||
|
)
|
||||||
server.logger.log({
|
server.logger.log({
|
||||||
level: 'error',
|
level: 'error',
|
||||||
message: `${hardsub.locale} Hardsub Playlist not found, downloading japanese playlist instead.`,
|
message: `${hardsub.locale} Hardsub Playlist not found, downloading japanese playlist instead.`,
|
||||||
@ -1296,11 +1303,8 @@ async function mergeParts(parts: { filename: string; url: string }[], downloadID
|
|||||||
|
|
||||||
await updatePlaylistByID(downloadID, 'decrypting video')
|
await updatePlaylistByID(downloadID, 'decrypting video')
|
||||||
console.log('Video Decryption started')
|
console.log('Video Decryption started')
|
||||||
const inputFilePath = `${tmp}/temp-main.m4s`
|
|
||||||
const outputFilePath = `${tmp}/main.m4s`
|
|
||||||
const keyArgument = `--show-progress --key ${drmkeys[1].kid}:${drmkeys[1].key}`
|
|
||||||
|
|
||||||
const command = `${mp4e} ${keyArgument} "${inputFilePath}" "${outputFilePath}"`
|
const command = `${shaka} input="${tmp}/temp-main.m4s",stream=video,output="${tmp}/main.m4s" --enable_raw_key_decryption --keys key_id=${drmkeys[1].kid}:key=${drmkeys[1].key}`
|
||||||
|
|
||||||
await exec(command)
|
await exec(command)
|
||||||
console.log('Video Decryption finished')
|
console.log('Video Decryption finished')
|
||||||
@ -1397,15 +1401,13 @@ async function mergeVideoFile(
|
|||||||
if (format === 'mp4') {
|
if (format === 'mp4') {
|
||||||
options.push('-c:s mov_text')
|
options.push('-c:s mov_text')
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [index, a] of audios.entries()) {
|
for (const [index, a] of audios.entries()) {
|
||||||
output.addInput(a)
|
output.addInput(a)
|
||||||
options.push(`-map ${ffindex}:a:0`)
|
options.push(`-map ${ffindex}:a:0`)
|
||||||
options.push(
|
options.push(
|
||||||
`-metadata:s:a:${index} language=${
|
`-metadata:s:a:${index} language=${
|
||||||
locales.find((l) => l.locale === getFilename(a, '.aac', '/'))
|
locales.find((l) => l.locale === getFilename(a, '.aac', '/')) ? locales.find((l) => l.locale === getFilename(a, '.aac', '/'))?.iso : getFilename(a, '.aac', '/')
|
||||||
? locales.find((l) => l.locale === getFilename(a, '.aac', '/'))?.iso
|
|
||||||
: getFilename(a, '.aac', '/')
|
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
options.push(
|
options.push(
|
||||||
@ -1415,7 +1417,7 @@ async function mergeVideoFile(
|
|||||||
: getFilename(a, '.aac', '/')
|
: getFilename(a, '.aac', '/')
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
ffindex++
|
ffindex++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ import { checkFileExistence, createFolder, deleteFolder } from './folder'
|
|||||||
import { concatenateTSFiles } from './concatenate'
|
import { concatenateTSFiles } from './concatenate'
|
||||||
import Ffmpeg from 'fluent-ffmpeg'
|
import Ffmpeg from 'fluent-ffmpeg'
|
||||||
import { getFFMPEGPath } from './ffmpeg'
|
import { getFFMPEGPath } from './ffmpeg'
|
||||||
import { getMP4DecryptPath } from '../services/mp4decrypt'
|
import { getShakaPath } from './shaka'
|
||||||
const ffmpegP = getFFMPEGPath()
|
const ffmpegP = getFFMPEGPath()
|
||||||
const mp4e = getMP4DecryptPath()
|
const shaka = getShakaPath()
|
||||||
import util from 'util'
|
import util from 'util'
|
||||||
import { server } from '../api'
|
import { server } from '../api'
|
||||||
const exec = util.promisify(require('child_process').exec)
|
const exec = util.promisify(require('child_process').exec)
|
||||||
@ -161,11 +161,8 @@ async function mergePartsAudio(
|
|||||||
dn.status = 'decrypting'
|
dn.status = 'decrypting'
|
||||||
}
|
}
|
||||||
console.log(`Audio Decryption started`)
|
console.log(`Audio Decryption started`)
|
||||||
const inputFilePath = `${tmp}/temp-main.m4s`
|
|
||||||
const outputFilePath = `${tmp}/main.m4s`
|
|
||||||
const keyArgument = `--show-progress --key ${drmkeys[1].kid}:${drmkeys[1].key}`
|
|
||||||
|
|
||||||
const command = `${mp4e} ${keyArgument} "${inputFilePath}" "${outputFilePath}"`
|
const command = `${shaka} input="${tmp}/temp-main.m4s",stream=audio,output="${tmp}/main.m4s" --enable_raw_key_decryption --keys key_id=${drmkeys[1].kid}:key=${drmkeys[1].key}`
|
||||||
|
|
||||||
await exec(command)
|
await exec(command)
|
||||||
concatenatedFile = `${tmp}/main.m4s`
|
concatenatedFile = `${tmp}/main.m4s`
|
||||||
|
@ -105,8 +105,8 @@ export function getFilename(path: string, ext: string, delimiter: string) {
|
|||||||
const segments = path.split(delimiter)
|
const segments = path.split(delimiter)
|
||||||
|
|
||||||
if (segments.length == 0) {
|
if (segments.length == 0) {
|
||||||
return "unkown"
|
return 'unkown'
|
||||||
}
|
}
|
||||||
|
|
||||||
return segments[segments.length - 1].split(ext)[0]
|
return segments[segments.length - 1].split(ext)[0]
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import { app } from 'electron'
|
|
||||||
import path from 'path'
|
|
||||||
const isDev = process.env.NODE_ENV === 'development'
|
|
||||||
const appPath = app.getAppPath()
|
|
||||||
const resourcesPath = path.dirname(appPath)
|
|
||||||
const decryptPath = path.join(resourcesPath, 'mp4decrypt')
|
|
||||||
if (isDev) {
|
|
||||||
require('dotenv').config()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMP4DecryptPath() {
|
|
||||||
if (isDev) {
|
|
||||||
const mp4Decrypt = process.env.MP4DECRYPT_PATH
|
|
||||||
|
|
||||||
return mp4Decrypt
|
|
||||||
} else {
|
|
||||||
const mp4Decrypt = `"${path.join(decryptPath, 'mp4decrypt.exe')}"`
|
|
||||||
|
|
||||||
return mp4Decrypt
|
|
||||||
}
|
|
||||||
}
|
|
21
src/api/services/shaka.ts
Normal file
21
src/api/services/shaka.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { app } from 'electron'
|
||||||
|
import path from 'path'
|
||||||
|
const isDev = process.env.NODE_ENV === 'development'
|
||||||
|
const appPath = app.getAppPath()
|
||||||
|
const resourcesPath = path.dirname(appPath)
|
||||||
|
const shakaFolderPath = path.join(resourcesPath, 'shaka')
|
||||||
|
if (isDev) {
|
||||||
|
require('dotenv').config()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShakaPath() {
|
||||||
|
if (isDev) {
|
||||||
|
const shaka = process.env.SHAKA_PATH
|
||||||
|
|
||||||
|
return shaka
|
||||||
|
} else {
|
||||||
|
const shaka = `"${path.join(shakaFolderPath, 'shaka.exe')}"`
|
||||||
|
|
||||||
|
return shaka
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,6 @@ export async function downloadCRSub(
|
|||||||
qual: 1080 | 720 | 480 | 360 | 240
|
qual: 1080 | 720 | 480 | 360 | 240
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
var resamplerActive = await settings.get('subtitleResamplerActive')
|
var resamplerActive = await settings.get('subtitleResamplerActive')
|
||||||
|
|
||||||
if (resamplerActive === undefined || resamplerActive === null) {
|
if (resamplerActive === undefined || resamplerActive === null) {
|
||||||
@ -59,8 +58,8 @@ export async function downloadCRSub(
|
|||||||
|
|
||||||
await finished(readableStream.pipe(stream))
|
await finished(readableStream.pipe(stream))
|
||||||
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
console.log(`Sub ${sub.language}.${sub.format} downloaded`)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsedASS = parse(await response.text())
|
var parsedASS = parse(await response.text())
|
||||||
|
@ -1,23 +1,15 @@
|
|||||||
// This is the dynamic renderer script for Electron.
|
|
||||||
// You can implement your custom renderer process configuration etc. here!
|
|
||||||
// --------------------------------------------
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import { BrowserWindow } from 'electron'
|
import { BrowserWindow } from 'electron'
|
||||||
import express, { static as serveStatic } from 'express'
|
import express, { static as serveStatic } from 'express'
|
||||||
|
|
||||||
// Internals
|
|
||||||
// =========
|
|
||||||
const isProduction = process.env.NODE_ENV !== 'development'
|
const isProduction = process.env.NODE_ENV !== 'development'
|
||||||
|
|
||||||
// Dynamic Renderer
|
|
||||||
// ================
|
|
||||||
export default async function (mainWindow: BrowserWindow) {
|
export default async function (mainWindow: BrowserWindow) {
|
||||||
if (!isProduction) return mainWindow.loadURL('http://localhost:3000/')
|
if (!isProduction) return mainWindow.loadURL('http://localhost:3000/')
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use('/', serveStatic(path.join(__dirname, '../../public')))
|
app.use('/', serveStatic(path.join(__dirname, '../../public')))
|
||||||
const listener = app.listen(8079, 'localhost', () => {
|
const listener = app.listen(8079, 'localhost', () => {
|
||||||
const port = (listener.address() as any).port
|
const port = (listener.address() as any).port
|
||||||
console.log('Dynamic-Renderer Listening on', port)
|
|
||||||
mainWindow.loadURL(`http://localhost:${port}`)
|
mainWindow.loadURL(`http://localhost:${port}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user