added better dir and file creation and bugfixes

This commit is contained in:
Daniel Haller 2024-04-18 00:44:18 +02:00
parent e74c822f49
commit e59f808ae4
8 changed files with 219 additions and 100 deletions

View File

@ -5,6 +5,12 @@ A simple tool for downloading videos from Crunchyroll and ADN.
- Linux - Linux
## Credits ## Credits
- This is literally just an improved version of [hama3254's](https://github.com/hama3254/Crunchyroll-Downloader-v3.0) [Crunchyroll-Downloader-v3.0](https://github.com/hama3254/Crunchyroll-Downloader-v3.0) - This is literally just an improved version of [hama3254's](https://github.com/hama3254/Crunchyroll-Downloader-v3.0) [Crunchyroll-Downloader-v3.0](https://github.com/hama3254/Crunchyroll-Downloader-v3.0)
## To-Do
- ADN Downloader
- Download Pause/Delete
- Download Speed Display
- Settings
- Open Download Path
## User Interface ## User Interface
#### Home #### Home
![Screenshot 2024-04-06 180439](https://github.com/Junon401/CR-Downloader/assets/166554835/b45c5799-3716-49c9-8abf-5fc7c9fe08c9) ![Screenshot 2024-04-06 180439](https://github.com/Junon401/CR-Downloader/assets/166554835/b45c5799-3716-49c9-8abf-5fc7c9fe08c9)

View File

@ -15,7 +15,7 @@
<div v-if="tab === 1" class="flex flex-col mt-5 gap-3.5 h-full" style="-webkit-app-region: no-drag"> <div v-if="tab === 1" class="flex flex-col mt-5 gap-3.5 h-full" style="-webkit-app-region: no-drag">
<div class="relative flex flex-col"> <div class="relative flex flex-col">
<select v-model="service" name="service" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer"> <select v-model="service" name="service" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer">
<option value="adn">ADN</option> <!-- <option value="adn">ADN</option> -->
<option value="crunchyroll">Crunchyroll</option> <option value="crunchyroll">Crunchyroll</option>
</select> </select>
</div> </div>
@ -64,7 +64,7 @@
<div class="relative flex flex-col"> <div class="relative flex flex-col">
<input v-model="url" type="text" name="text" placeholder="URL" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center" /> <input v-model="url" type="text" name="text" placeholder="URL" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center" />
</div> </div>
<div class="relative flex flex-col"> <!-- <div class="relative flex flex-col">
<input <input
@click="getFolderPath()" @click="getFolderPath()"
v-model="path" v-model="path"
@ -74,7 +74,7 @@
class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer"
readonly readonly
/> />
</div> </div> -->
<div class="relative flex flex-col mt-auto"> <div class="relative flex flex-col mt-auto">
<button @click="switchToSeason" class="relative py-3 border-2 rounded-xl flex flex-row items-center justify-center"> <button @click="switchToSeason" class="relative py-3 border-2 rounded-xl flex flex-row items-center justify-center">
<div class="flex flex-row items-center justify-center transition-all" :class="isFetchingSeasons ? 'opacity-0' : 'opacity-100'"> <div class="flex flex-row items-center justify-center transition-all" :class="isFetchingSeasons ? 'opacity-0' : 'opacity-100'">
@ -421,8 +421,20 @@ const toggleSub = (lang: { name: string | undefined; locale: string }) => {
const addToPlaylist = async () => { const addToPlaylist = async () => {
if (!episodes.value) return
const startEpisodeIndex = episodes.value.findIndex(episode => episode === selectedStartEpisode.value);
const endEpisodeIndex = episodes.value.findIndex(episode => episode === selectedEndEpisode.value);
if (startEpisodeIndex === -1 || endEpisodeIndex === -1) {
console.error('Indexes not found.');
return;
}
const selectedEpisodes = episodes.value.slice(startEpisodeIndex, endEpisodeIndex + 1);
const data = { const data = {
episodes: [selectedStartEpisode.value], episodes: selectedEpisodes,
dubs: selectedDubs.value, dubs: selectedDubs.value,
subs: selectedSubs.value, subs: selectedSubs.value,
dir: path.value, dir: path.value,

View File

@ -1,7 +1,10 @@
<template> <template>
<div> <div>
<MainHeader /> <MainHeader />
<div class="flex flex-col text-white"> <div class="flex flex-col text-white pt-16">
<button @click="deletePlaylist">
Delete Playlist
</button>
<div v-for="p in playlist" class="flex flex-row gap-4 h-40 p-5 bg-[#636363]"> <div v-for="p in playlist" class="flex flex-row gap-4 h-40 p-5 bg-[#636363]">
<div class="flex min-w-52 w-52"> <div class="flex min-w-52 w-52">
<img :src="p.media.images.thumbnail[0].find((p) => p.height === 1080)?.source" alt="Image" class="object-cover rounded-xl" /> <img :src="p.media.images.thumbnail[0].find((p) => p.height === 1080)?.source" alt="Image" class="object-cover rounded-xl" />
@ -12,7 +15,11 @@
</div> </div>
<div class="text-base capitalize"> {{ p.media.series_title }} Season {{ p.media.season_number }} Episode {{ p.media.episode_number }} </div> <div class="text-base capitalize"> {{ p.media.series_title }} Season {{ p.media.season_number }} Episode {{ p.media.episode_number }} </div>
<div class="relative w-full min-h-5 bg-[#bdbbbb] mt-1 rounded"> <div class="relative w-full min-h-5 bg-[#bdbbbb] mt-1 rounded">
<div v-if="p.partsleft && p.status === 'downloading'" class="w-full h-full rounded bg-[#4e422d] transition-all duration-300" :style="`width: calc((${p.partsdownloaded} / ${p.partsleft}) * 100%);`"></div> <div
v-if="p.partsleft && p.status === 'downloading'"
class="w-full h-full rounded bg-[#4e422d] transition-all duration-300"
:style="`width: calc((${p.partsdownloaded} / ${p.partsleft}) * 100%);`"
></div>
<div v-if="p.status === 'completed'" class="w-full h-full rounded bg-[#79ff77] transition-all duration-300"></div> <div v-if="p.status === 'completed'" class="w-full h-full rounded bg-[#79ff77] transition-all duration-300"></div>
<div v-if="p.status === 'merging'" class="absolute top-0 w-20 h-full rounded bg-[#293129] transition-all duration-300 loading-a"></div> <div v-if="p.status === 'merging'" class="absolute top-0 w-20 h-full rounded bg-[#293129] transition-all duration-300 loading-a"></div>
</div> </div>
@ -22,6 +29,7 @@
<div class="text-sm">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div> <div class="text-sm">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div>
<div class="text-sm">Subs: {{ p.sub.map((t) => t.name).join(', ') }}</div> <div class="text-sm">Subs: {{ p.sub.map((t) => t.name).join(', ') }}</div>
<div v-if="p.partsleft && p.status === 'downloading'" class="text-sm">{{ p.partsdownloaded }}/{{ p.partsleft }}</div> <div v-if="p.partsleft && p.status === 'downloading'" class="text-sm">{{ p.partsdownloaded }}/{{ p.partsleft }}</div>
<div v-if="p.downloadspeed && p.status === 'downloading'" class="text-sm">{{ p.downloadspeed }} MB/s</div>
</div> </div>
</div> </div>
</div> </div </div> </div
@ -39,9 +47,10 @@ const playlist = ref<
media: CrunchyEpisode media: CrunchyEpisode
dub: Array<{ locale: string; name: string }> dub: Array<{ locale: string; name: string }>
sub: Array<{ locale: string; name: string }> sub: Array<{ locale: string; name: string }>
dir: string, dir: string
partsleft: number, partsleft: number
partsdownloaded: number partsdownloaded: number
downloadspeed: number
}> }>
>() >()
@ -53,9 +62,10 @@ const getPlaylist = async () => {
media: CrunchyEpisode media: CrunchyEpisode
dub: Array<{ locale: string; name: string }> dub: Array<{ locale: string; name: string }>
sub: Array<{ locale: string; name: string }> sub: Array<{ locale: string; name: string }>
dir: string, dir: string
partsleft: number, partsleft: number
partsdownloaded: number partsdownloaded: number
downloadspeed: number
}> }>
>('http://localhost:8080/api/crunchyroll/playlist') >('http://localhost:8080/api/crunchyroll/playlist')
@ -71,6 +81,21 @@ const getPlaylist = async () => {
playlist.value = data.value playlist.value = data.value
} }
const deletePlaylist = async () => {
const { data, error } = await useFetch('http://localhost:8080/api/crunchyroll/playlist', {
method: "delete"
})
if (error.value) {
alert(error.value)
return
}
if (!data.value) {
return
}
}
onMounted(() => { onMounted(() => {
getPlaylist() getPlaylist()
@ -79,7 +104,6 @@ onMounted(() => {
</script> </script>
<style> <style>
.loading-a { .loading-a {
animation: animation infinite 3s; animation: animation infinite 3s;
} }
@ -97,5 +121,4 @@ onMounted(() => {
left: 0%; left: 0%;
} }
} }
</style> </style>

View File

@ -34,8 +34,6 @@ interface PlaylistAttributes {
sub: Array<string> sub: Array<string>
hardsub: boolean, hardsub: boolean,
dir: string, dir: string,
partsdownloaded: number,
partsleft: number,
failedreason: string failedreason: string
} }
@ -45,6 +43,7 @@ interface PlaylistCreateAttributes {
sub: Array<string> sub: Array<string>
dir: string, dir: string,
hardsub: boolean, hardsub: boolean,
status: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'completed' | 'failed'
} }
const Account: ModelDefined<AccountAttributes, AccountCreateAttributes> = sequelize.define('Accounts', { const Account: ModelDefined<AccountAttributes, AccountCreateAttributes> = sequelize.define('Accounts', {
@ -86,7 +85,6 @@ const Playlist: ModelDefined<PlaylistAttributes, PlaylistCreateAttributes> = seq
status: { status: {
allowNull: false, allowNull: false,
type: DataTypes.STRING, type: DataTypes.STRING,
defaultValue: 'waiting'
}, },
media: { media: {
allowNull: false, allowNull: false,
@ -108,16 +106,6 @@ const Playlist: ModelDefined<PlaylistAttributes, PlaylistCreateAttributes> = seq
allowNull: false, allowNull: false,
type: DataTypes.BOOLEAN type: DataTypes.BOOLEAN
}, },
partsdownloaded: {
allowNull: true,
type: DataTypes.BOOLEAN,
defaultValue: 0
},
partsleft: {
allowNull: true,
type: DataTypes.BOOLEAN,
defaultValue: 0
},
failedreason: { failedreason: {
allowNull: true, allowNull: true,
type: DataTypes.STRING type: DataTypes.STRING

View File

@ -1,5 +1,5 @@
import type { FastifyReply, FastifyRequest } from 'fastify' import type { FastifyReply, FastifyRequest } from 'fastify'
import { crunchyLogin, checkIfLoggedInCR, safeLoginData, addEpisodeToPlaylist, getPlaylist } from './crunchyroll.service' import { crunchyLogin, checkIfLoggedInCR, safeLoginData, addEpisodeToPlaylist, getPlaylist, getDownloading, deletePlaylist } from './crunchyroll.service'
import { dialog } from 'electron' import { dialog } from 'electron'
import { messageBox } from '../../../electron/background' import { messageBox } from '../../../electron/background'
import { CrunchyEpisodes, CrunchySeason } from '../../types/crunchyroll' import { CrunchyEpisodes, CrunchySeason } from '../../types/crunchyroll'
@ -76,12 +76,22 @@ export async function addPlaylistController(
const body = request.body; const body = request.body;
for (const e of body.episodes) { for (const e of body.episodes) {
await addEpisodeToPlaylist(e, body.subs, body.dubs, body.dir, body.hardsub) await addEpisodeToPlaylist(e, body.subs, body.dubs, body.dir, body.hardsub, "waiting")
} }
return reply.code(201).send() return reply.code(201).send()
} }
export async function deleteCompletePlaylistController(
request: FastifyRequest,
reply: FastifyReply
) {
await deletePlaylist()
return reply.code(200).send()
}
export async function getPlaylistController( export async function getPlaylistController(
request: FastifyRequest, request: FastifyRequest,
reply: FastifyReply reply: FastifyReply
@ -89,6 +99,20 @@ export async function getPlaylistController(
const playlist = await getPlaylist() const playlist = await getPlaylist()
for (const v of playlist) {
if (v.dataValues.status === 'downloading') {
const found = await getDownloading(v.dataValues.id)
if (found) {
(v as any).dataValues = {
...v.dataValues,
partsleft: found.partsToDownload,
partsdownloaded: found.downloadedParts,
downloadspeed: found.downloadSpeed.toFixed(2)
}
}
}
}
return reply.code(200).send(playlist.reverse()) return reply.code(200).send(playlist.reverse())
} }

View File

@ -1,5 +1,5 @@
import type { FastifyInstance } from 'fastify' import type { FastifyInstance } from 'fastify'
import { addPlaylistController, checkLoginController, getPlaylistController, loginController, loginLoginController } from './crunchyroll.controller' import { addPlaylistController, checkLoginController, deleteCompletePlaylistController, getPlaylistController, loginController, loginLoginController } from './crunchyroll.controller'
async function crunchyrollRoutes(server: FastifyInstance) { async function crunchyrollRoutes(server: FastifyInstance) {
server.post( server.post(
@ -72,6 +72,21 @@ async function crunchyrollRoutes(server: FastifyInstance) {
}, },
getPlaylistController getPlaylistController
) )
server.delete(
'/playlist',
{
schema: {
response: {
'4xx': {
error: { type: 'string' },
message: { type: 'string' }
}
}
}
},
deleteCompletePlaylistController
)
} }
export default crunchyrollRoutes export default crunchyrollRoutes

View File

@ -131,13 +131,21 @@ export async function safeLoginData(user: string, password: string, service: str
return login?.get() return login?.get()
} }
export async function addEpisodeToPlaylist(e: CrunchyEpisode, s: Array<string>, d: Array<string>, dir: string, hardsub: boolean) { export async function addEpisodeToPlaylist(
e: CrunchyEpisode,
s: Array<string>,
d: Array<string>,
dir: string,
hardsub: boolean,
status: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'completed' | 'failed'
) {
const episode = await Playlist.create({ const episode = await Playlist.create({
media: e, media: e,
sub: s, sub: s,
dub: d, dub: d,
dir: dir, dir: dir,
hardsub: hardsub hardsub: hardsub,
status
}) })
return episode.get() return episode.get()
@ -149,45 +157,45 @@ export async function getPlaylist() {
return episodes return episodes
} }
export async function deletePlaylist() {
await Playlist.truncate()
return true
}
export async function getDownloading(id: number) {
const found = downloading.find((i) => i.id === id)
if (found) return found
return null
}
export async function updatePlaylistByID(id: number, status: 'waiting' | 'preparing' | 'downloading' | 'completed' | 'merging' | 'failed') { export async function updatePlaylistByID(id: number, status: 'waiting' | 'preparing' | 'downloading' | 'completed' | 'merging' | 'failed') {
await Playlist.update({ status: status }, { where: { id: id } }) await Playlist.update({ status: status }, { where: { id: id } })
} }
export async function updatePlaylistToDownloadPartsByID(id: number, parts: number) { var isDownloading: number = 0
await Playlist.update({ partsleft: parts }, { where: { id: id } })
}
let updateTimeout: NodeJS.Timeout | null = null
let cooldown = false
export async function updatePlaylistToDownloadedPartsByID(id: number, parts: number, lenght: number) {
if (cooldown && parts !== lenght) {
return
}
cooldown = true
await Playlist.update({ partsdownloaded: parts }, { where: { id: id } })
updateTimeout = setTimeout(function () {
cooldown = false
updateTimeout = null
}, 2000)
}
async function checkPlaylists() { async function checkPlaylists() {
const episodes = await Playlist.findAll({ where: { status: 'waiting' } }) const eps = await Playlist.findAll({ where: { status: 'waiting' } })
for (const e of episodes) { for (const e of eps) {
await updatePlaylistByID(e.dataValues.id, 'preparing') if (isDownloading < 2 && e.dataValues.status === 'waiting') {
await downloadPlaylist( updatePlaylistByID(e.dataValues.id, 'preparing')
isDownloading++
downloadPlaylist(
e.dataValues.media.id, e.dataValues.media.id,
(e as any).dataValues.dub.map((s: { locale: any }) => s.locale), (e as any).dataValues.dub.map((s: { locale: any }) => s.locale),
(e as any).dataValues.sub.map((s: { locale: any }) => s.locale), (e as any).dataValues.sub.map((s: { locale: any }) => s.locale),
e.dataValues.hardsub, e.dataValues.hardsub,
e.dataValues.id e.dataValues.id,
e.dataValues.media.series_title,
e.dataValues.media.season_number,
e.dataValues.media.episode_number
) )
} }
}
} }
cron.schedule('*/2 * * * * *', () => { cron.schedule('*/2 * * * * *', () => {
@ -249,6 +257,23 @@ async function createFolder() {
} }
} }
async function createFolderName(name: string) {
const folderPath = path.join(app.getPath('documents'), name)
try {
await fs.promises.access(folderPath)
return folderPath
} catch (error) {
try {
await fs.promises.mkdir(folderPath, { recursive: true })
return folderPath
} catch (mkdirError) {
console.error('Error creating season folder:', mkdirError)
throw mkdirError
}
}
}
async function deleteFolder(folderPath: string) { async function deleteFolder(folderPath: string) {
fs.rmSync(folderPath, { recursive: true, force: true }) fs.rmSync(folderPath, { recursive: true, force: true })
} }
@ -285,11 +310,24 @@ export async function crunchyGetPlaylistMPD(q: string) {
} }
} }
export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Array<string>, hardsub: boolean, downloadID: number) { var downloading: Array<{
var playlist = await crunchyGetPlaylist(e) id: number
downloadedParts: number
partsToDownload: number
downloadSpeed: number
}> = []
console.log(dubs) export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Array<string>, hardsub: boolean, downloadID: number, name: string, season: number, episode: number) {
console.log(subs) downloading.push({
id: downloadID,
downloadedParts: 0,
partsToDownload: 0,
downloadSpeed: 0
})
await updatePlaylistByID(downloadID, 'downloading')
var playlist = await crunchyGetPlaylist(e)
if (!playlist) { if (!playlist) {
console.log('Playlist not found') console.log('Playlist not found')
@ -312,6 +350,10 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
const audioFolder = await createFolder() const audioFolder = await createFolder()
const videoFolder = await createFolder()
const seasonFolder = await createFolderName(`${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season}`)
const dubDownloadList: Array<{ const dubDownloadList: Array<{
audio_locale: string audio_locale: string
guid: string guid: string
@ -443,20 +485,6 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
var mdp = await crunchyGetPlaylistMPD(play.url) var mdp = await crunchyGetPlaylistMPD(play.url)
// if (hardsub) {
// const hardsuburl = play.hardSubs.find(h=> h.hlang === subs[0])?.url
// if (!hardsuburl) {
// console.error('No Hardsub stream found')
// return
// }
// mdp = await crunchyGetPlaylistMPD(hardsuburl)
// console.error('Hardsub stream found')
// } else {
// console.error('Hardsub is false')
// }
if (!mdp) return if (!mdp) return
var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.width === 1920) var hq = mdp.playlists.find((i) => i.attributes.RESOLUTION?.width === 1920)
@ -477,11 +505,15 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
}) })
} }
await updatePlaylistByID(downloadID, 'downloading') // await updatePlaylistToDownloadPartsByID(downloadID, p.length)
await updatePlaylistToDownloadPartsByID(downloadID, p.length) const dn = downloading.find((i) => i.id === downloadID)
const file = await downloadParts(p, downloadID) if (dn) {
dn.partsToDownload = p.length
}
const file = await downloadParts(p, downloadID, videoFolder)
return file return file
} }
@ -490,14 +522,13 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
if (!audios) return if (!audios) return
await updatePlaylistByID(downloadID, 'merging') await mergeFile(file as string, audios, subss, String(playlist.assetId), seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`)
await mergeFile(file as string, audios, subss, String(playlist.assetId)) await updatePlaylistByID(downloadID, 'completed')
await deleteFolder(subFolder) await deleteFolder(subFolder)
await deleteFolder(audioFolder) await deleteFolder(audioFolder)
await deleteFolder(videoFolder)
await updatePlaylistByID(downloadID, 'completed')
return playlist return playlist
} }
@ -534,9 +565,12 @@ async function fetchAndPipe(url: string, stream: fs.WriteStream, index: number)
}) })
} }
async function downloadParts(parts: { filename: string; url: string }[], downloadID: number) { async function downloadParts(parts: { filename: string; url: string }[], downloadID: number, dir: string) {
var partsdownloaded = 0
const path = await createFolder() const path = await createFolder()
const dn = downloading.find((i) => i.id === downloadID)
let totalDownloadedBytes = 0
let startTime = Date.now()
for (const [index, part] of parts.entries()) { for (const [index, part] of parts.entries()) {
let success = false let success = false
@ -544,10 +578,25 @@ async function downloadParts(parts: { filename: string; url: string }[], downloa
try { try {
const stream = fs.createWriteStream(`${path}/${part.filename}`) const stream = fs.createWriteStream(`${path}/${part.filename}`)
const { body } = await fetch(part.url) const { body } = await fetch(part.url)
await finished(Readable.fromWeb(body as any).pipe(stream))
const readableStream = Readable.from(body as any)
let partDownloadedBytes = 0
readableStream.on('data', (chunk) => {
partDownloadedBytes += chunk.length
totalDownloadedBytes += chunk.length
})
await finished(readableStream.pipe(stream))
console.log(`Fragment ${index + 1} downloaded`) console.log(`Fragment ${index + 1} downloaded`)
partsdownloaded++
updatePlaylistToDownloadedPartsByID(downloadID, partsdownloaded, parts.length) if (dn) {
dn.downloadedParts++
const endTime = Date.now()
const durationInSeconds = (endTime - startTime) / 1000
dn.downloadSpeed = totalDownloadedBytes / 1024 / 1024 / durationInSeconds
}
success = true success = true
} catch (error) { } catch (error) {
console.error(`Error occurred during download of fragment ${index + 1}:`, error) console.error(`Error occurred during download of fragment ${index + 1}:`, error)
@ -557,7 +606,7 @@ async function downloadParts(parts: { filename: string; url: string }[], downloa
} }
} }
return await mergeParts(parts, path) return await mergeParts(parts, downloadID, path, dir)
} }
async function downloadSub( async function downloadSub(
@ -631,12 +680,15 @@ async function concatenateTSFiles(inputFiles: Array<string>, outputFile: string)
}) })
} }
async function mergeParts(parts: { filename: string; url: string }[], tmp: string) { async function mergeParts(parts: { filename: string; url: string }[], downloadID: number, tmp: string, dir: string) {
const tempname = (Math.random() + 1).toString(36).substring(2) const tempname = (Math.random() + 1).toString(36).substring(2)
try { try {
const list: Array<string> = [] const list: Array<string> = []
await updatePlaylistByID(downloadID, 'merging')
isDownloading--
for (const [index, part] of parts.entries()) { for (const [index, part] of parts.entries()) {
list.push(`${tmp}/${part.filename}`) list.push(`${tmp}/${part.filename}`)
} }
@ -648,11 +700,11 @@ async function mergeParts(parts: { filename: string; url: string }[], tmp: strin
Ffmpeg() Ffmpeg()
.input(concatenatedFile) .input(concatenatedFile)
.outputOptions('-c copy') .outputOptions('-c copy')
.save(app.getPath('documents') + `/${tempname}.mp4`) .save(dir + `/${tempname}.mp4`)
.on('end', async () => { .on('end', async () => {
console.log('Merging finished') console.log('Merging finished')
await deleteFolder(tmp) await deleteFolder(tmp)
return resolve(app.getPath('documents') + `/${tempname}.mp4`) return resolve(dir + `/${tempname}.mp4`)
}) })
}) })
} catch (error) { } catch (error) {
@ -688,7 +740,7 @@ async function mergePartsAudio(parts: { filename: string; url: string }[], tmp:
} }
} }
async function mergeFile(video: string, audios: Array<string>, subs: Array<string>, name: string) { async function mergeFile(video: string, audios: Array<string>, subs: Array<string>, name: string, path: string, filename: string) {
const locales: Array<{ const locales: Array<{
locale: string locale: string
name: string name: string
@ -796,7 +848,7 @@ async function mergeFile(video: string, audios: Array<string>, subs: Array<strin
output output
.addOptions(options) .addOptions(options)
.saveToFile(app.getPath('documents') + `/${name}.mkv`) .saveToFile(path + `/${filename}.mkv`)
.on('error', (error) => { .on('error', (error) => {
console.log(error) console.log(error)
reject(error) reject(error)

View File

@ -65,7 +65,6 @@ function createWindow() {
if (singleInstance(app, mainWindow)) return if (singleInstance(app, mainWindow)) return
// Open the DevTools. // Open the DevTools.
!isProduction &&
mainWindow.webContents.openDevTools({ mainWindow.webContents.openDevTools({
mode: 'bottom' mode: 'bottom'
}) })