diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..ef3f9b3
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Debug Main Process",
+ "type": "node",
+ "request": "launch",
+ "cwd": "${workspaceFolder}",
+ "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
+ "windows": {
+ "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
+ },
+ "args" : ["."],
+ "outputCapture": "std"
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/components/Settings/Naming.vue b/components/Settings/Naming.vue
index cd97085..197a84a 100644
--- a/components/Settings/Naming.vue
+++ b/components/Settings/Naming.vue
@@ -9,19 +9,13 @@
placeholder="Episode Naming"
class="bg-[#5c5b5b] w-full focus:outline-none px-3 py-2 rounded-xl text-sm text-center"
/>
-
- Example:
-
+ Example:
{{ `${episodeNaming}` }}
-
- Variables:
-
-
- {seriesName}, {seasonNumber}, {seasonNumberDD}, {episodeNumber}, {episodeNumberDD}, {quality}
-
+ Variables:
+ {seriesName}, {seasonNumber}, {seasonNumberDD}, {episodeNumber}, {episodeNumberDD}, {quality}
Season Folder Naming
@@ -32,30 +26,24 @@
placeholder="Episode Naming"
class="bg-[#5c5b5b] w-full focus:outline-none px-3 py-2 rounded-xl text-sm text-center"
/>
-
- Example:
-
+
Example:
{{ `${seasonNaming}` }}
-
- Variables:
-
-
- {seriesName}, {seasonNumber}, {seasonNumberDD}, {quality}
-
+
Variables:
+
{seriesName}, {seasonNumber}, {seasonNumberDD}, {quality}
diff --git a/pages/index.vue b/pages/index.vue
index 34ad988..01cdb3e 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -56,19 +56,35 @@
{{ p.status }}
-
+
{{ p.status }}
-
+
{{ p.status }}
-
+
{{ p.status }}
-
+
+
+ {{ p.status }}
+
+
{{ p.status }}
@@ -77,10 +93,13 @@
{{ p.status }}
-
-
- {{ a.status }} Audio {{ a.audio }}
-
+
+
+ {{ a.status }} Audio {{ a.audio }}
+
@@ -141,9 +160,9 @@ const playlist = ref<
service: string
format: string
audiosdownloading: {
- status: string,
- audio: string
- }[]
+ status: string
+ audio: string
+ }[]
}>
>()
@@ -165,7 +184,7 @@ const getPlaylist = async () => {
service: string
format: string
audiosdownloading: {
- status: string,
+ status: string
audio: string
}[]
}>
diff --git a/src/api/db/database.ts b/src/api/db/database.ts
index 6517512..b0d8935 100644
--- a/src/api/db/database.ts
+++ b/src/api/db/database.ts
@@ -29,7 +29,20 @@ interface AccountCreateAttributes {
interface PlaylistAttributes {
id: number
- status: 'waiting' | 'preparing' | 'waiting for playlist' | 'waiting for sub playlist' | 'waiting for dub playlist' | 'downloading' | 'merging video' | 'decrypting video' | 'awaiting all dubs downloaded' | 'merging video & audio' | 'completed' | 'failed'
+ status:
+ | 'waiting'
+ | 'preparing'
+ | 'waiting for playlist'
+ | 'waiting for sub playlist'
+ | 'waiting for dub playlist'
+ | 'downloading'
+ | 'downloading video'
+ | 'merging video'
+ | 'decrypting video'
+ | 'awaiting all dubs downloaded'
+ | 'merging video & audio'
+ | 'completed'
+ | 'failed'
media: CrunchyEpisode | ADNEpisode
dub: Array
sub: Array
@@ -49,7 +62,20 @@ interface PlaylistCreateAttributes {
dir: string
quality: 1080 | 720 | 480 | 360 | 240
hardsub: boolean
- status: 'waiting' | 'preparing' | 'waiting for playlist' | 'waiting for sub playlist' | 'waiting for dub playlist' | 'downloading' | 'merging video' | 'decrypting video' | 'awaiting all dubs downloaded' | 'merging video & audio' | 'completed' | 'failed'
+ status:
+ | 'waiting'
+ | 'preparing'
+ | 'waiting for playlist'
+ | 'waiting for sub playlist'
+ | 'waiting for dub playlist'
+ | 'downloading'
+ | 'downloading video'
+ | 'merging video'
+ | 'decrypting video'
+ | 'awaiting all dubs downloaded'
+ | 'merging video & audio'
+ | 'completed'
+ | 'failed'
service: 'CR' | 'ADN'
format: 'mp4' | 'mkv'
}
diff --git a/src/api/routes/crunchyroll/crunchyroll.service.ts b/src/api/routes/crunchyroll/crunchyroll.service.ts
index 634a632..e24478a 100644
--- a/src/api/routes/crunchyroll/crunchyroll.service.ts
+++ b/src/api/routes/crunchyroll/crunchyroll.service.ts
@@ -17,10 +17,14 @@ const crErrors = [
// Crunchyroll Login Handler
export async function crunchyLogin(user: string, passw: string, geo: string) {
var endpoint = await settings.get('CREndpoint')
- const drmL3blob = await settings.get('l3blob')
- const drmL3key = await settings.get('l3key')
+ const drmL3blob = await settings.has('l3blob')
+ const drmL3key = await settings.has('l3key')
if (!drmL3blob || !drmL3key) {
+ endpoint = 1
+ }
+
+ if (!endpoint) {
await settings.set('CREndpoint', 1)
endpoint = 1
}
@@ -71,8 +75,8 @@ async function crunchyLoginFetchProxy(user: string, passw: string, geo: string)
var body
var endpoint = await settings.get('CREndpoint')
- const drmL3blob = await settings.get('l3blob')
- const drmL3key = await settings.get('l3key')
+ const drmL3blob = await settings.has('l3blob')
+ const drmL3key = await settings.has('l3key')
var proxy:
| {
name: string
@@ -189,10 +193,14 @@ async function crunchyLoginFetch(user: string, passw: string) {
var body
var endpoint = await settings.get('CREndpoint')
- const drmL3blob = await settings.get('l3blob')
- const drmL3key = await settings.get('l3key')
+ const drmL3blob = await settings.has('l3blob')
+ const drmL3key = await settings.has('l3key')
if (!drmL3blob || !drmL3key) {
+ endpoint = 1
+ }
+
+ if (!endpoint) {
await settings.set('CREndpoint', 1)
endpoint = 1
}
@@ -274,6 +282,27 @@ async function crunchyLoginFetch(user: string, passw: string) {
}
}
+let counter = 0
+var maxLimit = 0
+
+async function incrementPlaylistCounter() {
+ return new Promise((resolve) => {
+ const interval = setInterval(() => {
+ if (counter < maxLimit) {
+ counter++
+ clearInterval(interval)
+ resolve()
+ }
+ }, 100)
+ })
+}
+
+function decrementPlaylistCounter() {
+ if (counter > 0) {
+ counter--
+ }
+}
+
// Crunchyroll Playlist Fetch
export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
const isProxyActive = await settings.get('proxyActive')
@@ -290,10 +319,14 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
}
var endpoint = await settings.get('CREndpoint')
- const drmL3blob = await settings.get('l3blob')
- const drmL3key = await settings.get('l3key')
+ const drmL3blob = await settings.has('l3blob')
+ const drmL3key = await settings.has('l3key')
if (!drmL3blob || !drmL3key) {
+ endpoint = 1
+ }
+
+ if (!endpoint) {
await settings.set('CREndpoint', 1)
endpoint = 1
}
@@ -383,6 +416,12 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
var playlist: VideoPlaylist
+ if (maxLimit === 0) {
+ maxLimit = await checkAccountMaxStreams()
+ }
+
+ await incrementPlaylistCounter()
+
try {
const response = await fetch(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}${
@@ -406,44 +445,12 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
playlist = data
} else {
const error = await response.text()
- const errorJSON: {
- activeStreams: {
- accountId: string
- active: boolean
- assetId: string
- clientId: string
- contentId: string
- country: string
- createdTimestamp: string
- deviceSubtype: string
- deviceType: string
- episodeIdentity: string
- id: string
- token: string
- }[]
- } = await JSON.parse(error)
-
- if (errorJSON && errorJSON.activeStreams && errorJSON.activeStreams.length !== 0) {
- for (const e of errorJSON.activeStreams) {
- await deleteVideoToken(e.contentId, e.token)
- }
-
- server.logger.log({
- level: 'error',
- message: 'Refetching Crunchyroll Video Playlist & Deleting all Video Token because too many streams',
- error: errorJSON,
- timestamp: new Date().toISOString(),
- section: 'playlistCrunchyrollFetch'
- })
-
- return await crunchyGetPlaylist(q, geo)
- }
messageBox('error', ['Cancel'], 2, 'Failed to get Crunchyroll Video Playlist', 'Failed to get Crunchyroll Video Playlist', error)
server.logger.log({
level: 'error',
message: 'Failed to get Crunchyroll Video Playlist',
- error: errorJSON,
+ error: error,
timestamp: new Date().toISOString(),
section: 'playlistCrunchyrollFetch'
})
@@ -466,6 +473,8 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
'User-Agent': 'Crunchyroll/1.8.0 Nintendo Switch/12.3.12.0 UE4/4.27'
}
+ await incrementPlaylistCounter()
+
const responseProx = await fetch(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}${
endpoints.find((e) => e.id === endpoint) ? endpoints.find((e) => e.id === endpoint)?.url : '/console/switch/play'
@@ -502,6 +511,8 @@ export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
}
await deleteVideoToken(q, dataProx.token)
+ } else {
+ decrementPlaylistCounter();
}
}
}
@@ -536,6 +547,9 @@ export async function deleteVideoToken(content: string, token: string) {
timestamp: new Date().toISOString(),
section: 'tokenDeletionCrunchyrollFetch'
})
+
+ decrementPlaylistCounter()
+
return 'ok'
} else {
const error = await response.text()
@@ -605,7 +619,7 @@ export async function getAccountInfo() {
if (!login) return
const headers = {
- Authorization: `Bearer ${login.access_token}`,
+ Authorization: `Bearer ${login.access_token}`
}
try {
@@ -616,8 +630,8 @@ export async function getAccountInfo() {
if (response.ok) {
const data: {
- account_id: string,
- external_id: string,
+ account_id: string
+ external_id: string
} = await JSON.parse(await response.text())
return data
@@ -640,7 +654,6 @@ export async function getAccountInfo() {
// Check Max account streams because of crunchyroll activestream limit
export async function checkAccountMaxStreams() {
-
const accountinfo = await getAccountInfo()
if (!accountinfo) return 1
@@ -654,7 +667,7 @@ export async function checkAccountMaxStreams() {
if (!login) return 1
const headers = {
- Authorization: `Bearer ${login.access_token}`,
+ Authorization: `Bearer ${login.access_token}`
}
try {
@@ -666,22 +679,22 @@ export async function checkAccountMaxStreams() {
if (response.ok) {
const data: {
items: {
- __class__: string,
- __href__: string,
- __links__: string,
- __actions__: string,
- benefit: string,
+ __class__: string
+ __href__: string
+ __links__: string
+ __actions__: string
+ benefit: string
source: string
}[]
} = await JSON.parse(await response.text())
if (!data.items || data.items.length === 0) return 1
- if (data.items.find(i => i.benefit === 'concurrent_streams.4')) return 2
+ if (data.items.find((i) => i.benefit === 'concurrent_streams.4')) return 2
- if (data.items.find(i => i.benefit === 'concurrent_streams.1')) return 1
+ if (data.items.find((i) => i.benefit === 'concurrent_streams.1')) return 1
- if (data.items.find(i => i.benefit === 'concurrent_streams.6')) return 3
+ if (data.items.find((i) => i.benefit === 'concurrent_streams.6')) return 3
return 1
} else {
@@ -699,4 +712,4 @@ export async function checkAccountMaxStreams() {
} catch (e) {
throw new Error(e as string)
}
- }
+}
diff --git a/src/api/routes/service/service.service.ts b/src/api/routes/service/service.service.ts
index c0b75c2..5fbfc12 100644
--- a/src/api/routes/service/service.service.ts
+++ b/src/api/routes/service/service.service.ts
@@ -171,6 +171,7 @@ export async function updatePlaylistByID(
| 'waiting for sub playlist'
| 'waiting for dub playlist'
| 'downloading'
+ | 'downloading video'
| 'merging video'
| 'decrypting video'
| 'awaiting all dubs downloaded'
@@ -193,7 +194,7 @@ export async function updatePlaylistByID(
section: 'playlistItemUpdateDatabase'
})
} catch (e) {
- messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to update playlist item', JSON.stringify(e))
+ messageBox('error', ['Cancel'], 2, 'Database Error', 'Failed to update playlist item', 'Failed to update playlist item')
server.logger.log({
level: 'error',
message: 'Failed to update playlist item',
@@ -218,6 +219,7 @@ export async function addEpisodeToPlaylist(
| 'waiting for sub playlist'
| 'waiting for dub playlist'
| 'downloading'
+ | 'downloading video'
| 'merging video'
| 'decrypting video'
| 'awaiting all dubs downloaded'
@@ -451,27 +453,6 @@ export async function downloadADNPlaylist(
await deleteFolder(videoFolder)
}
-var counter = 0
-var maxLimit = 1
-
-async function incrementPlaylistCounter() {
- return new Promise((resolve) => {
- const interval = setInterval(() => {
- if (counter < maxLimit) {
- counter++
- clearInterval(interval)
- resolve()
- }
- }, 100)
- })
-}
-
-function decrementPlaylistCounter() {
- if (counter > 0) {
- counter--
- }
-}
-
// Download Crunchyroll Playlist
export async function downloadCrunchyrollPlaylist(
e: string,
@@ -488,15 +469,8 @@ export async function downloadCrunchyrollPlaylist(
format: 'mp4' | 'mkv',
geo: string | undefined
) {
- const accmaxstream = await checkAccountMaxStreams()
-
- if (accmaxstream) {
- maxLimit = accmaxstream
- }
-
await updatePlaylistByID(downloadID, 'waiting for playlist')
- await incrementPlaylistCounter()
var playlist = await crunchyGetPlaylist(e, geo)
if (!playlist) {
@@ -517,8 +491,6 @@ export async function downloadCrunchyrollPlaylist(
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
if (found) {
await deleteVideoToken(episodeID, playlist.data.token)
- decrementPlaylistCounter()
- await incrementPlaylistCounter()
playlist = await crunchyGetPlaylist(found.guid, found.geo)
} else {
console.log('Exact Playlist not found, taking what crunchy gives.')
@@ -547,7 +519,6 @@ export async function downloadCrunchyrollPlaylist(
}
await deleteVideoToken(episodeID, playlist.data.token)
- decrementPlaylistCounter()
const subFolder = await createFolder()
@@ -600,7 +571,6 @@ export async function downloadCrunchyrollPlaylist(
if (playlist.data.audioLocale !== 'ja-JP') {
const foundStream = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
if (foundStream) {
- await incrementPlaylistCounter()
subPlaylist = await crunchyGetPlaylist(foundStream.guid, foundStream.geo)
}
} else {
@@ -641,7 +611,6 @@ export async function downloadCrunchyrollPlaylist(
}
await deleteVideoToken(episodeID, subPlaylist.data.token)
- decrementPlaylistCounter()
}
await updatePlaylistByID(downloadID, 'waiting for dub playlist')
@@ -653,11 +622,9 @@ export async function downloadCrunchyrollPlaylist(
}
if (found) {
- await incrementPlaylistCounter()
const list = await crunchyGetPlaylist(found.guid, found.geo)
if (list) {
await deleteVideoToken(episodeID, list.data.token)
- decrementPlaylistCounter()
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
if (foundSub) {
@@ -699,7 +666,7 @@ export async function downloadCrunchyrollPlaylist(
}
}
- await updatePlaylistByID(downloadID, 'downloading')
+ await updatePlaylistByID(downloadID, 'downloading video')
const subDownload = async () => {
const sbs: Array = []
@@ -713,7 +680,6 @@ export async function downloadCrunchyrollPlaylist(
const audioDownload = async () => {
const audios: Array = []
for (const v of dubDownloadList) {
- await incrementPlaylistCounter()
const list = await crunchyGetPlaylist(v.guid, v.geo)
if (!list) return
@@ -723,7 +689,6 @@ export async function downloadCrunchyrollPlaylist(
if (!playlist) return
await deleteVideoToken(episodeID, list.data.token)
- decrementPlaylistCounter()
const assetId = playlist.mediaGroups.AUDIO.audio.main.playlists[0].segments[0].resolvedUri.match(/\/assets\/(?:p\/)?([^_,]+)/)
@@ -837,7 +802,6 @@ export async function downloadCrunchyrollPlaylist(
return
}
- await incrementPlaylistCounter()
const play = await crunchyGetPlaylist(code, geo)
if (!play) {
@@ -882,7 +846,6 @@ export async function downloadCrunchyrollPlaylist(
if (!mdp) return
await deleteVideoToken(episodeID, play.data.token)
- decrementPlaylistCounter()
var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.height === quality)
diff --git a/src/api/services/folder.ts b/src/api/services/folder.ts
index f4c8718..0918982 100644
--- a/src/api/services/folder.ts
+++ b/src/api/services/folder.ts
@@ -4,8 +4,7 @@ import fs from 'fs'
import settings from 'electron-settings'
export async function createFolder() {
-
- var tempPath = await settings.get('tempPath') as string
+ var tempPath = (await settings.get('tempPath')) as string
if (!tempPath) {
tempPath = app.getPath('temp')
@@ -33,8 +32,7 @@ export async function checkDirectoryExistence(dir: string) {
}
export async function createFolderName(name: string, dir: string) {
-
- var tempPath = await settings.get('tempPath') as string
+ var tempPath = (await settings.get('tempPath')) as string
if (!tempPath) {
tempPath = app.getPath('temp')
@@ -69,8 +67,7 @@ export async function deleteFolder(folderPath: string) {
}
export async function deleteTemporaryFolders() {
-
- var tempPath = await settings.get('tempPath') as string
+ var tempPath = (await settings.get('tempPath')) as string
if (!tempPath) {
tempPath = app.getPath('temp')
diff --git a/src/electron/background.ts b/src/electron/background.ts
index 1c593f6..d3f7d02 100644
--- a/src/electron/background.ts
+++ b/src/electron/background.ts
@@ -18,6 +18,7 @@ var mainWindow: BrowserWindow
function createWindow() {
console.log('System info', { isProduction, platform, architucture })
+
mainWindow = new BrowserWindow({
title: 'Crunchyroll Downloader',
icon: __dirname + '/icon/favicon.ico',
@@ -73,6 +74,11 @@ function createWindow() {
// App events
// ==========
app.whenReady().then(async () => {
+ settings.configure({
+ dir: app.getPath('documents') + '/Crunchyroll Downloader/settings/',
+ atomicSave: process.platform !== 'win32'
+ })
+
startAPI()
const mainWindow = createWindow()
diff --git a/src/electron/preload.ts b/src/electron/preload.ts
index 5e4b102..c03deb9 100644
--- a/src/electron/preload.ts
+++ b/src/electron/preload.ts
@@ -22,5 +22,5 @@ contextBridge.exposeInMainWorld('myAPI', {
setEpisodeTemplate: (name: string) => ipcRenderer.invoke('dialog:setEpisodeTemplate', name),
getEpisodeTemplate: () => ipcRenderer.invoke('dialog:getEpisodeTemplate'),
setSeasonTemplate: (name: string) => ipcRenderer.invoke('dialog:setSeasonTemplate', name),
- getSeasonTemplate: () => ipcRenderer.invoke('dialog:getSeasonTemplate'),
+ getSeasonTemplate: () => ipcRenderer.invoke('dialog:getSeasonTemplate')
})