Download of Videos is now possible. Minor styling changes.
This commit is contained in:
25
backend.py
25
backend.py
@@ -1,6 +1,5 @@
|
||||
import threading
|
||||
import yt_dlp as ydl
|
||||
from yt_dlp import DownloadError
|
||||
import os
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
@@ -13,15 +12,15 @@ from file_cache import *
|
||||
|
||||
|
||||
# adds thread to queue and starts it
|
||||
def enqueue_download(url, update=False):
|
||||
def enqueue_download(url, update=False, ext='mp3'):
|
||||
# download and processing is happening in background / another thread
|
||||
t = Thread(target=process_general, args=(url,update))
|
||||
t = Thread(target=process_general, args=(url, ext, update))
|
||||
thread_queue.append(t)
|
||||
t.start()
|
||||
return
|
||||
|
||||
|
||||
def process_general(url, update=False):
|
||||
def process_general(url, ext, update=False):
|
||||
# get current time and put in list to be displayed on /index
|
||||
current_time = datetime.now().time()
|
||||
running_downloads.append([url, str(current_time.hour) + ':' + str(current_time.minute)])
|
||||
@@ -43,7 +42,7 @@ def process_general(url, update=False):
|
||||
if update:
|
||||
process_update(parent, query, current_thread)
|
||||
else:
|
||||
process_download(url, parent, query, current_thread)
|
||||
process_download(url, ext, parent, query, current_thread)
|
||||
|
||||
try:
|
||||
running_downloads.pop(0)
|
||||
@@ -54,7 +53,7 @@ def process_general(url, update=False):
|
||||
|
||||
|
||||
# this is the 'controller' for the download process
|
||||
def process_download(url, parent, query, current_thread):
|
||||
def process_download(url, ext, parent, query, current_thread):
|
||||
# one of the three cases does not throw an exception
|
||||
# and therefore gets to download and return
|
||||
# kinda hacky but whatever
|
||||
@@ -76,12 +75,12 @@ def process_download(url, parent, query, current_thread):
|
||||
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
||||
|
||||
# start download
|
||||
download_all(url, parent)
|
||||
download_all(url, ext, parent)
|
||||
thread_queue.remove(current_thread)
|
||||
return
|
||||
|
||||
# when downloading: channel: DownloadError, single file: KeyError
|
||||
except (DownloadError, KeyError):
|
||||
except (ydl.DownloadError, KeyError):
|
||||
pass
|
||||
|
||||
# if downloading channel
|
||||
@@ -102,7 +101,7 @@ def process_download(url, parent, query, current_thread):
|
||||
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
||||
|
||||
# start download
|
||||
download_all(url, parent)
|
||||
download_all(url, ext, parent)
|
||||
thread_queue.remove(current_thread)
|
||||
return
|
||||
|
||||
@@ -119,7 +118,7 @@ def process_download(url, parent, query, current_thread):
|
||||
urls.append('https://www.youtube.com/watch?v=' + query['id'])
|
||||
|
||||
# start download
|
||||
download_all(url)
|
||||
download_all(url, ext=ext)
|
||||
|
||||
# this is broad on purpose; there has been no exception thrown here _yet_
|
||||
except Exception as e:
|
||||
@@ -156,7 +155,7 @@ def process_update(parent, query, current_thread):
|
||||
return
|
||||
|
||||
# when downloading: channel: DownloadError, single file: KeyError
|
||||
except (DownloadError, KeyError):
|
||||
except (ydl.DownloadError, KeyError):
|
||||
pass
|
||||
|
||||
# if downloading channel
|
||||
@@ -196,7 +195,7 @@ def check_already_exists(video_id) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def download_all(url, parent=None, ext='mp3'):
|
||||
def download_all(url, ext, parent=None):
|
||||
# if no new files to download, there's nothing to do here
|
||||
if not len(urls) > 0: return
|
||||
|
||||
@@ -311,7 +310,7 @@ def yt_download(location, ext='mp3'):
|
||||
# try to download all new files
|
||||
try:
|
||||
ydl.YoutubeDL(opts).download(urls)
|
||||
except DownloadError:
|
||||
except ydl.DownloadError:
|
||||
pass
|
||||
|
||||
return
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import URLField, SubmitField
|
||||
from wtforms.validators import URL, DataRequired
|
||||
from wtforms import URLField, SelectField, SubmitField
|
||||
from wtforms.validators import URL, DataRequired, AnyOf
|
||||
|
||||
|
||||
class DownloadForm(FlaskForm):
|
||||
url = URLField('Video, Channel or Playlist', validators=[DataRequired(), URL()], render_kw={'placeholder': 'YouTube Link'})
|
||||
ext = SelectField('Type', choices=[('mp3', 'Audio'), ('mp4', 'Video')], validators=[AnyOf(('mp3', 'mp4'))])
|
||||
submit = SubmitField('Go')
|
||||
|
||||
26
frontend.py
26
frontend.py
@@ -1,6 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from flask import Blueprint, request, render_template, flash, send_from_directory, current_app
|
||||
from flask import Blueprint, request, render_template, flash, send_from_directory
|
||||
from flask_nav3 import Nav
|
||||
from flask_nav3.elements import Navbar, View
|
||||
|
||||
@@ -14,10 +14,9 @@ frontend = Blueprint('frontend', __name__)
|
||||
nav = Nav()
|
||||
nav.register_element('frontend_top', Navbar(
|
||||
View('ytm-ls', '.index'),
|
||||
View('downloader', '.downloader'),
|
||||
View('updater', '.updater'),
|
||||
View('library', '.library')
|
||||
)
|
||||
View('Downloader', '.downloader'),
|
||||
View('Library', '.library')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -36,11 +35,12 @@ def downloader():
|
||||
|
||||
# get url out of form
|
||||
url = str(form.url.data)
|
||||
ext = str(form.ext.data)
|
||||
|
||||
# check if valid link
|
||||
# this tool should technically work with other platforms but that is not tested
|
||||
# since KeyErrors are to be expected in backend.process_download(url), it's blocked here
|
||||
# you are invited to test and adjust the code for other platforms and open a merge request
|
||||
# you are invited to test and adjust the code for other platforms
|
||||
valid_link = True if 'youtube.com' in url or 'youtu.be' in url else False
|
||||
|
||||
# if there has been a problem with the form (empty or error) or the link is not valid
|
||||
@@ -49,7 +49,7 @@ def downloader():
|
||||
return render_template('downloader.html', form=form, ytLink=valid_link, amount=len(urls))
|
||||
|
||||
# kick off download process
|
||||
enqueue_download(url)
|
||||
enqueue_download(url, ext=ext)
|
||||
|
||||
# show download start confirmation
|
||||
flash('Download enqueued and will finish in background.')
|
||||
@@ -75,21 +75,13 @@ def download(file_path):
|
||||
)
|
||||
|
||||
|
||||
@frontend.route('/update', methods=['GET', 'POST'])
|
||||
def updater():
|
||||
downloads = query_db('SELECT name, ROWID FROM playlist')
|
||||
if not downloads:
|
||||
flash('Library has no playlists yet. Try downloading some!')
|
||||
return render_template('updater.html', downloads=downloads)
|
||||
|
||||
|
||||
@frontend.route('/update/<int:url_rowid>')
|
||||
@frontend.route('/update/<int:url_rowid>', methods=['GET'])
|
||||
def update(url_rowid):
|
||||
url = query_db('SELECT url FROM playlist WHERE ROWID = :url_rowid',
|
||||
{'url_rowid': url_rowid})[0][0]
|
||||
|
||||
# kick off download process
|
||||
enqueue_download(url, True)
|
||||
enqueue_download(url, update=True)
|
||||
|
||||
# show download start confirmation
|
||||
flash('Update enqueued and will finish in background.')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<div class="container" style="width: 50%">
|
||||
<div class="container-fluid" style="width: fit-content(105%)">
|
||||
<table id="videos" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -14,7 +14,7 @@
|
||||
{% for video in videos %}
|
||||
<tr>
|
||||
<td class="text-center">{{ video['name'] }}</td>
|
||||
<td class="text-center"><a href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
|
||||
<td class="text-center"><a class="btn" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -2,18 +2,15 @@
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<div class="container">
|
||||
<div class="card" style="width: fit-content">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<form method="POST">
|
||||
{{ form.csrf_token }}
|
||||
{{ form.url.label }} <br>
|
||||
{{ form.url }}
|
||||
{{ form.submit }}
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="container-fluid" style="width: fit-content; align-self: flex-start">
|
||||
<div class="table-bordered" style="padding: .5cm">
|
||||
<form method="POST" class="form-group">
|
||||
{{ form.csrf_token }}
|
||||
{{ form.url.label(class_="form-text") }} <br>
|
||||
{{ form.url(class_="form-control") }} <br>
|
||||
{{ form.ext(class_="form-control") }} <br>
|
||||
{{ form.submit(class_="btn primary") }}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<div class="container" style="width: fit-content(105%)">
|
||||
<div class="container-fluid" style="width: fit-content(105%)">
|
||||
{% if playlists %}
|
||||
<div class="card">
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -12,12 +12,14 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center">Playlists</th>
|
||||
<th scope="col" class="text-center">Update</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for playlist in playlists %}
|
||||
<tr>
|
||||
<td class="text-center"> <a href="/library-playlist?playlist={{ playlist['ROWID'] }}">{{ playlist['name'] }}</a></td>
|
||||
<td class="text-center"><a class="btn" href="/library-playlist?playlist={{ playlist['ROWID'] }}">{{ playlist['name'] }}</a></td>
|
||||
<td class="text-center"><a class="btn" href="/update/{{ playlist[1] }}">Start</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -46,7 +48,7 @@
|
||||
{% for video in videos %}
|
||||
<tr>
|
||||
<td class="text-center">{{ video['name'] }}</td>
|
||||
<td class="text-center"><a href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
|
||||
<td class="text-center"><a class="btn" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{%- extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<div class="container" style="width: fit-content(105%)">
|
||||
{% if downloads %}
|
||||
<div class="card">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<table id="videos" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center">Title</th>
|
||||
<th scope="col" class="text-center">Update</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for d in downloads %}
|
||||
<tr>
|
||||
<td class="text-center">{{ d[0] }}</td>
|
||||
<td class="text-center"><a href="/update/{{ d[1] }}">Start</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endblock %}
|
||||
Reference in New Issue
Block a user