mp4decrypt -> shaka, jsencrypt -> node-forge, package update

This commit is contained in:
stratuma 2024-06-28 05:07:41 +02:00
parent f5403038e6
commit 1902f58f37
21 changed files with 397 additions and 737 deletions

8
.gitignore vendored
View File

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

@ -0,0 +1,3 @@
export default defineAppConfig({
nuxtIcon: {},
});

View File

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

View File

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

View File

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

View File

@ -1,9 +1,4 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
typescript: {
shim: false
},
ssr: false, ssr: false,
modules: ['@nuxtjs/tailwindcss', 'nuxt-icon', '@nuxtjs/google-fonts'], modules: ['@nuxtjs/tailwindcss', 'nuxt-icon', '@nuxtjs/google-fonts'],
googleFonts: { googleFonts: {
@ -11,5 +6,5 @@ export default defineNuxtConfig({
'DM+Sans': ['600', '1000'], 'DM+Sans': ['600', '1000'],
'Protest+Riot': true 'Protest+Riot': true
} }
}, }
}) })

View File

@ -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/**"
] ]
} }
} }

View File

@ -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>('')
@ -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]

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -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')
@ -1403,9 +1407,7 @@ async function mergeVideoFile(
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(

View File

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

View File

@ -105,7 +105,7 @@ 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]

View File

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

View File

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

View File

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