added better dir and file creation and bugfixes
This commit is contained in:
parent
e74c822f49
commit
e59f808ae4
@ -5,6 +5,12 @@ A simple tool for downloading videos from Crunchyroll and ADN.
|
||||
- Linux
|
||||
## 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)
|
||||
## To-Do
|
||||
- ADN Downloader
|
||||
- Download Pause/Delete
|
||||
- Download Speed Display
|
||||
- Settings
|
||||
- Open Download Path
|
||||
## User Interface
|
||||
#### Home
|
||||
![Screenshot 2024-04-06 180439](https://github.com/Junon401/CR-Downloader/assets/166554835/b45c5799-3716-49c9-8abf-5fc7c9fe08c9)
|
||||
|
@ -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 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">
|
||||
<option value="adn">ADN</option>
|
||||
<!-- <option value="adn">ADN</option> -->
|
||||
<option value="crunchyroll">Crunchyroll</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -64,7 +64,7 @@
|
||||
<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" />
|
||||
</div>
|
||||
<div class="relative flex flex-col">
|
||||
<!-- <div class="relative flex flex-col">
|
||||
<input
|
||||
@click="getFolderPath()"
|
||||
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"
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
<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">
|
||||
<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 () => {
|
||||
|
||||
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 = {
|
||||
episodes: [selectedStartEpisode.value],
|
||||
episodes: selectedEpisodes,
|
||||
dubs: selectedDubs.value,
|
||||
subs: selectedSubs.value,
|
||||
dir: path.value,
|
||||
|
@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<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 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" />
|
||||
@ -11,8 +14,12 @@
|
||||
{{ p.status }}
|
||||
</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 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 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.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>
|
||||
@ -22,6 +29,7 @@
|
||||
<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 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
|
||||
@ -39,9 +47,10 @@ const playlist = ref<
|
||||
media: CrunchyEpisode
|
||||
dub: Array<{ locale: string; name: string }>
|
||||
sub: Array<{ locale: string; name: string }>
|
||||
dir: string,
|
||||
partsleft: number,
|
||||
partsdownloaded: number
|
||||
dir: string
|
||||
partsleft: number
|
||||
partsdownloaded: number
|
||||
downloadspeed: number
|
||||
}>
|
||||
>()
|
||||
|
||||
@ -53,9 +62,10 @@ const getPlaylist = async () => {
|
||||
media: CrunchyEpisode
|
||||
dub: Array<{ locale: string; name: string }>
|
||||
sub: Array<{ locale: string; name: string }>
|
||||
dir: string,
|
||||
partsleft: number,
|
||||
dir: string
|
||||
partsleft: number
|
||||
partsdownloaded: number
|
||||
downloadspeed: number
|
||||
}>
|
||||
>('http://localhost:8080/api/crunchyroll/playlist')
|
||||
|
||||
@ -71,6 +81,21 @@ const getPlaylist = async () => {
|
||||
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(() => {
|
||||
getPlaylist()
|
||||
|
||||
@ -79,7 +104,6 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.loading-a {
|
||||
animation: animation infinite 3s;
|
||||
}
|
||||
@ -97,5 +121,4 @@ onMounted(() => {
|
||||
left: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -34,8 +34,6 @@ interface PlaylistAttributes {
|
||||
sub: Array<string>
|
||||
hardsub: boolean,
|
||||
dir: string,
|
||||
partsdownloaded: number,
|
||||
partsleft: number,
|
||||
failedreason: string
|
||||
}
|
||||
|
||||
@ -45,6 +43,7 @@ interface PlaylistCreateAttributes {
|
||||
sub: Array<string>
|
||||
dir: string,
|
||||
hardsub: boolean,
|
||||
status: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'completed' | 'failed'
|
||||
}
|
||||
|
||||
const Account: ModelDefined<AccountAttributes, AccountCreateAttributes> = sequelize.define('Accounts', {
|
||||
@ -86,7 +85,6 @@ const Playlist: ModelDefined<PlaylistAttributes, PlaylistCreateAttributes> = seq
|
||||
status: {
|
||||
allowNull: false,
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'waiting'
|
||||
},
|
||||
media: {
|
||||
allowNull: false,
|
||||
@ -108,16 +106,6 @@ const Playlist: ModelDefined<PlaylistAttributes, PlaylistCreateAttributes> = seq
|
||||
allowNull: false,
|
||||
type: DataTypes.BOOLEAN
|
||||
},
|
||||
partsdownloaded: {
|
||||
allowNull: true,
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: 0
|
||||
},
|
||||
partsleft: {
|
||||
allowNull: true,
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: 0
|
||||
},
|
||||
failedreason: {
|
||||
allowNull: true,
|
||||
type: DataTypes.STRING
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { messageBox } from '../../../electron/background'
|
||||
import { CrunchyEpisodes, CrunchySeason } from '../../types/crunchyroll'
|
||||
@ -76,12 +76,22 @@ export async function addPlaylistController(
|
||||
const body = request.body;
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
export async function deleteCompletePlaylistController(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
|
||||
await deletePlaylist()
|
||||
|
||||
return reply.code(200).send()
|
||||
}
|
||||
|
||||
export async function getPlaylistController(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
@ -89,6 +99,20 @@ export async function getPlaylistController(
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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) {
|
||||
server.post(
|
||||
@ -72,6 +72,21 @@ async function crunchyrollRoutes(server: FastifyInstance) {
|
||||
},
|
||||
getPlaylistController
|
||||
)
|
||||
|
||||
server.delete(
|
||||
'/playlist',
|
||||
{
|
||||
schema: {
|
||||
response: {
|
||||
'4xx': {
|
||||
error: { type: 'string' },
|
||||
message: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
deleteCompletePlaylistController
|
||||
)
|
||||
}
|
||||
|
||||
export default crunchyrollRoutes
|
||||
|
@ -131,13 +131,21 @@ export async function safeLoginData(user: string, password: string, service: str
|
||||
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({
|
||||
media: e,
|
||||
sub: s,
|
||||
dub: d,
|
||||
dir: dir,
|
||||
hardsub: hardsub
|
||||
hardsub: hardsub,
|
||||
status
|
||||
})
|
||||
|
||||
return episode.get()
|
||||
@ -149,44 +157,44 @@ export async function getPlaylist() {
|
||||
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') {
|
||||
await Playlist.update({ status: status }, { where: { id: id } })
|
||||
}
|
||||
|
||||
export async function updatePlaylistToDownloadPartsByID(id: number, parts: number) {
|
||||
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)
|
||||
}
|
||||
var isDownloading: number = 0
|
||||
|
||||
async function checkPlaylists() {
|
||||
const episodes = await Playlist.findAll({ where: { status: 'waiting' } })
|
||||
const eps = await Playlist.findAll({ where: { status: 'waiting' } })
|
||||
|
||||
for (const e of episodes) {
|
||||
await updatePlaylistByID(e.dataValues.id, 'preparing')
|
||||
await downloadPlaylist(
|
||||
e.dataValues.media.id,
|
||||
(e as any).dataValues.dub.map((s: { locale: any }) => s.locale),
|
||||
(e as any).dataValues.sub.map((s: { locale: any }) => s.locale),
|
||||
e.dataValues.hardsub,
|
||||
e.dataValues.id
|
||||
)
|
||||
for (const e of eps) {
|
||||
if (isDownloading < 2 && e.dataValues.status === 'waiting') {
|
||||
updatePlaylistByID(e.dataValues.id, 'preparing')
|
||||
isDownloading++
|
||||
downloadPlaylist(
|
||||
e.dataValues.media.id,
|
||||
(e as any).dataValues.dub.map((s: { locale: any }) => s.locale),
|
||||
(e as any).dataValues.sub.map((s: { locale: any }) => s.locale),
|
||||
e.dataValues.hardsub,
|
||||
e.dataValues.id,
|
||||
e.dataValues.media.series_title,
|
||||
e.dataValues.media.season_number,
|
||||
e.dataValues.media.episode_number
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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 playlist = await crunchyGetPlaylist(e)
|
||||
var downloading: Array<{
|
||||
id: number
|
||||
downloadedParts: number
|
||||
partsToDownload: number
|
||||
downloadSpeed: number
|
||||
}> = []
|
||||
|
||||
console.log(dubs)
|
||||
console.log(subs)
|
||||
export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Array<string>, hardsub: boolean, downloadID: number, name: string, season: number, episode: number) {
|
||||
downloading.push({
|
||||
id: downloadID,
|
||||
downloadedParts: 0,
|
||||
partsToDownload: 0,
|
||||
downloadSpeed: 0
|
||||
})
|
||||
|
||||
await updatePlaylistByID(downloadID, 'downloading')
|
||||
|
||||
var playlist = await crunchyGetPlaylist(e)
|
||||
|
||||
if (!playlist) {
|
||||
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 videoFolder = await createFolder()
|
||||
|
||||
const seasonFolder = await createFolderName(`${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season}`)
|
||||
|
||||
const dubDownloadList: Array<{
|
||||
audio_locale: string
|
||||
guid: string
|
||||
@ -443,20 +485,6 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
@ -490,14 +522,13 @@ export async function downloadPlaylist(e: string, dubs: Array<string>, subs: Arr
|
||||
|
||||
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(audioFolder)
|
||||
|
||||
await updatePlaylistByID(downloadID, 'completed')
|
||||
await deleteFolder(videoFolder)
|
||||
|
||||
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) {
|
||||
var partsdownloaded = 0
|
||||
async function downloadParts(parts: { filename: string; url: string }[], downloadID: number, dir: string) {
|
||||
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()) {
|
||||
let success = false
|
||||
@ -544,10 +578,25 @@ async function downloadParts(parts: { filename: string; url: string }[], downloa
|
||||
try {
|
||||
const stream = fs.createWriteStream(`${path}/${part.filename}`)
|
||||
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`)
|
||||
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
|
||||
} catch (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(
|
||||
@ -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)
|
||||
|
||||
try {
|
||||
const list: Array<string> = []
|
||||
|
||||
await updatePlaylistByID(downloadID, 'merging')
|
||||
isDownloading--
|
||||
|
||||
for (const [index, part] of parts.entries()) {
|
||||
list.push(`${tmp}/${part.filename}`)
|
||||
}
|
||||
@ -648,11 +700,11 @@ async function mergeParts(parts: { filename: string; url: string }[], tmp: strin
|
||||
Ffmpeg()
|
||||
.input(concatenatedFile)
|
||||
.outputOptions('-c copy')
|
||||
.save(app.getPath('documents') + `/${tempname}.mp4`)
|
||||
.save(dir + `/${tempname}.mp4`)
|
||||
.on('end', async () => {
|
||||
console.log('Merging finished')
|
||||
await deleteFolder(tmp)
|
||||
return resolve(app.getPath('documents') + `/${tempname}.mp4`)
|
||||
return resolve(dir + `/${tempname}.mp4`)
|
||||
})
|
||||
})
|
||||
} 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<{
|
||||
locale: string
|
||||
name: string
|
||||
@ -796,7 +848,7 @@ async function mergeFile(video: string, audios: Array<string>, subs: Array<strin
|
||||
|
||||
output
|
||||
.addOptions(options)
|
||||
.saveToFile(app.getPath('documents') + `/${name}.mkv`)
|
||||
.saveToFile(path + `/${filename}.mkv`)
|
||||
.on('error', (error) => {
|
||||
console.log(error)
|
||||
reject(error)
|
||||
|
@ -65,7 +65,6 @@ function createWindow() {
|
||||
if (singleInstance(app, mainWindow)) return
|
||||
|
||||
// Open the DevTools.
|
||||
!isProduction &&
|
||||
mainWindow.webContents.openDevTools({
|
||||
mode: 'bottom'
|
||||
})
|
||||
|
Reference in New Issue
Block a user