-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Presets
- Loading branch information
Showing
6 changed files
with
374 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,50 @@ | ||
import logging, params, youtube_dl | ||
import logging, ydl_utils, params | ||
from urllib.parse import urlparse, unquote | ||
from fastapi import BackgroundTasks, FastAPI, Response | ||
|
||
app = FastAPI() | ||
|
||
# used to define if the url is a video, un playlist or a video in a playlist | ||
def define_properties(url): | ||
properties = {"playlist" : False, "video" : False} # set at the beginning in case params.playlist_detection is empty | ||
@app.get(params.api_route) | ||
async def download_request(response : Response, background_tasks : BackgroundTasks, url, | ||
format = None, subtitles = None, location = None, filename = None, presets = None): | ||
|
||
for entry in params.playlist_detection: | ||
properties = {"playlist" : False, "video" : False} # reset evety loop | ||
|
||
for indicator in entry['video_indicators']: | ||
properties['video'] = True if url.find(indicator) != -1 else properties['video'] | ||
|
||
for indicator in entry['playlist_indicators']: | ||
properties['playlist'] = True if url.find(indicator) != -1 else properties['playlist'] | ||
|
||
return properties | ||
|
||
def must_be_checked(url, no_playlist = params.no_playlist): | ||
properties = define_properties(url) | ||
is_a_playlist = properties['playlist'] | ||
is_a_video = properties['video'] | ||
|
||
# To avoid failing a test for ONE video impossible to download in the entire playlist | ||
if is_a_video and ((not is_a_playlist) or (is_a_playlist and no_playlist)) : | ||
return True | ||
elif is_a_playlist and ((not is_a_video) or (is_a_video and not no_playlist)): | ||
return False | ||
else: #In other cases : checking | ||
return True | ||
|
||
### Verify if youtube-dl can find video and the format is right | ||
def check_download(url, format): | ||
ydl_opts = { # the option ignoreerrors breaks the function but it can be a problem while downloading playlists with unavailable videos inside | ||
'quiet': True, | ||
'simulate': True, | ||
'format': format, | ||
'noplaylist': params.no_playlist | ||
} | ||
|
||
with youtube_dl.YoutubeDL(ydl_opts) as ydl: | ||
try: | ||
if must_be_checked(url): | ||
logging.info("Checking download") | ||
ydl.download([url]) | ||
return {'checked' : True, 'errors' : False} | ||
else: | ||
logging.warning("Unable to check download") | ||
return {'checked' : False, 'errors' : False} | ||
except: | ||
return {'checked' : True, 'errors' : True} | ||
|
||
### Launch the download instruction | ||
def launch_download(url, ydl_opts): | ||
logging.info(f"Downloading '{url}' with quality '{ydl_opts['format']}' in '{ydl_opts['outtmpl']}'") | ||
|
||
with youtube_dl.YoutubeDL(ydl_opts) as ydl: | ||
ydl.download([url]) | ||
|
||
@app.get("/download") | ||
async def create_download(response : Response, background_tasks : BackgroundTasks, url: str, | ||
format: str = params.default_format, subtitles : str = params.default_subtitles_languages, location: str = "default"): | ||
decoded_url = unquote(url) | ||
decoded_format = unquote(format) | ||
decoded_subtitles = subtitles.split(',') if subtitles is not None else None | ||
|
||
# used to pass useful vars for naming purpose | ||
ydl_api_opts = { | ||
'url': decoded_url, | ||
'hostname' : urlparse(decoded_url).hostname, | ||
'location_identifier' : location | ||
} | ||
|
||
download_dir = params.download_dir(ydl_api_opts) | ||
|
||
ydl_opts = { | ||
'quiet': True, | ||
'ignoreerrors' : True, | ||
'outtmpl': download_dir + params.file_name_template(ydl_api_opts), | ||
'format': decoded_format, | ||
'noplaylist': params.no_playlist, | ||
'writesubtitles': subtitles is not None, | ||
'subtitleslangs' : decoded_subtitles, | ||
'subtitlesformat': params.default_subtitles_format | ||
decoded_presets = [] # from string to list | ||
selected_presets_objects = [] # store presets objects required by the presets field | ||
|
||
if presets is not None: | ||
decoded_presets = presets.split(',') if presets is not None else None | ||
selected_presets_objects = ydl_utils.existing_presets(decoded_presets) # transform string in object | ||
|
||
query_parameters = { # parameters object build form url query parameters | ||
'format' : unquote(format) if format is not None else None, | ||
'subtitles' : unquote(subtitles) if subtitles is not None else None, | ||
'location' : unquote(location) if location is not None else None, | ||
'filename' : unquote(filename) if filename is not None else None, | ||
'presets' : unquote(presets) if presets is not None else None | ||
} | ||
|
||
checked_download = check_download(url, decoded_format) | ||
|
||
if checked_download['checked'] is False: | ||
background_tasks.add_task(launch_download, decoded_url, ydl_opts) | ||
response.status_code = 202 | ||
elif checked_download['checked'] and checked_download['errors'] is False: | ||
background_tasks.add_task(launch_download, decoded_url, ydl_opts) | ||
response.status_code = 200 | ||
# generate all options sets for all download | ||
downloads_options_sets = ydl_utils.generate_ydl_options_sets(decoded_url, selected_presets_objects, query_parameters) | ||
|
||
# count the number of check downloads and the number of errors | ||
validity_check = ydl_utils.recap_all_downloads_validity(downloads_options_sets) | ||
|
||
# if all downloads were checked and without errors, we can ensure the file will be correctly downloaded | ||
if validity_check.get('checked') == validity_check.get('total') and validity_check.get('errors') == 0: | ||
background_tasks.add_task(ydl_utils.launch_downloads, decoded_url, downloads_options_sets) | ||
response.status_code = 200 # request ok | ||
# if not all downloads were checked, we can't ensure all files will be correctly downloaded | ||
elif validity_check.get('checked') != validity_check.get('total'): | ||
background_tasks.add_task(ydl_utils.launch_downloads, decoded_url, downloads_options_sets) | ||
response.status_code = 202 # request ok but result not granted | ||
# if all downloads are in error, we can ensure no file will be downloaded | ||
else: | ||
logging.error(f"Impossible to download '{decoded_url}'") | ||
response.status_code = 400 | ||
response.status_code = 400 # bad request | ||
|
||
return {'url' : decoded_url, 'format': decoded_format, 'download_dir' : download_dir, 'status' : response.status_code, 'subtitles' : decoded_subtitles, 'checked_download' : checked_download} | ||
return { | ||
'url' : decoded_url, | ||
'presets_errors' : (len(decoded_presets) - len(selected_presets_objects)), | ||
'list' : downloads_options_sets | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,35 @@ | ||
import logging | ||
from datetime import date | ||
api_route = "/download" | ||
|
||
### Change here the default format to use : https://github.com/ytdl-org/youtube-dl/tree/3e4cedf9e8cd3157df2457df7274d0c842421945#format-selection | ||
default_format="bestvideo+bestaudio/best" | ||
default_download_format= "bestvideo+bestaudio/best" #https://github.com/ytdl-org/youtube-dl/tree/3e4cedf9e8cd3157df2457df7274d0c842421945#format-selection | ||
|
||
### Example : "en,fr", None = don't download subtitles | ||
default_subtitles_languages = None | ||
|
||
### if not possible, it will download any available format | ||
default_subtitles_languages = None #example : "en,fr" | ||
default_subtitles_format = "srt" | ||
|
||
### equivalent of --no-playlist option : if the playlist is in the url True = download only the current video, False = download the whole playlist | ||
no_playlist = True | ||
|
||
### if present in url, the url is a playlist. | ||
playlist_detection = [ | ||
{'video_indicators': ['/watch?'] , 'playlist_indicators' : ['?list=', '&list=']}, #preset for youtube | ||
no_playlist = True #prevent downloading all the playlist when a video url contains playlist id | ||
playlist_detection = [ #used to detect if the url is a video, a playlist or a video in a playlist | ||
{'video_indicators': ['/watch?'] , 'playlist_indicators' : ['?list=', '&list=']} #youtube | ||
] | ||
|
||
### Change here the download directory and the file name : https://github.com/ytdl-org/youtube-dl/tree/3e4cedf9e8cd3157df2457df7274d0c842421945#output-template | ||
# provided vars | ||
# ydl_api_opts = {'url', 'hostname', 'location_identifier' } | ||
def file_name_template(ydl_api_opts): | ||
return "%(title)s_(%(height)s).%(ext)s" | ||
|
||
# Multiple choices with the parameter &location | ||
def download_dir(ydl_api_opts): | ||
locations={ ###- --%--- REPLACE ---%--- here with your different download directorues | ||
'default' : f"{ydl_api_opts.get('hostname')}/", | ||
#'date' : f"{date.today().strftime('%Y_%m_%d')}/" # for example : &location=date | ||
} | ||
|
||
if locations.get(ydl_api_opts.get('location_identifier')) is not None: | ||
location = locations.get(ydl_api_opts.get('location_identifier')) | ||
else: | ||
logging.warning(f"{ydl_api_opts.get('location_identifier')} identifier not found. Using the default one instead") | ||
location = locations.get('default') | ||
|
||
return location | ||
root_download_directory = "downloads" | ||
# https://github.com/ytdl-org/youtube-dl/tree/3e4cedf9e8cd3157df2457df7274d0c842421945#output-template | ||
# you can use those tags : %hostname%, %location_identifier%, %filename_identifier% | ||
download_directory_templates={ # you must keep a 'default' preset | ||
'default' : f"{root_download_directory}/videos/%hostname%/", | ||
'audio' : f"{root_download_directory}/audio/" | ||
} | ||
|
||
# https://github.com/ytdl-org/youtube-dl/tree/3e4cedf9e8cd3157df2457df7274d0c842421945#output-template | ||
# you can use those tags : %hostname%, %location_identifier%, %filename_identifier% | ||
file_name_templates = { # you must keep a 'default' preset | ||
'default' : "%(title)s_(%(height)s).%(ext)s", | ||
'audio' : "%(title)s.%(ext)s", | ||
} | ||
|
||
presets_templates={ # you must keep a 'default' preset | ||
'default' : {'format' : default_download_format, 'subtitles' : default_subtitles_languages, 'location' : 'default', 'filename' : 'default'}, | ||
'audio': {'format' : 'bestaudio', 'subtitles' : default_subtitles_languages, 'location' : 'audio', 'filename' : 'audio'}, | ||
'best' : {'format' : 'bestvideo+bestaudio/best', 'subtitles' : default_subtitles_languages, 'location' : 'default', 'filename' : 'default'}, | ||
'fullhd' : {'format' : 'best[height=1080]/best', 'subtitles' : default_subtitles_languages, 'location' : 'default', 'filename' : 'default'}, | ||
'hd' : {'format' : 'best[height=720]/best', 'subtitles' : default_subtitles_languages, 'location' : 'default', 'filename' : 'default'}, | ||
'sd' : {'format' : 'best[height=360]/best', 'subtitles' : default_subtitles_languages, 'location' : 'default', 'filename' : 'default'}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.