Module musar.download
Tools to download YouTube playlists and set the appropriate audio tags.
Expand source code
"""Tools to download YouTube playlists and set the appropriate audio tags.
"""
import io
import os
import re
import json
import glob
import logging
import subprocess
import requests
import shadow_useragent
import PIL.Image
import eyed3
from .misc import most_common_list_value
def download_video(youtube_dl_path, video_url, output_filename):
"""Download a YouTube video using youtube-dl, and extract its audio.
Parameters
----------
youtube_dl_path : str
Path to the youtube-dl executable.
video_url : str
URL of the YouTube video.
output_filename : str
Path for the output MP3 file.
"""
logging.info("Downloading YouTube video %s", video_url)
command = [
youtube_dl_path,
# "--verbose",
"--extract-audio",
"--audio-format",
"mp3",
"--output",
output_filename,
video_url
]
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
process.wait()
class YoutubePlaylistInfo:
"""Wrapper for a YouTube playlist metadata.
Attributes
----------
tracks : Dict[index, str]
Keys are playlist indices and values are video titles.
album : str
Playlist title.
thumbnail_url : str
URL of the playlist's thumbnail.
thumbnail_data : PIL.Image.Image
Playlist's thumbnail.
artist : str
Most common channel accross playlist's videos.
year : str
Year information from user input.
genre : str
Genre information from user input.
"""
def __init__(self):
self.tracks = None
self.album = None
self.thumbnail_url = None
self.thumbnail_data = None
self.artist = None
self.year = None
self.genre = None
def parse_from_json(self, data):
"""Parse the JSON data available after an HTML request to the
YouTube playlist URL.
Parameters
----------
data : Dict
JSON data extracted from the HTML request response.
Returns
-------
YoutubePlaylistInfo
Returns itself.
"""
logging.info("Parsing playlist JSON data")
self.tracks = dict()
channels = list()
video_array = data["contents"]["twoColumnBrowseResultsRenderer"]\
["tabs"][0]["tabRenderer"]["content"]\
["sectionListRenderer"]["contents"][0]\
["itemSectionRenderer"]["contents"][0]\
["playlistVideoListRenderer"]["contents"]
for video_item in video_array:
item = video_item["playlistVideoRenderer"]
self.tracks[item["index"]["simpleText"]] = item["title"]["runs"][0]["text"]
channels.append(item["shortBylineText"]["runs"][0]["text"])
thumbnail_array = data["sidebar"]["playlistSidebarRenderer"]\
["items"][0]\
["playlistSidebarPrimaryInfoRenderer"]\
["thumbnailRenderer"]\
["playlistCustomThumbnailRenderer"]\
["thumbnail"]["thumbnails"]
self.thumbnail_url = max(
thumbnail_array,
key=lambda item: item["width"]
)["url"]
self.album = data["sidebar"]["playlistSidebarRenderer"]["items"][0]\
["playlistSidebarPrimaryInfoRenderer"]\
["title"]["runs"][0]["text"]
self.artist = most_common_list_value(channels)
return self
def fetch_thumbnail(self, headers):
"""Send a request for the thumbnail. Uses the `thumbnail_url` attribute
and writes response to the `thumbnail_data` attribute.
Parameters
----------
headers : Dict[str, str]
Request HTTP headers.
"""
logging.info("Requesting thumbnail at %s", self.thumbnail_url)
response = requests.get(self.thumbnail_url, headers=headers)
self.thumbnail_data = PIL.Image.open(io.BytesIO(response.content))
def validate(self):
"""Ask for user input to validate the playlist info.
"""
print("The playlist contains %d tracks." % len(self.tracks))
for attr in ["album", "artist", "year", "genre"]:
attr_value = getattr(self, attr)
uinput = input("%s: %s\n>>> " % (
attr,
"?" if attr_value is None else "'%s'" % attr_value
))
if uinput.strip() != "":
setattr(self, attr, uinput.strip())
def set_tags(self, pdf):
"""Set the tags to downloaded tracks from the playlist info.
Parameters
----------
pdf : str
Path to the folder tracks were downloaded to.
"""
img_data = io.BytesIO()
self.thumbnail_data.convert("RGB").save(img_data, format="jpeg")
for filename in glob.glob(os.path.join(pdf, "*.mp3")):
track_key = str(int(os.path.splitext(os.path.basename(filename))[0]) + 1)
audiofile = eyed3.load(filename)
audiofile.tag.title = self.tracks[track_key].strip()
audiofile.tag.artist = self.artist.strip()
audiofile.tag.album_artist = self.artist.strip()
audiofile.tag.album = self.album.strip()
audiofile.tag.track_num = (int(track_key), len(self.tracks))
audiofile.tag.disc_num = (1, 1)
audiofile.tag.genre = self.genre.strip()
audiofile.tag.recording_date = int(self.year.strip())
audiofile.tag.images.set(
eyed3.id3.frames.ImageFrame.FRONT_COVER,
img_data.getvalue(),
"image/jpeg"
)
audiofile.tag.save()
def check_playlist_url(playlist_url):
"""Check if a playlist URL is well-formated.
Parameters
----------
playlist_url : str
URL to a YouTube playlist.
Returns
-------
str
If the URL is well-formated, return the playlist ID. Else return `None`.
"""
match = re.match(
r"https?://www\.youtube\.com/playlist\?list=(.+)",
playlist_url.strip()
)
if match is None:
raise ValueError("Incorrect URL: %s" % playlist_url)
return match.group(1)
class PlaylistDownloader:
"""Wrapper for YouTube playlist downloading processes.
Parameters
----------
config : musar.config.Config
General configuration.
Attributes
----------
html : str
Response from the playlist HTTP request
data : Dict
Parsed JSON data from the response `html`
info : YoutubePlaylistInfo
Playlist metadata.
headers : Dict[str, str]
Headers for HTTP requests.
config
"""
def __init__(self, config):
self.config = config
self.html = None
self.data = None
self.info = YoutubePlaylistInfo()
self.headers = {
"User-Agent": "{}".format(shadow_useragent.ShadowUserAgent().percent(0.05)),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
}
def main(self,
playlist_url,
skip_download=False,
skip_tags=False,
edit_tags=False
):
"""Download a YouTube playlist and set the tags to the audio tracks.
Parameters
----------
playlist_url : str
URL of the playlist to download.
skip_download : bool
If `True`, no downloading occurs, simply tag setting for already
downloaded files. Files must be in the correct downloads folder.
skip_tags : bool
If `True`, tags are not automatically set to the tracks.
edit_tags : bool
If `True`, opens the Mp3tag program on the downloaded folder.
Returns
-------
str
Path to the folder tracks were downloaded to.
"""
playlist_id = check_playlist_url(playlist_url)
logging.info("Downloading playlist with id '%s'", playlist_id)
pdf = os.path.join(self.config.options.download_folder, playlist_id)
if not skip_download or not skip_tags:
self.html = request_playlist_html(playlist_url, self.headers)
self.data = extract_initial_data(self.html)
if not skip_download:
video_array = self.data["contents"]["twoColumnBrowseResultsRenderer"]\
["tabs"][0]["tabRenderer"]["content"]\
["sectionListRenderer"]["contents"][0]\
["itemSectionRenderer"]["contents"][0]\
["playlistVideoListRenderer"]["contents"]
os.makedirs(pdf, exist_ok=False)
for i, video_item in enumerate(video_array):
print("Downloading %d of %d" % (i + 1, len(video_array)))
video_id = video_item["playlistVideoRenderer"]["videoId"]
video_url = "https://www.youtube.com/watch?v=%s" % video_id
output_filename = os.path.join(
pdf,
"%s.%%(ext)s" % str(i).zfill(3)
)
download_video(
self.config.options.youtube_dl_path,
video_url,
output_filename
)
if not skip_tags:
self.info.parse_from_json(self.data)
self.info.validate()
self.info.fetch_thumbnail(self.headers)
self.info.set_tags(pdf)
if edit_tags:
command = [
self.config.options.mp3tag_path,
"/fp",
os.path.realpath(pdf),
]
process = subprocess.Popen(command)
process.wait()
os.startfile(pdf)
return pdf
def request_playlist_html(playlist_url, headers):
"""Send a request to a YouTube playlist webpage.
Parameters
----------
playlist_url : str
URL to the playlist webpage.
headers : Dict[str, str]
HTTP request headers.
Returns
-------
str
Response HTML
"""
logging.info("Requesting YouTube playlist at %s", playlist_url)
response = requests.get(playlist_url, headers=headers)
return response.text
def extract_initial_data(html):
"""Extract and parse the JSON string from the HTML of a playlist webpage.
Parameters
----------
html : str
HTML to extract the string from.
Returns
-------
Dict
Parsed JSON data.
"""
logging.info("Extracting JSON string from playlist HTML data")
for line in html.split("\n"):
if line.strip().startswith('window["ytInitialData"]'):
return json.loads(line.strip()[26:-1])
return None
Functions
def check_playlist_url(playlist_url)
-
Check if a playlist URL is well-formated.
Parameters
playlist_url
:str
- URL to a YouTube playlist.
Returns
str
- If the URL is well-formated, return the playlist ID. Else return
None
.
Expand source code
def check_playlist_url(playlist_url): """Check if a playlist URL is well-formated. Parameters ---------- playlist_url : str URL to a YouTube playlist. Returns ------- str If the URL is well-formated, return the playlist ID. Else return `None`. """ match = re.match( r"https?://www\.youtube\.com/playlist\?list=(.+)", playlist_url.strip() ) if match is None: raise ValueError("Incorrect URL: %s" % playlist_url) return match.group(1)
def download_video(youtube_dl_path, video_url, output_filename)
-
Download a YouTube video using youtube-dl, and extract its audio.
Parameters
youtube_dl_path
:str
- Path to the youtube-dl executable.
video_url
:str
- URL of the YouTube video.
output_filename
:str
- Path for the output MP3 file.
Expand source code
def download_video(youtube_dl_path, video_url, output_filename): """Download a YouTube video using youtube-dl, and extract its audio. Parameters ---------- youtube_dl_path : str Path to the youtube-dl executable. video_url : str URL of the YouTube video. output_filename : str Path for the output MP3 file. """ logging.info("Downloading YouTube video %s", video_url) command = [ youtube_dl_path, # "--verbose", "--extract-audio", "--audio-format", "mp3", "--output", output_filename, video_url ] process = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) process.wait()
def extract_initial_data(html)
-
Extract and parse the JSON string from the HTML of a playlist webpage.
Parameters
html
:str
- HTML to extract the string from.
Returns
Dict
- Parsed JSON data.
Expand source code
def extract_initial_data(html): """Extract and parse the JSON string from the HTML of a playlist webpage. Parameters ---------- html : str HTML to extract the string from. Returns ------- Dict Parsed JSON data. """ logging.info("Extracting JSON string from playlist HTML data") for line in html.split("\n"): if line.strip().startswith('window["ytInitialData"]'): return json.loads(line.strip()[26:-1]) return None
def request_playlist_html(playlist_url, headers)
-
Send a request to a YouTube playlist webpage.
Parameters
playlist_url
:str
- URL to the playlist webpage.
headers
:Dict[str, str]
- HTTP request headers.
Returns
str
- Response HTML
Expand source code
def request_playlist_html(playlist_url, headers): """Send a request to a YouTube playlist webpage. Parameters ---------- playlist_url : str URL to the playlist webpage. headers : Dict[str, str] HTTP request headers. Returns ------- str Response HTML """ logging.info("Requesting YouTube playlist at %s", playlist_url) response = requests.get(playlist_url, headers=headers) return response.text
Classes
class PlaylistDownloader (config)
-
Wrapper for YouTube playlist downloading processes.
Parameters
config
:Config
- General configuration.
Attributes
html
:str
- Response from the playlist HTTP request
data
:Dict
- Parsed JSON data from the response
html
info
:YoutubePlaylistInfo
- Playlist metadata.
headers
:Dict[str, str]
- Headers for HTTP requests.
config
Expand source code
class PlaylistDownloader: """Wrapper for YouTube playlist downloading processes. Parameters ---------- config : musar.config.Config General configuration. Attributes ---------- html : str Response from the playlist HTTP request data : Dict Parsed JSON data from the response `html` info : YoutubePlaylistInfo Playlist metadata. headers : Dict[str, str] Headers for HTTP requests. config """ def __init__(self, config): self.config = config self.html = None self.data = None self.info = YoutubePlaylistInfo() self.headers = { "User-Agent": "{}".format(shadow_useragent.ShadowUserAgent().percent(0.05)), "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1", "Pragma": "no-cache", "Cache-Control": "no-cache", } def main(self, playlist_url, skip_download=False, skip_tags=False, edit_tags=False ): """Download a YouTube playlist and set the tags to the audio tracks. Parameters ---------- playlist_url : str URL of the playlist to download. skip_download : bool If `True`, no downloading occurs, simply tag setting for already downloaded files. Files must be in the correct downloads folder. skip_tags : bool If `True`, tags are not automatically set to the tracks. edit_tags : bool If `True`, opens the Mp3tag program on the downloaded folder. Returns ------- str Path to the folder tracks were downloaded to. """ playlist_id = check_playlist_url(playlist_url) logging.info("Downloading playlist with id '%s'", playlist_id) pdf = os.path.join(self.config.options.download_folder, playlist_id) if not skip_download or not skip_tags: self.html = request_playlist_html(playlist_url, self.headers) self.data = extract_initial_data(self.html) if not skip_download: video_array = self.data["contents"]["twoColumnBrowseResultsRenderer"]\ ["tabs"][0]["tabRenderer"]["content"]\ ["sectionListRenderer"]["contents"][0]\ ["itemSectionRenderer"]["contents"][0]\ ["playlistVideoListRenderer"]["contents"] os.makedirs(pdf, exist_ok=False) for i, video_item in enumerate(video_array): print("Downloading %d of %d" % (i + 1, len(video_array))) video_id = video_item["playlistVideoRenderer"]["videoId"] video_url = "https://www.youtube.com/watch?v=%s" % video_id output_filename = os.path.join( pdf, "%s.%%(ext)s" % str(i).zfill(3) ) download_video( self.config.options.youtube_dl_path, video_url, output_filename ) if not skip_tags: self.info.parse_from_json(self.data) self.info.validate() self.info.fetch_thumbnail(self.headers) self.info.set_tags(pdf) if edit_tags: command = [ self.config.options.mp3tag_path, "/fp", os.path.realpath(pdf), ] process = subprocess.Popen(command) process.wait() os.startfile(pdf) return pdf
Methods
def main(self, playlist_url, skip_download=False, skip_tags=False, edit_tags=False)
-
Download a YouTube playlist and set the tags to the audio tracks.
Parameters
playlist_url
:str
- URL of the playlist to download.
skip_download
:bool
- If
True
, no downloading occurs, simply tag setting for already downloaded files. Files must be in the correct downloads folder. skip_tags
:bool
- If
True
, tags are not automatically set to the tracks. edit_tags
:bool
- If
True
, opens the Mp3tag program on the downloaded folder.
Returns
str
- Path to the folder tracks were downloaded to.
Expand source code
def main(self, playlist_url, skip_download=False, skip_tags=False, edit_tags=False ): """Download a YouTube playlist and set the tags to the audio tracks. Parameters ---------- playlist_url : str URL of the playlist to download. skip_download : bool If `True`, no downloading occurs, simply tag setting for already downloaded files. Files must be in the correct downloads folder. skip_tags : bool If `True`, tags are not automatically set to the tracks. edit_tags : bool If `True`, opens the Mp3tag program on the downloaded folder. Returns ------- str Path to the folder tracks were downloaded to. """ playlist_id = check_playlist_url(playlist_url) logging.info("Downloading playlist with id '%s'", playlist_id) pdf = os.path.join(self.config.options.download_folder, playlist_id) if not skip_download or not skip_tags: self.html = request_playlist_html(playlist_url, self.headers) self.data = extract_initial_data(self.html) if not skip_download: video_array = self.data["contents"]["twoColumnBrowseResultsRenderer"]\ ["tabs"][0]["tabRenderer"]["content"]\ ["sectionListRenderer"]["contents"][0]\ ["itemSectionRenderer"]["contents"][0]\ ["playlistVideoListRenderer"]["contents"] os.makedirs(pdf, exist_ok=False) for i, video_item in enumerate(video_array): print("Downloading %d of %d" % (i + 1, len(video_array))) video_id = video_item["playlistVideoRenderer"]["videoId"] video_url = "https://www.youtube.com/watch?v=%s" % video_id output_filename = os.path.join( pdf, "%s.%%(ext)s" % str(i).zfill(3) ) download_video( self.config.options.youtube_dl_path, video_url, output_filename ) if not skip_tags: self.info.parse_from_json(self.data) self.info.validate() self.info.fetch_thumbnail(self.headers) self.info.set_tags(pdf) if edit_tags: command = [ self.config.options.mp3tag_path, "/fp", os.path.realpath(pdf), ] process = subprocess.Popen(command) process.wait() os.startfile(pdf) return pdf
class YoutubePlaylistInfo
-
Wrapper for a YouTube playlist metadata.
Attributes
tracks
:Dict[index, str]
- Keys are playlist indices and values are video titles.
album
:str
- Playlist title.
thumbnail_url
:str
- URL of the playlist's thumbnail.
thumbnail_data
:PIL.Image.Image
- Playlist's thumbnail.
artist
:str
- Most common channel accross playlist's videos.
year
:str
- Year information from user input.
genre
:str
- Genre information from user input.
Expand source code
class YoutubePlaylistInfo: """Wrapper for a YouTube playlist metadata. Attributes ---------- tracks : Dict[index, str] Keys are playlist indices and values are video titles. album : str Playlist title. thumbnail_url : str URL of the playlist's thumbnail. thumbnail_data : PIL.Image.Image Playlist's thumbnail. artist : str Most common channel accross playlist's videos. year : str Year information from user input. genre : str Genre information from user input. """ def __init__(self): self.tracks = None self.album = None self.thumbnail_url = None self.thumbnail_data = None self.artist = None self.year = None self.genre = None def parse_from_json(self, data): """Parse the JSON data available after an HTML request to the YouTube playlist URL. Parameters ---------- data : Dict JSON data extracted from the HTML request response. Returns ------- YoutubePlaylistInfo Returns itself. """ logging.info("Parsing playlist JSON data") self.tracks = dict() channels = list() video_array = data["contents"]["twoColumnBrowseResultsRenderer"]\ ["tabs"][0]["tabRenderer"]["content"]\ ["sectionListRenderer"]["contents"][0]\ ["itemSectionRenderer"]["contents"][0]\ ["playlistVideoListRenderer"]["contents"] for video_item in video_array: item = video_item["playlistVideoRenderer"] self.tracks[item["index"]["simpleText"]] = item["title"]["runs"][0]["text"] channels.append(item["shortBylineText"]["runs"][0]["text"]) thumbnail_array = data["sidebar"]["playlistSidebarRenderer"]\ ["items"][0]\ ["playlistSidebarPrimaryInfoRenderer"]\ ["thumbnailRenderer"]\ ["playlistCustomThumbnailRenderer"]\ ["thumbnail"]["thumbnails"] self.thumbnail_url = max( thumbnail_array, key=lambda item: item["width"] )["url"] self.album = data["sidebar"]["playlistSidebarRenderer"]["items"][0]\ ["playlistSidebarPrimaryInfoRenderer"]\ ["title"]["runs"][0]["text"] self.artist = most_common_list_value(channels) return self def fetch_thumbnail(self, headers): """Send a request for the thumbnail. Uses the `thumbnail_url` attribute and writes response to the `thumbnail_data` attribute. Parameters ---------- headers : Dict[str, str] Request HTTP headers. """ logging.info("Requesting thumbnail at %s", self.thumbnail_url) response = requests.get(self.thumbnail_url, headers=headers) self.thumbnail_data = PIL.Image.open(io.BytesIO(response.content)) def validate(self): """Ask for user input to validate the playlist info. """ print("The playlist contains %d tracks." % len(self.tracks)) for attr in ["album", "artist", "year", "genre"]: attr_value = getattr(self, attr) uinput = input("%s: %s\n>>> " % ( attr, "?" if attr_value is None else "'%s'" % attr_value )) if uinput.strip() != "": setattr(self, attr, uinput.strip()) def set_tags(self, pdf): """Set the tags to downloaded tracks from the playlist info. Parameters ---------- pdf : str Path to the folder tracks were downloaded to. """ img_data = io.BytesIO() self.thumbnail_data.convert("RGB").save(img_data, format="jpeg") for filename in glob.glob(os.path.join(pdf, "*.mp3")): track_key = str(int(os.path.splitext(os.path.basename(filename))[0]) + 1) audiofile = eyed3.load(filename) audiofile.tag.title = self.tracks[track_key].strip() audiofile.tag.artist = self.artist.strip() audiofile.tag.album_artist = self.artist.strip() audiofile.tag.album = self.album.strip() audiofile.tag.track_num = (int(track_key), len(self.tracks)) audiofile.tag.disc_num = (1, 1) audiofile.tag.genre = self.genre.strip() audiofile.tag.recording_date = int(self.year.strip()) audiofile.tag.images.set( eyed3.id3.frames.ImageFrame.FRONT_COVER, img_data.getvalue(), "image/jpeg" ) audiofile.tag.save()
Methods
def fetch_thumbnail(self, headers)
-
Send a request for the thumbnail. Uses the
thumbnail_url
attribute and writes response to thethumbnail_data
attribute.Parameters
headers
:Dict[str, str]
- Request HTTP headers.
Expand source code
def fetch_thumbnail(self, headers): """Send a request for the thumbnail. Uses the `thumbnail_url` attribute and writes response to the `thumbnail_data` attribute. Parameters ---------- headers : Dict[str, str] Request HTTP headers. """ logging.info("Requesting thumbnail at %s", self.thumbnail_url) response = requests.get(self.thumbnail_url, headers=headers) self.thumbnail_data = PIL.Image.open(io.BytesIO(response.content))
def parse_from_json(self, data)
-
Parse the JSON data available after an HTML request to the YouTube playlist URL.
Parameters
data
:Dict
- JSON data extracted from the HTML request response.
Returns
YoutubePlaylistInfo
- Returns itself.
Expand source code
def parse_from_json(self, data): """Parse the JSON data available after an HTML request to the YouTube playlist URL. Parameters ---------- data : Dict JSON data extracted from the HTML request response. Returns ------- YoutubePlaylistInfo Returns itself. """ logging.info("Parsing playlist JSON data") self.tracks = dict() channels = list() video_array = data["contents"]["twoColumnBrowseResultsRenderer"]\ ["tabs"][0]["tabRenderer"]["content"]\ ["sectionListRenderer"]["contents"][0]\ ["itemSectionRenderer"]["contents"][0]\ ["playlistVideoListRenderer"]["contents"] for video_item in video_array: item = video_item["playlistVideoRenderer"] self.tracks[item["index"]["simpleText"]] = item["title"]["runs"][0]["text"] channels.append(item["shortBylineText"]["runs"][0]["text"]) thumbnail_array = data["sidebar"]["playlistSidebarRenderer"]\ ["items"][0]\ ["playlistSidebarPrimaryInfoRenderer"]\ ["thumbnailRenderer"]\ ["playlistCustomThumbnailRenderer"]\ ["thumbnail"]["thumbnails"] self.thumbnail_url = max( thumbnail_array, key=lambda item: item["width"] )["url"] self.album = data["sidebar"]["playlistSidebarRenderer"]["items"][0]\ ["playlistSidebarPrimaryInfoRenderer"]\ ["title"]["runs"][0]["text"] self.artist = most_common_list_value(channels) return self
-
Set the tags to downloaded tracks from the playlist info.
Parameters
pdf
:str
- Path to the folder tracks were downloaded to.
Expand source code
def set_tags(self, pdf): """Set the tags to downloaded tracks from the playlist info. Parameters ---------- pdf : str Path to the folder tracks were downloaded to. """ img_data = io.BytesIO() self.thumbnail_data.convert("RGB").save(img_data, format="jpeg") for filename in glob.glob(os.path.join(pdf, "*.mp3")): track_key = str(int(os.path.splitext(os.path.basename(filename))[0]) + 1) audiofile = eyed3.load(filename) audiofile.tag.title = self.tracks[track_key].strip() audiofile.tag.artist = self.artist.strip() audiofile.tag.album_artist = self.artist.strip() audiofile.tag.album = self.album.strip() audiofile.tag.track_num = (int(track_key), len(self.tracks)) audiofile.tag.disc_num = (1, 1) audiofile.tag.genre = self.genre.strip() audiofile.tag.recording_date = int(self.year.strip()) audiofile.tag.images.set( eyed3.id3.frames.ImageFrame.FRONT_COVER, img_data.getvalue(), "image/jpeg" ) audiofile.tag.save()
def validate(self)
-
Ask for user input to validate the playlist info.
Expand source code
def validate(self): """Ask for user input to validate the playlist info. """ print("The playlist contains %d tracks." % len(self.tracks)) for attr in ["album", "artist", "year", "genre"]: attr_value = getattr(self, attr) uinput = input("%s: %s\n>>> " % ( attr, "?" if attr_value is None else "'%s'" % attr_value )) if uinput.strip() != "": setattr(self, attr, uinput.strip())