This commit is contained in:
Daniel Haller 2024-05-02 03:15:04 +02:00
parent 03bc1d5270
commit 755b69daf1
7 changed files with 96 additions and 62 deletions

7
.gitignore vendored
View File

@ -10,18 +10,19 @@ dist
.DS_Store
# Electron
crunchyroll-downloader-advanced-output
crunchyroll-downloader-output
debug.log
debug.log
*.log
debug.json
errors.json
build/
crunchyroll-downloader-advanced-output-*/
crunchyroll-downloader-output-*/
updater/
# FFMPEG
ffmpeg/
# MP4DECRYPT
mp4decrypt/
keys/
src/api/services/widevine.ts

View File

@ -6,8 +6,8 @@ const Platform = builder.Platform
* @type {import('electron-builder').Configuration}
*/
const options = {
appId: 'com.stratum.crunchyrolldownloaderadvanced',
productName: 'Crunchyroll Downloader Advanced',
appId: 'com.stratum.crunchyrolldownloader',
productName: 'Crunchyroll Downloader',
compression: 'maximum',
removePackageScripts: true,
@ -15,12 +15,17 @@ const options = {
nodeGypRebuild: true,
buildDependenciesFromSource: true,
publish: {
provider: 'github',
releaseType: 'release'
},
directories: {
output: 'crunchyroll-downloader-advanced-output-${version}'
output: 'crunchyroll-downloader-output-${version}'
},
win: {
artifactName: 'crunchyroll-downloader-advanced-${version}-windows-installer.${ext}',
artifactName: 'crunchyroll-downloader-${version}-windows-installer.${ext}',
icon: 'public/favicon.ico',
target: [
{

View File

@ -14,8 +14,7 @@
<img src="/logo.png" class="h-8" />
<div class="text-[10px] leading-[10px] text-opacity-90 text-white select-none"
>Crunchyroll <br />
Downloader <br />
Advanced</div
Downloader</div
>
</div>
<div class="w-full flex gap-2 flex-row items-center justify-center">

58
components/Updater.vue Normal file
View File

@ -0,0 +1,58 @@
<template>
<div
class="fixed bottom-3 right-5 p-3 flex flex-col bg-[#111111dc] w-80 min-h-24 rounded-xl font-dm text-white transition-all duration-300"
:class="
(data?.status === 'update-available' && !ignoreUpdate) || data?.status === 'downloading' || data?.status === 'update-downloaded'
? 'opacity-100'
: 'opacity-0 pointer-events-none'
"
>
<button @click="ignoreUpdate = true" class="absolute right-3 top-2">
<Icon name="akar-icons:cross" class="h-4 w-4 text-white" />
</button>
<div class="text-base text-center"> Update available </div>
<div class="text-sm text-center"> A new update is available </div>
<div v-if="data && data.info && data.info.version" class="text-sm text-center"> v{{ data.info.version }} </div>
<button @click="startDownload" v-if="data && data.status === 'update-available'" class="text-sm py-3 bg-[#363434] mt-5 rounded-xl" :disabled="downloading">
Download Update
</button>
<button v-if="data && data.status === 'downloading'" class="relative text-sm py-3 bg-[#363434] mt-5 rounded-xl overflow-hidden">
<div class="absolute top-0 left-0 w-full h-full bg-[#a1a1a141] transition-all duration-300" :style="`width: calc((${data.info.percent} / 100) * 100%);`"></div>
Downloading...
</button>
<button @click="startInstall" v-if="data && data.status === 'update-downloaded'" class="text-sm py-3 bg-[#363434] mt-5 rounded-xl" :disabled="installing">
Install Update
</button>
</div>
</template>
<script lang="ts" setup>
const data = ref<{ status: string; info: any }>()
const downloading = ref<boolean>(false)
const installing = ref<boolean>(false)
const ignoreUpdate = ref<boolean>(false)
const checkUpdate = () => {
;(window as any).myAPI.getUpdateStatus().then((result: any) => {
data.value = result
})
}
const startDownload = () => {
downloading.value = true
;(window as any).myAPI.startUpdateDownload()
}
const startInstall = () => {
installing.value = true
;(window as any).myAPI.startUpdateInstall()
}
onMounted(() => {
checkUpdate()
setInterval(checkUpdate, 2000)
})
</script>
<style></style>

View File

@ -1,10 +1,11 @@
{
"name": "crunchyroll-downloader-advanced",
"name": "crunchyroll-downloader",
"author": "Stratum",
"description": "Crunchyroll Downloader Advanced",
"version": "1.0.0",
"description": "Crunchyroll Downloader",
"version": "1.1.0",
"private": true,
"main": ".output/src/electron/background.js",
"repository": "https://github.com/stratuma/Crunchyroll-Downloader-v4.0",
"scripts": {
"dev": "nuxt dev -o",
"build": "nuxt generate",
@ -53,11 +54,9 @@
"fastify": "^4.26.2",
"fluent-ffmpeg": "^2.1.2",
"jsencrypt": "^3.3.2",
"long": "^5.2.3",
"mpd-parser": "^1.3.0",
"node-cache": "^5.1.2",
"node-cron": "^3.0.3",
"protobufjs": "^7.2.6",
"sequelize": "^6.37.3",
"sqlite3": "5.1.6"
},
@ -65,7 +64,7 @@
"extraResources": [
"./ffmpeg/**",
"./mp4decrypt/**",
"./keys/**"
"./updater/**"
]
}
}

View File

@ -6,6 +6,9 @@ import { parse as mpdParse } from 'mpd-parser'
import { loggedInCheck } from '../service/service.service'
import { app } from 'electron'
// Disable when Crunchyroll turns off switch endpoint
const enableDRMBypass = false
const crErrors = [
{
error: 'invalid_grant',
@ -118,46 +121,15 @@ export async function crunchyGetPlaylist(q: string) {
}
try {
const response = await fetch(`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}/console/switch/play`, {
method: 'GET',
headers: headers
})
if (response.ok) {
const data: VideoPlaylist = JSON.parse(await response.text())
data.hardSubs = Object.values((data as any).hardSubs)
data.subtitles = Object.values((data as any).subtitles)
return { data: data, account_id: login.account_id }
} else {
throw new Error(await response.text())
}
} catch (e) {
throw new Error(e as string)
}
}
export async function crunchyGetPlaylistDRM(q: string) {
const account = await loggedInCheck('CR')
if (!account) return
const { data: login, error } = await crunchyLogin(account.username, account.password)
if (!login) return
const headers = {
Authorization: `Bearer ${login.access_token}`,
'x-cr-stream-limits': 'false'
}
try {
const response = await fetch(`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}/tv/samsung/play`, {
method: 'GET',
headers: headers
})
const response = await fetch(
enableDRMBypass
? `https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}/tv/samsung/play`
: `https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}/console/switch/play`,
{
method: 'GET',
headers: headers
}
)
if (response.ok) {
const data: VideoPlaylist = JSON.parse(await response.text())

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 { crunchyGetPlaylist, crunchyGetPlaylistDRM, crunchyGetPlaylistMPD, deleteVideoToken } from '../crunchyroll/crunchyroll.service'
import { crunchyGetPlaylist, crunchyGetPlaylistMPD, deleteVideoToken } from '../crunchyroll/crunchyroll.service'
import fs from 'fs'
var cron = require('node-cron')
import { Readable } from 'stream'
@ -327,7 +327,7 @@ export async function downloadCrunchyrollPlaylist(
await updatePlaylistByID(downloadID, 'downloading')
var playlist = await crunchyGetPlaylistDRM(e)
var playlist = await crunchyGetPlaylist(e)
if (!playlist) {
await updatePlaylistByID(downloadID, 'failed')
@ -340,7 +340,7 @@ export async function downloadCrunchyrollPlaylist(
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
if (found) {
await deleteVideoToken(episodeID, playlist.data.token)
playlist = await crunchyGetPlaylistDRM(found.guid)
playlist = await crunchyGetPlaylist(found.guid)
} else {
console.log('Exact Playlist not found, taking what crunchy gives.'),
messageBox(
@ -394,7 +394,7 @@ export async function downloadCrunchyrollPlaylist(
if (playlist.data.audioLocale !== 'ja-JP') {
const foundStream = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
if (foundStream) {
subPlaylist = await crunchyGetPlaylistDRM(foundStream.guid)
subPlaylist = await crunchyGetPlaylist(foundStream.guid)
}
} else {
subPlaylist = playlist
@ -424,7 +424,7 @@ export async function downloadCrunchyrollPlaylist(
}
if (found) {
const list = await crunchyGetPlaylistDRM(found.guid)
const list = await crunchyGetPlaylist(found.guid)
if (list) {
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
if (foundSub) {
@ -479,7 +479,7 @@ export async function downloadCrunchyrollPlaylist(
const audioDownload = async () => {
const audios: Array<string> = []
for (const v of dubDownloadList) {
const list = await crunchyGetPlaylistDRM(v.guid)
const list = await crunchyGetPlaylist(v.guid)
if (!list) return
@ -554,7 +554,7 @@ export async function downloadCrunchyrollPlaylist(
return
}
const play = await crunchyGetPlaylistDRM(code)
const play = await crunchyGetPlaylist(code)
if (!play) {
await updatePlaylistByID(downloadID, 'failed')