Skip to content

Commit a0e1650

Browse files
committed
Merge branch 'unstable'
2 parents d3f3c68 + cd9d902 commit a0e1650

File tree

14 files changed

+139
-41
lines changed

14 files changed

+139
-41
lines changed

.github/workflows/release.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ jobs:
2929
run: python setup.py sdist bdist_wheel
3030
- name: Upload to PyPI
3131
env:
32-
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
33-
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
34-
run: twine upload dist/* --skip-existing
32+
TWINE_USERNAME: "__token__"
33+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
34+
run: twine upload dist/*

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ usage: ytmdl [-h] [-q] [-o OUTPUT_DIR] [--song SONG-METADATA]
169169
[--filename NAME] [--pl-start NUMBER] [--pl-end NUMBER]
170170
[--pl-items ITEM_SPEC] [--ignore-errors] [--title-as-name]
171171
[--level LEVEL] [--disable-file] [--list-level]
172-
[SONG_NAME [SONG_NAME ...]]
172+
[SONG_NAME ...]
173173

174174
positional arguments:
175175
SONG_NAME Name of the song to download. Can be an URL to a
@@ -251,7 +251,8 @@ Metadata:
251251
--on-meta-error ON_META_ERROR
252252
What to do if adding the metadata fails for some
253253
reason like lack of metadata or perhaps a network
254-
issue. Options are ['exit', 'skip', 'manual']
254+
issue. Options are ['exit', 'skip', 'manual',
255+
'youtube']
255256

256257
Playlist:
257258
--pl-start NUMBER Playlist video to start at (default is 1)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
'requests',
1818
'colorama',
1919
'beautifulsoup4',
20-
'downloader-cli',
20+
'downloader-cli>=0.3.4',
2121
'pyxdg',
2222
'ffmpeg-python',
2323
'pysocks',

ytmdl/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# Store the version of the package
2-
__version__ = "2023.07.27"
2+
__version__ = "2023.11.26"

ytmdl/core.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ytmdl.exceptions import (
1717
DownloadError, ConvertError, NoMetaError, MetadataError
1818
)
19+
from ytmdl.meta.yt import extract_meta_from_yt
1920

2021

2122
logger = Logger("core")
@@ -200,7 +201,7 @@ def trim(name: str, args) -> None:
200201
trim.Trim(name)
201202

202203

203-
def meta(conv_name: str, song_name: str, search_by: str, args):
204+
def meta(conv_name: str, song_name: str, search_by: str, link: str, args):
204205
"""Handle adding the metadata for the passed song.
205206
206207
We will use the passed name to search for metadata, ask
@@ -236,14 +237,26 @@ def meta(conv_name: str, song_name: str, search_by: str, args):
236237

237238
# If no meta was found raise error
238239
if not TRACK_INFO:
239-
# Check if we are supposed to add manual meta
240-
if args.on_meta_error != "manual":
240+
# Check if we are supposed to add manual meta or from youtube
241+
if args.on_meta_error not in ["manual", "youtube"]:
241242
raise NoMetaError(search_by)
242-
243-
TRACK_INFO = manual.get_data(song_name)
244-
return TRACK_INFO
243+
244+
if args.on_meta_error == "manual":
245+
TRACK_INFO = manual.get_data(song_name)
246+
elif args.on_meta_error == 'youtube':
247+
# Extract meta from youtube
248+
track_info = extract_meta_from_yt(link)
249+
TRACK_INFO = [track_info]
250+
251+
option = song.setData(TRACK_INFO, IS_QUIET, conv_name, PASSED_FORMAT, 0, skip_showing_choice=True)
252+
if not isinstance(option, int):
253+
raise MetadataError(search_by)
254+
255+
return TRACK_INFO[option]
245256

246257
logger.info('Setting data...')
258+
259+
247260
option = song.setData(TRACK_INFO, IS_QUIET, conv_name, PASSED_FORMAT,
248261
args.choice)
249262

@@ -260,6 +273,6 @@ def meta(conv_name: str, song_name: str, search_by: str, args):
260273
logger.info(
261274
"Amending the search because -2 was entered as the option")
262275
search_by = utility.get_new_meta_search_by(search_by)
263-
return meta(conv_name, song_name, search_by, args)
276+
return meta(conv_name, song_name, search_by, link, args)
264277

265278
return TRACK_INFO[option]

ytmdl/dir.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ def __replace_special_characters(passed_name: str) -> str:
2222
return sub(r'/', '-', passed_name)
2323

2424

25+
def get_abs_path(path_passed: str) -> str:
26+
"""
27+
Get the absolute path by removing special path directives
28+
that `ytmdl` supports.
29+
"""
30+
if "$" not in path_passed:
31+
return path_passed
32+
33+
return path_passed.split("$")[0]
34+
35+
2536
def cleanup(TRACK_INFO, index, datatype, remove_cached=True, filename_passed=None):
2637
"""Move the song from temp to the song dir."""
2738
try:
@@ -42,7 +53,7 @@ def cleanup(TRACK_INFO, index, datatype, remove_cached=True, filename_passed=Non
4253
SONG_NAME = filename_passed + ".{}".format(datatype)
4354

4455
DIR = defaults.DEFAULT.SONG_DIR
45-
logger.debug(DIR)
56+
logger.debug("directory being used: ", DIR)
4657

4758
# Check if DIR has $ in its path
4859
# If it does then make those folders accordingly

ytmdl/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def post_processing(
411411

412412
# Else fill the meta by searching
413413
try:
414-
track_selected = meta(conv_name, song_name, song_metadata, args)
414+
track_selected = meta(conv_name, song_name, song_metadata, link, args)
415415
except NoMetaError as no_meta_error:
416416
if args.on_meta_error == 'skip':
417417
# Write to the archive file
@@ -493,7 +493,7 @@ def pre_checks(args):
493493

494494
# Ensure the output directory is legitimate
495495
if (args.output_dir is not None):
496-
if path.isdir(path.expanduser(args.output_dir)):
496+
if path.isdir(path.expanduser(dir.get_abs_path(args.output_dir))):
497497
defaults.DEFAULT.SONG_DIR = path.expanduser(args.output_dir)
498498
else:
499499
logger.warning(

ytmdl/manual.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,24 @@ class Meta:
2626
track_number : Number of the track in the album
2727
artwork_url_100 : URL of the album cover
2828
"""
29-
def __init__(self):
30-
self.release_date = "{}T00:00:00Z".format(datetime.now().date())
31-
self.track_name = "N/A"
32-
self.artist_name = "N/A"
33-
self.collection_name = "N/A"
34-
self.primary_genre_name = "N/A"
35-
self.track_number = "1"
36-
self.artwork_url_100 = ""
29+
def __init__(
30+
self,
31+
release_date: str = None,
32+
track_name: str = "N/A",
33+
artist_name: str = "N/A",
34+
collection_name: str = "N/A",
35+
primary_genre_name: str = "N/A",
36+
track_number: str = "1",
37+
artwork_url_100: str = ""
38+
):
39+
self.release_date = "{}T00:00:00Z".format(datetime.now().date()) if \
40+
release_date is None else release_date
41+
self.track_name = track_name
42+
self.artist_name = artist_name
43+
self.collection_name = collection_name
44+
self.primary_genre_name = primary_genre_name
45+
self.track_number = track_number
46+
self.artwork_url_100 = artwork_url_100
3747

3848
def _read_individual(self, default_value):
3949
"""

ytmdl/meta/yt.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Handle metadata extraction from youtube
3+
"""
4+
5+
from typing import Dict, List
6+
from datetime import datetime
7+
8+
from yt_dlp import YoutubeDL
9+
from simber import Logger
10+
11+
from ytmdl.manual import Meta
12+
from ytmdl.utils.ytdl import get_ytdl_opts
13+
from ytmdl.exceptions import ExtractError
14+
15+
# Create logger
16+
logger = Logger("meta:yt")
17+
18+
19+
def __parse_release_date_from_utc(timestamp: int) -> str:
20+
if timestamp is None:
21+
return None
22+
23+
dt_object = datetime.utcfromtimestamp(timestamp)
24+
return dt_object.strftime('%Y-%m-%dT%H:%M:%SZ')
25+
26+
def __parse_genre_name_from_categories(categories: List[str]) -> str:
27+
return categories[0] if len(categories) else "N/A"
28+
29+
def __parse_meta_from_details(details: Dict) -> Meta:
30+
"""
31+
Parse the meta object from the passed details
32+
"""
33+
return Meta(
34+
release_date=__parse_release_date_from_utc(details.get("release_timestamp", None)),
35+
track_name=details.get("title", "N/A"),
36+
artist_name=details.get("channel", "N/A"),
37+
primary_genre_name=__parse_genre_name_from_categories(details.get("categories", [])),
38+
artwork_url_100=details.get("thumbnail", "N/A")
39+
)
40+
41+
def extract_meta_from_yt(video_url: str) -> Meta:
42+
"""
43+
Extract the metadata from the passed video ID and return
44+
it accordingly.
45+
"""
46+
ytdl_obj = YoutubeDL(get_ytdl_opts())
47+
48+
try:
49+
details = ytdl_obj.extract_info(video_url, download=False)
50+
return __parse_meta_from_details(details)
51+
except Exception as e:
52+
logger.debug("Got exception while extracting details for video: ", video_url)
53+
logger.warning("Failed to extract metadata from yt with exception: ", str(e))
54+
raise ExtractError(f"error extracting data from yt: {str(e)}")

ytmdl/setupConfig.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def __init__(self):
139139

140140
self.DEFAULT_FORMAT = 'mp3'
141141

142-
self.ON_ERROR_OPTIONS = ['exit', 'skip', 'manual']
142+
self.ON_ERROR_OPTIONS = ['exit', 'skip', 'manual', 'youtube']
143143

144144
self.ON_ERROR_DEFAULT = 'exit'
145145

0 commit comments

Comments
 (0)