Small bug fixes and added time to objects.

This commit is contained in:
ArabCoders 2023-11-25 00:23:29 +03:00
parent 10a31b5ee8
commit b7bf9627a1
13 changed files with 82 additions and 26 deletions

3
.vscode/launch.json vendored
View file

@ -33,8 +33,7 @@
"YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads", "YTP_DOWNLOAD_PATH": "${workspaceFolder}/var/downloads",
"YTP_TEMP_PATH": "${workspaceFolder}/var/tmp", "YTP_TEMP_PATH": "${workspaceFolder}/var/tmp",
"YTP_YTDL_OPTIONS_FILE": "${workspaceFolder}/var/config/ytdlp.json", "YTP_YTDL_OPTIONS_FILE": "${workspaceFolder}/var/config/ytdlp.json",
"YTP_URL_HOST": "http://localhost:8081", "YTP_URL_HOST": "http://localhost:8081"
"YTP_KEEP_ARCHIVE": "true",
} }
} }
] ]

View file

@ -23,6 +23,7 @@ class Config:
ytdl_options: dict | str = {} ytdl_options: dict | str = {}
ytdl_options_file: str = '' ytdl_options_file: str = ''
ytdl_debug: bool = False
host: str = '0.0.0.0' host: str = '0.0.0.0'
port: int = 8081 port: int = 8081
@ -31,7 +32,7 @@ class Config:
base_path: str = '' base_path: str = ''
_boolean_vars: tuple = ('keep_archive') _boolean_vars: tuple = ('keep_archive', 'ytdl_debug')
def __init__(self): def __init__(self):
baseDefualtPath: str = os.path.dirname(os.path.dirname(__file__)) baseDefualtPath: str = os.path.dirname(os.path.dirname(__file__))

View file

@ -19,7 +19,7 @@ class ItemDTO:
ytdlp_cookies: str = None ytdlp_cookies: str = None
ytdlp_config: dict = field(default_factory=dict) ytdlp_config: dict = field(default_factory=dict)
output_template: str = None output_template: str = None
timestamp: int = time.time_ns() timestamp: float = time.time_ns()
is_live: bool = None is_live: bool = None
# yt-dlp injected fields. # yt-dlp injected fields.

View file

@ -50,7 +50,7 @@ class DataStore:
with sqlite3.connect(self.db_file) as db: with sqlite3.connect(self.db_file) as db:
db.row_factory = sqlite3.Row db.row_factory = sqlite3.Row
cursor = db.execute( cursor = db.execute(
'SELECT "id", "data" FROM "history" WHERE "type" = ? ORDER BY "created_at" ASC', f'SELECT "id", "data" FROM "history" WHERE "type" = ? ORDER BY "created_at" ASC',
(self.type,) (self.type,)
) )

View file

@ -22,6 +22,7 @@ class Download:
ytdl_opts: dict = None ytdl_opts: dict = None
info: ItemDTO = None info: ItemDTO = None
default_ytdl_opts: dict = None default_ytdl_opts: dict = None
debug: bool = False
_ytdlp_fields: tuple = ( _ytdlp_fields: tuple = (
'tmpfilename', 'tmpfilename',
@ -41,16 +42,19 @@ class Download:
download_dir: str, download_dir: str,
temp_dir: str, temp_dir: str,
output_template_chapter: str, output_template_chapter: str,
default_ytdl_opts: dict default_ytdl_opts: dict,
debug: bool = False
): ):
self.download_dir = download_dir self.download_dir = download_dir
self.temp_dir = temp_dir self.temp_dir = temp_dir
self.output_template_chapter = output_template_chapter self.output_template_chapter = output_template_chapter
self.output_template = info.output_template self.output_template = info.output_template
self.format = get_format(info.format, info.quality) self.format = get_format(info.format, info.quality)
self.ytdl_opts = get_opts(info.format, info.quality, info.ytdlp_config if info.ytdlp_config else {}) self.ytdl_opts = get_opts(
info.format, info.quality, info.ytdlp_config if info.ytdlp_config else {})
self.info = info self.info = info
self.default_ytdl_opts = default_ytdl_opts self.default_ytdl_opts = default_ytdl_opts
self.debug = debug
self.canceled = False self.canceled = False
self.tmpfilename = None self.tmpfilename = None
@ -82,7 +86,6 @@ class Download:
) )
params: dict = { params: dict = {
'quiet': True,
'no_color': True, 'no_color': True,
'format': self.format, 'format': self.format,
'paths': { 'paths': {
@ -99,6 +102,12 @@ class Download:
**mergeConfig(self.default_ytdl_opts, self.ytdl_opts), **mergeConfig(self.default_ytdl_opts, self.ytdl_opts),
} }
if self.debug:
params['verbose'] = True
params['logger'] = logging.getLogger('YTPTube-ytdl')
else:
params['quiet'] = True
if self.info.ytdlp_cookies: if self.info.ytdlp_cookies:
try: try:
data = jsonCookie(json.loads(self.info.ytdlp_cookies)) data = jsonCookie(json.loads(self.info.ytdlp_cookies))
@ -113,6 +122,8 @@ class Download:
logging.error( logging.error(
f'Invalid cookies: was provided for {self.info.title} - {str(e)}') f'Invalid cookies: was provided for {self.info.title} - {str(e)}')
logging.debug(
f'Downloading {self.info._id} {self.info.title}... {params}')
ret = yt_dlp.YoutubeDL(params=params).download([self.info.url]) ret = yt_dlp.YoutubeDL(params=params).download([self.info.url])
self.status_queue.put( self.status_queue.put(

View file

@ -8,7 +8,7 @@ from src.Notifier import Notifier
from src.Download import Download from src.Download import Download
from src.DTO.ItemDTO import ItemDTO from src.DTO.ItemDTO import ItemDTO
from src.DataStore import DataStore from src.DataStore import DataStore
from src.Utils import ObjectSerializer, calcDownloadPath, ExtractInfo from src.Utils import ObjectSerializer, calcDownloadPath, ExtractInfo, mergeConfig
from datetime import datetime, timezone from datetime import datetime, timezone
log = logging.getLogger('DownloadQueue') log = logging.getLogger('DownloadQueue')
@ -129,6 +129,7 @@ class DownloadQueue:
temp_dir=self.config.temp_path, temp_dir=self.config.temp_path,
output_template_chapter=output_chapter, output_template_chapter=output_chapter,
default_ytdl_opts=self.config.ytdl_options, default_ytdl_opts=self.config.ytdl_options,
debug=bool(self.config.ytdl_debug)
) )
) )
@ -163,6 +164,8 @@ class DownloadQueue:
output_template: str = '', output_template: str = '',
already=None already=None
): ):
ytdlp_config = ytdlp_config if ytdlp_config else {}
log.info( log.info(
f'adding {url}: {quality=} {format=} {folder=} {output_template=} {ytdlp_cookies=} {ytdlp_config=}') f'adding {url}: {quality=} {format=} {folder=} {output_template=} {ytdlp_cookies=} {ytdlp_config=}')
@ -173,7 +176,19 @@ class DownloadQueue:
else: else:
already.add(url) already.add(url)
try: try:
entry = await asyncio.get_running_loop().run_in_executor(None, ExtractInfo, self.config.ytdl_options, url) entry = await asyncio.get_running_loop().run_in_executor(
None,
ExtractInfo,
mergeConfig(self.config.ytdl_options, ytdlp_config),
url,
bool(self.config.ytdl_debug)
)
if not entry:
return {
'status': 'error',
'msg': 'No metadata, most likely video has been downloaded before.' if self.config.keep_archive else 'Unable to extract info check logs.'
}
logging.debug(f'entry: extract info says: {entry}')
except yt_dlp.utils.YoutubeDLError as exc: except yt_dlp.utils.YoutubeDLError as exc:
return {'status': 'error', 'msg': str(exc)} return {'status': 'error', 'msg': str(exc)}

View file

@ -165,9 +165,8 @@ def calcDownloadPath(basePath: str, folder: str = None) -> str:
return download_path return download_path
def ExtractInfo(config: dict, url: str) -> dict: def ExtractInfo(config: dict, url: str, debug: bool = False) -> dict:
params: dict = { params: dict = {
'quiet': True,
'no_color': True, 'no_color': True,
'extract_flat': True, 'extract_flat': True,
'skip_download': True, 'skip_download': True,
@ -176,6 +175,12 @@ def ExtractInfo(config: dict, url: str) -> dict:
**config, **config,
} }
if debug:
params['verbose'] = True
params['logger'] = logging.getLogger('YTPTube-ytdl')
else:
params['quiet'] = True
return yt_dlp.YoutubeDL(params=params).extract_info(url, download=False) return yt_dlp.YoutubeDL(params=params).extract_info(url, download=False)

View file

@ -1,16 +1,17 @@
{ {
"name": "frontend", "name": "YTPTube",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "frontend", "name": "YTPTube",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.6.1",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"moment": "^2.29.4",
"socket.io-client": "^4.7.2", "socket.io-client": "^4.7.2",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-toastification": "^2.0.0-rc.5" "vue-toastification": "^2.0.0-rc.5"
@ -8021,6 +8022,14 @@
"integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==",
"dev": true "dev": true
}, },
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",

View file

@ -12,6 +12,7 @@
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.6.1",
"bulma": "^0.9.4", "bulma": "^0.9.4",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"moment": "^2.29.4",
"socket.io-client": "^4.7.2", "socket.io-client": "^4.7.2",
"vue": "^3.2.13", "vue": "^3.2.13",
"vue-toastification": "^2.0.0-rc.5" "vue-toastification": "^2.0.0-rc.5"

View file

@ -156,12 +156,13 @@ const selectedQuality = useStorage('selectedQuality', '')
const ytdlpConfig = useStorage('ytdlp_config', '') const ytdlpConfig = useStorage('ytdlp_config', '')
const ytdlpCookies = useStorage('ytdlp_cookies', '') const ytdlpCookies = useStorage('ytdlp_cookies', '')
const output_template = useStorage('output_template', null) const output_template = useStorage('output_template', null)
const qualities = ref([]) const downloadPath = useStorage('downloadPath', null)
const url = ref('') const url = useStorage('downloadUrl', null)
const downloadPath = ref('')
const addInProgress = ref(false)
const showAdvanced = useStorage('show_advanced', false) const showAdvanced = useStorage('show_advanced', false)
const qualities = ref([])
const addInProgress = ref(false)
const updateQualities = () => { const updateQualities = () => {
for (const key in downloadFormats) { for (const key in downloadFormats) {
const item = downloadFormats[key]; const item = downloadFormats[key];
@ -190,7 +191,7 @@ const addDownload = () => {
url: url.value, url: url.value,
format: selectedFormat.value, format: selectedFormat.value,
quality: selectedQuality.value, quality: selectedQuality.value,
path: downloadPath.value, folder: downloadPath.value,
ytdlp_config: ytdlpConfig.value, ytdlp_config: ytdlpConfig.value,
ytdlp_cookies: ytdlpCookies.value, ytdlp_cookies: ytdlpCookies.value,
output_template: output_template.value, output_template: output_template.value,
@ -206,6 +207,8 @@ const resetStorage = () => {
ytdlpConfig.value = ''; ytdlpConfig.value = '';
ytdlpCookies.value = ''; ytdlpCookies.value = '';
output_template.value = null; output_template.value = null;
url.value = '';
downloadPath.value = '';
} }
bus.on((event, data) => { bus.on((event, data) => {

View file

@ -74,7 +74,7 @@
<div class="column is-12" v-if="item.error"> <div class="column is-12" v-if="item.error">
<span class="has-text-danger">{{ item.error }}</span> <span class="has-text-danger">{{ item.error }}</span>
</div> </div>
<div class="column is-half has-text-centered"> <div class="column is-4 has-text-centered">
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">
<i v-if="item.status == 'finished'" class="has-text-success fa-solid fa-circle-check"></i> <i v-if="item.status == 'finished'" class="has-text-success fa-solid fa-circle-check"></i>
@ -83,7 +83,12 @@
<span>{{ capitalize(item.status) }}</span> <span>{{ capitalize(item.status) }}</span>
</span> </span>
</div> </div>
<div class="column is-half has-text-centered"> <div class="column is-4 has-text-centered">
<span :data-tooltip="moment(item.timestamp / 1000000).format('MMMM Do YYYY, h:mm:ss a')">
{{ moment(item.timestamp / 1000000).fromNow() }}
</span>
</div>
<div class="column is-4 has-text-centered">
<label class="checkbox is-block"> <label class="checkbox is-block">
<input class="completed-checkbox" type="checkbox" v-model="selectedElms" :id="'checkbox-' + item._id" <input class="completed-checkbox" type="checkbox" v-model="selectedElms" :id="'checkbox-' + item._id"
:value="item._id"> :value="item._id">
@ -159,6 +164,7 @@
<script setup> <script setup>
import { defineProps, computed, ref, watch, defineEmits } from 'vue'; import { defineProps, computed, ref, watch, defineEmits } from 'vue';
import moment from "moment";
const emits = defineEmits(['deleteItem', 'addItem']); const emits = defineEmits(['deleteItem', 'addItem']);
@ -287,7 +293,7 @@ const reQueueItem = (id, item) => {
url: item.url, url: item.url,
format: item.format, format: item.format,
quality: item.quality, quality: item.quality,
path: item.folder, folder: item.folder,
ytdlp_config: item.ytdlp_config, ytdlp_config: item.ytdlp_config,
ytdlp_cookies: item.ytdlp_cookies, ytdlp_cookies: item.ytdlp_cookies,
output_template: item.output_template, output_template: item.output_template,

View file

@ -43,7 +43,7 @@
:value="item.percent ? percentPipe(item.percent) : ''" max="100"> :value="item.percent ? percentPipe(item.percent) : ''" max="100">
</progress> </progress>
</div> </div>
<div class="column is-half has-text-centered"> <div class="column is-4 has-text-centered">
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">
<i v-if="item.status == 'finished'" class="has-text-success fa-solid fa-circle-check"></i> <i v-if="item.status == 'finished'" class="has-text-success fa-solid fa-circle-check"></i>
@ -52,7 +52,12 @@
<span>{{ capitalize(item.status) }}</span> <span>{{ capitalize(item.status) }}</span>
</span> </span>
</div> </div>
<div class="column is-half has-text-centered"> <div class="column is-4 has-text-centered">
<span :data-tooltip="moment(item.timestamp / 1000000).format('MMMM Do YYYY, h:mm:ss a')">
{{ moment(item.timestamp / 1000000).fromNow() }}
</span>
</div>
<div class="column is-4 has-text-centered">
<label class="checkbox is-block"> <label class="checkbox is-block">
<input class="completed-checkbox" type="checkbox" v-model="selectedElms" :value="item._id"> <input class="completed-checkbox" type="checkbox" v-model="selectedElms" :value="item._id">
Select Select
@ -61,8 +66,7 @@
</div> </div>
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<a class="button is-danger is-fullwidth" <a class="button is-danger is-fullwidth" @click="$emit('deleteItem', 'queue', item._id)">
@click="$emit('deleteItem', 'queue', item._id)">
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">
<i class="fa-solid fa-trash-can"></i> <i class="fa-solid fa-trash-can"></i>
@ -107,6 +111,7 @@
<script setup> <script setup>
import { defineProps, defineEmits, ref, watch, computed } from 'vue'; import { defineProps, defineEmits, ref, watch, computed } from 'vue';
import moment from "moment";
defineEmits(['deleteItem']); defineEmits(['deleteItem']);

View file

@ -21,6 +21,7 @@ app.config.globalProperties.makeDownload = (config, item) => {
app.use(Toast, { app.use(Toast, {
transition: "Vue-Toastification__bounce", transition: "Vue-Toastification__bounce",
position: "bottom-right",
maxToasts: 5, maxToasts: 5,
newestOnTop: true newestOnTop: true
}); });