Alot of misc changes and fixes. Titles with a forward slash are now being corrected for insert in db.
This commit is contained in:
@@ -5,13 +5,12 @@ YouTube Media Library Server aims to make downloading and updating media librari
|
|||||||
- Download of channels, playlists and videos in mp3 or mp4
|
- Download of channels, playlists and videos in mp3 or mp4
|
||||||
- Download of processed files either individually or entire channels and playlists as a .zip
|
- Download of processed files either individually or entire channels and playlists as a .zip
|
||||||
- Update of channels and playlists from the library
|
- Update of channels and playlists from the library
|
||||||
|
- Watching videos via an embed
|
||||||
|
|
||||||
|
|
||||||
# currently not supported
|
# currently not supported
|
||||||
Having the same video in mp3 _and_ mp4 is not possible with how the download process and database work. This includes channels and playlists. The chosen type on initial download dictates what you have.
|
Having the same video in mp3 _and_ mp4 is not possible with how the download process and database work. This includes channels and playlists. The chosen type on initial download dictates what you have.
|
||||||
|
|
||||||
Deleting files will be added soon:tm:.
|
|
||||||
|
|
||||||
|
|
||||||
# media sources
|
# media sources
|
||||||
Currently supported is YouTube but its possible expand since the download itself is handled by yt-dlp.
|
Currently supported is YouTube but its possible expand since the download itself is handled by yt-dlp.
|
||||||
|
|||||||
47
backend.py
47
backend.py
@@ -82,7 +82,10 @@ def process_download(url, ext, parent, query, current_thread):
|
|||||||
try:
|
try:
|
||||||
# this throws KeyError when downloading single file
|
# this throws KeyError when downloading single file
|
||||||
for video in query['entries']:
|
for video in query['entries']:
|
||||||
if video is not None and check_already_exists(video['id']):
|
if video is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if check_already_exists(video['id']):
|
||||||
add_new_video_to_collection(parent, video['id'])
|
add_new_video_to_collection(parent, video['id'])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -91,11 +94,14 @@ def process_download(url, ext, parent, query, current_thread):
|
|||||||
download=False)
|
download=False)
|
||||||
|
|
||||||
# replace url with name of playlist
|
# replace url with name of playlist
|
||||||
queued_downloads[0][0] = parent
|
queued_downloads[0][0] = parent.replace('/', '⧸')
|
||||||
|
|
||||||
|
if max_video_length and video['duration'] > max_video_length:
|
||||||
|
continue
|
||||||
|
|
||||||
# add new entry to file_cache
|
# add new entry to file_cache
|
||||||
ids.append(video['id'])
|
ids.append(video['id'])
|
||||||
titles.append(video['title'])
|
titles.append(video['title'].replace('/', '⧸'))
|
||||||
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
||||||
|
|
||||||
# start download
|
# start download
|
||||||
@@ -113,18 +119,24 @@ def process_download(url, ext, parent, query, current_thread):
|
|||||||
for tab in query['entries']:
|
for tab in query['entries']:
|
||||||
# for every video in their respective tabs
|
# for every video in their respective tabs
|
||||||
for video in tab['entries']:
|
for video in tab['entries']:
|
||||||
if video is not None and check_already_exists(video['id']):
|
if video is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if check_already_exists(video['id']):
|
||||||
add_new_video_to_collection(parent, video['id'])
|
add_new_video_to_collection(parent, video['id'])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# replace url with name of channel
|
# replace url with name of channel
|
||||||
queued_downloads[0][0] = parent
|
queued_downloads[0][0] = parent.replace('/', '⧸')
|
||||||
|
|
||||||
|
if max_video_length and video['duration'] > max_video_length:
|
||||||
|
continue
|
||||||
|
|
||||||
# there have been cases of duplicate urls or some with '/watch?v=@channel_name'
|
# there have been cases of duplicate urls or some with '/watch?v=@channel_name'
|
||||||
# but no consistency has been observed
|
# but no consistency has been observed
|
||||||
# still works though so will not be checked for now
|
# still works though so will not be checked for now
|
||||||
ids.append(video['id'])
|
ids.append(video['id'])
|
||||||
titles.append(video['title'])
|
titles.append(video['title'].replace('/', '⧸'))
|
||||||
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
urls.append('https://www.youtube.com/watch?v=' + video['id'])
|
||||||
|
|
||||||
# start download
|
# start download
|
||||||
@@ -141,11 +153,11 @@ def process_download(url, ext, parent, query, current_thread):
|
|||||||
# when downloading single files that already exist, there's no need for adjustments in db
|
# when downloading single files that already exist, there's no need for adjustments in db
|
||||||
if not check_already_exists(query['id']):
|
if not check_already_exists(query['id']):
|
||||||
ids.append(query['id'])
|
ids.append(query['id'])
|
||||||
titles.append(query['title'])
|
titles.append(query['title'].replace('/', '⧸'))
|
||||||
urls.append('https://www.youtube.com/watch?v=' + query['id'])
|
urls.append('https://www.youtube.com/watch?v=' + query['id'])
|
||||||
|
|
||||||
# replace url with name of video
|
# replace url with name of video
|
||||||
queued_downloads[0][0] = query['title']
|
queued_downloads[0][0] = query['title'].replace('/', '⧸')
|
||||||
|
|
||||||
# start download
|
# start download
|
||||||
download_all(url, ext=ext)
|
download_all(url, ext=ext)
|
||||||
@@ -167,6 +179,9 @@ def process_update(parent, query, current_thread):
|
|||||||
try:
|
try:
|
||||||
# this throws KeyError when downloading single file
|
# this throws KeyError when downloading single file
|
||||||
for video in query['entries']:
|
for video in query['entries']:
|
||||||
|
if video is None:
|
||||||
|
continue
|
||||||
|
|
||||||
if check_already_exists(video['id']):
|
if check_already_exists(video['id']):
|
||||||
add_new_video_to_collection(parent, video['id'])
|
add_new_video_to_collection(parent, video['id'])
|
||||||
continue
|
continue
|
||||||
@@ -175,6 +190,9 @@ def process_update(parent, query, current_thread):
|
|||||||
ydl.YoutubeDL({'quiet': True}).extract_info('https://www.youtube.com/watch?v=' + video['id'],
|
ydl.YoutubeDL({'quiet': True}).extract_info('https://www.youtube.com/watch?v=' + video['id'],
|
||||||
download=False)
|
download=False)
|
||||||
|
|
||||||
|
if max_video_length and video['duration'] > max_video_length:
|
||||||
|
continue
|
||||||
|
|
||||||
# add new entry to file_cache
|
# add new entry to file_cache
|
||||||
ids.append(video['id'])
|
ids.append(video['id'])
|
||||||
titles.append(video['title'])
|
titles.append(video['title'])
|
||||||
@@ -195,10 +213,16 @@ def process_update(parent, query, current_thread):
|
|||||||
for tab in query['entries']:
|
for tab in query['entries']:
|
||||||
# for every video in their respective tabs
|
# for every video in their respective tabs
|
||||||
for video in tab['entries']:
|
for video in tab['entries']:
|
||||||
|
if video is None:
|
||||||
|
continue
|
||||||
|
|
||||||
if check_already_exists(video['id']):
|
if check_already_exists(video['id']):
|
||||||
add_new_video_to_collection(parent, video['id'])
|
add_new_video_to_collection(parent, video['id'])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if max_video_length and video['duration'] > max_video_length:
|
||||||
|
continue
|
||||||
|
|
||||||
# there have been cases of duplicate urls or some with '/watch?v=@channel_name'
|
# there have been cases of duplicate urls or some with '/watch?v=@channel_name'
|
||||||
# but no consistency has been observed
|
# but no consistency has been observed
|
||||||
# still works though so will not be checked for now
|
# still works though so will not be checked for now
|
||||||
@@ -339,7 +363,7 @@ def yt_download(location, ext='mp3'):
|
|||||||
# if videos are wanted, adjust the options
|
# if videos are wanted, adjust the options
|
||||||
if ext == 'mp4':
|
if ext == 'mp4':
|
||||||
opts['format'] = 'bestvideo[height<=1080]+bestaudio/best[height<=1080]'
|
opts['format'] = 'bestvideo[height<=1080]+bestaudio/best[height<=1080]'
|
||||||
opts.pop('format')
|
opts['merge_output_format'] = 'mp4'
|
||||||
opts.pop('postprocessors')
|
opts.pop('postprocessors')
|
||||||
|
|
||||||
# try to download all new files
|
# try to download all new files
|
||||||
@@ -455,3 +479,8 @@ def delete_file_or_playlist(file_name):
|
|||||||
rmtree(downloads_path() + folder)
|
rmtree(downloads_path() + folder)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# checks if file is somewhere in downloads directory and returns true if so
|
||||||
|
def check_file_path(path):
|
||||||
|
downloads = downloads_path()
|
||||||
|
return downloads in os.path.abspath(downloads + path)
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ titles = []
|
|||||||
urls = []
|
urls = []
|
||||||
thread_queue = []
|
thread_queue = []
|
||||||
queued_downloads = []
|
queued_downloads = []
|
||||||
|
max_video_length = 600
|
||||||
|
|||||||
26
frontend.py
26
frontend.py
@@ -1,5 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
Blueprint,
|
||||||
request,
|
request,
|
||||||
@@ -14,7 +16,8 @@ from backend import (
|
|||||||
zip_folder_not_in_directory,
|
zip_folder_not_in_directory,
|
||||||
enqueue_download,
|
enqueue_download,
|
||||||
internet_available,
|
internet_available,
|
||||||
delete_file_or_playlist
|
delete_file_or_playlist,
|
||||||
|
check_file_path
|
||||||
)
|
)
|
||||||
from forms.download import DownloadForm
|
from forms.download import DownloadForm
|
||||||
from db_tools import query_db
|
from db_tools import query_db
|
||||||
@@ -50,7 +53,7 @@ def downloader():
|
|||||||
# if there has been a problem with the form (empty or error) or the link is not valid
|
# if there has been a problem with the form (empty or error) or the link is not valid
|
||||||
if not form.validate_on_submit() or not valid_link:
|
if not form.validate_on_submit() or not valid_link:
|
||||||
valid_link = True if url == 'None' else False # if url is empty, don't show error
|
valid_link = True if url == 'None' else False # if url is empty, don't show error
|
||||||
return render_template('downloader.html', form=form, ytLink=valid_link, amount=len(urls))
|
return render_template('downloader.html', form=form, amount=len(urls))
|
||||||
|
|
||||||
if not internet_available():
|
if not internet_available():
|
||||||
flash('No internet connection available.', 'danger')
|
flash('No internet connection available.', 'danger')
|
||||||
@@ -113,7 +116,6 @@ def download():
|
|||||||
# else a directory is requested
|
# else a directory is requested
|
||||||
else:
|
else:
|
||||||
zip_path, zip_name = zip_folder(file_path)
|
zip_path, zip_name = zip_folder(file_path)
|
||||||
print(zip_path, zip_name)
|
|
||||||
zip_folder_not_in_directory(zip_path + zip_name)
|
zip_folder_not_in_directory(zip_path + zip_name)
|
||||||
|
|
||||||
# zip and send
|
# zip and send
|
||||||
@@ -155,23 +157,25 @@ def update():
|
|||||||
# todo: add functionality to library
|
# todo: add functionality to library
|
||||||
@frontend.route('/player', methods=['GET'])
|
@frontend.route('/player', methods=['GET'])
|
||||||
def player():
|
def player():
|
||||||
return render_template('video-player.html')
|
return render_template('player.html', file=request.args.get('file'))
|
||||||
|
|
||||||
|
|
||||||
@frontend.route('/serve', methods=['GET'])
|
@frontend.route('/serve', methods=['GET'])
|
||||||
def serve():
|
def serve():
|
||||||
file = request.args.get('file', None)
|
file = request.args.get('file').replace('/', '').replace('\\', '')
|
||||||
if 'audio' in file:
|
if not check_file_path(file):
|
||||||
file = r'downloads\\CptCatman\\my vital organs all lift together.mp3'
|
flash('Video not found', 'danger')
|
||||||
|
return render_template('flash-message.html')
|
||||||
|
|
||||||
|
if 'mp3' in file:
|
||||||
return send_file(
|
return send_file(
|
||||||
file,
|
downloads_path() + file,
|
||||||
'audio/mpeg',
|
'audio/mpeg',
|
||||||
True
|
True
|
||||||
)
|
)
|
||||||
else:
|
elif 'mp4' in file:
|
||||||
file = r'downloads\\Rick Astley - Never Gonna Give You Up (Official Music Video).webm'
|
|
||||||
return send_file(
|
return send_file(
|
||||||
file,
|
downloads_path() + file,
|
||||||
'video/webm',
|
'video/webm',
|
||||||
True
|
True
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from waitress import serve
|
from waitress import serve
|
||||||
from app import create_app
|
from app import create_app
|
||||||
|
import file_cache
|
||||||
import os
|
import os
|
||||||
|
|
||||||
PORT = 5000
|
PORT = 5000
|
||||||
|
MAX_VIDEO_DOWNLOAD_LENGTH_MINUTES = 10
|
||||||
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
file_cache.max_video_length = MAX_VIDEO_DOWNLOAD_LENGTH_MINUTES * 60
|
||||||
serve(create_app(), port=PORT)
|
serve(create_app(), port=PORT)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for video in videos %}
|
{% for video in videos %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">{{ video['name'] }}</td>
|
<td class="text-center"><a class="btn btn-link" href="/player?file={{ video['path'] + video['name'] + video['ext'] }}">{{ video['name'] }}</a></td>
|
||||||
<td class="text-center"><a class="btn btn-link" href="/download?file={{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
|
<td class="text-center"><a class="btn btn-link" href="/download?file={{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for entry in running_downloads %}
|
{% for entry in running_downloads %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><a href="{{ entry[0] }}" target="_blank">{{ entry[0] }}</a></td>
|
<td class="text-center">{{ entry[0] }}</td>
|
||||||
<td class="text-center">{{ entry[1] }}</td>
|
<td class="text-center">{{ entry[1] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="text-center">Currently processing</th>
|
<th scope="col" class="text-center">New downloads in {{ running_downloads[0][0] }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{% for video in videos %}
|
{% for video in videos %}
|
||||||
{% if 'mp3' in video['ext'] %}
|
{% if 'mp3' in video['ext'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center align-middle">{{ video['name'] }}</td>
|
<td class="text-center align-middle"><a class="btn btn-link" href="/player?file={{ video['path'] + video['name'] + video['ext'] }}">{{ video['name'] }}</a></td>
|
||||||
<td class="text-center align-middle"><a class="btn btn-link" href="/download?file={{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
|
<td class="text-center align-middle"><a class="btn btn-link" href="/download?file={{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
|
||||||
<td class="text-center align-middle"><a class="btn btn-danger" href="/delete?file={{ video['path'] + video['name'] + video['ext'] }}&from=/library"> </a></td>
|
<td class="text-center align-middle"><a class="btn btn-danger" href="/delete?file={{ video['path'] + video['name'] + video['ext'] }}&from=/library"> </a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
21
templates/player.html
Normal file
21
templates/player.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{%- extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{{ super() }}
|
||||||
|
<div class="container-fluid">
|
||||||
|
{% if 'mp4' in file %}
|
||||||
|
<div class="section d-flex justify-content-center embed-responsive embed-responsive-16by9" style="padding: 1.5%">
|
||||||
|
<video class="embed-responsive-item" style="width: 100%; height: auto" controls>
|
||||||
|
<source src="serve?file={{ file }}" type="video/webm">
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if 'mp3' in file %}
|
||||||
|
<div class="section d-flex justify-content-center embed-responsive" style="padding: 1.5%">
|
||||||
|
<audio class="embed-responsive-item" controls>
|
||||||
|
<source src="serve?file={{ file }}" type="audio/mpeg">
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{%- endblock %}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{%- extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{{ super() }}
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div id="trailer" class="section d-flex justify-content-center embed-responsive embed-responsive-16by9">
|
|
||||||
<video class="embed-responsive-item" style="width: 100%; padding: 1.5%; height: auto" controls>
|
|
||||||
<source src="serve?file=video" type="video/webm">
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{%- endblock %}
|
|
||||||
Reference in New Issue
Block a user