From 761e473acd7e260768ae3db649ac594ac34cd4ef Mon Sep 17 00:00:00 2001 From: Rocky Roer Date: Wed, 10 Jul 2019 16:03:12 -0400 Subject: [PATCH 1/5] Started Configuration Settings - Added maximum number of songs key to settings-example.cfg - Added playlist ordering key to settings-example.cfg - Added helpful comments to settings-example.cfg - Rewrote createbillboardplaylist.py to read in settings - Did not implement those settings yet. --- createbillboardplaylist.py | 30 +++++++++++++++++++++++++----- settings-example.cfg | 7 +++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/createbillboardplaylist.py b/createbillboardplaylist.py index dfe92f2..550e248 100755 --- a/createbillboardplaylist.py +++ b/createbillboardplaylist.py @@ -324,8 +324,9 @@ def create_all(self): def load_config(logger): """Loads config values from the settings.cfg file in the script dir""" config_path = get_script_dir() + 'settings.cfg' - section_name = 'accounts' + config_values = {} + # Do basic checks on the config file if not os.path.exists(config_path): logger.error("Error: No config file found. Copy settings-example.cfg " "to settings.cfg and customize it.") @@ -334,7 +335,7 @@ def load_config(logger): config = SafeConfigParser() config.read(config_path) - # Do basic checks on the config file + section_name = 'accounts' if not config.has_section(section_name): logger.error("Error: The config file doesn't have an accounts " "section. Check the config file format.") @@ -345,9 +346,25 @@ def load_config(logger): "Check the config file values.") exit() - config_values = { - 'api_key': config.get(section_name, 'api_key'), - } + config_values['api_key'] = config.get(section_name, 'api_key') + + section_name = 'settings' + if not config.has_section(section_name): + logger.error("Error: The config file doesn't have an settings " + "section. Check the config file format.") + exit() + + if not config.has_option(section_name, 'max_number_of_songs'): + logger.error("Error: The max_number_of_songs value is missing: " + "Check the config file values.") + exit() + config_values['max_number_of_songs'] = config.get(section_name, 'max_number_of_songs') + + if not config.has_option(section_name, 'playlist_ordering'): + logger.error("Error: The playlist_ordering value is missing: " + "Check the config file values.") + exit() + config_values['playlist_ordering'] = config.get(section_name, 'playlist_ordering') return config_values @@ -364,6 +381,9 @@ def main(): logger.setLevel(logging.INFO) config = load_config(logger) + #print(config['max_number_of_songs']) + #print(config['playlist_ordering']) + youtube = YoutubeAdapter(logger, config['api_key'], get_script_dir()) billboard_adapter = BillboardAdapter() diff --git a/settings-example.cfg b/settings-example.cfg index 89034f4..1489be9 100644 --- a/settings-example.cfg +++ b/settings-example.cfg @@ -10,4 +10,11 @@ # The default key is not valid, but looks similar to a valid key api_key = BOzaTrDFLxICpI9ReyXiK3cVy9X2NYhcLz3gqTn +[settings] +# The number of songs for each play list. +# Should be an integer greater than 0. +# Enter a large number (e.g. 200) if you want all songs downloaded for any chart. +max_number_of_songs = 5 +# The ordering of videos in playlists should be ASCENDING or DESCENDING +playlist_ordering = ASCENDING \ No newline at end of file From e2dbe5a154c8dfaac8a8341621375d25d9d72a82 Mon Sep 17 00:00:00 2001 From: Rocky Roer Date: Wed, 10 Jul 2019 16:52:26 -0400 Subject: [PATCH 2/5] Implemented Number of Songs and Playlist Ordering - Modified createbillboardplaylist.py to filter chart entries down to only include up to the maximum number of videos given in the config, and put them in either ascending or descending order --- createbillboardplaylist.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/createbillboardplaylist.py b/createbillboardplaylist.py index 550e248..998b007 100755 --- a/createbillboardplaylist.py +++ b/createbillboardplaylist.py @@ -208,10 +208,12 @@ def get_chart_data(cls, chart_id, date=None): class PlaylistCreator(object): """This class contains the logic needed to retrieve Billboard charts and create playlists from them.""" - def __init__(self, logger, youtube, billboard_adapter): + def __init__(self, logger, youtube, billboard_adapter, config): self.logger = logger self.youtube = youtube self.billboard = billboard_adapter + self.playlist_ordering = config['playlist_ordering'] + self.max_number_of_songs = int(config['max_number_of_songs']) def add_first_video_to_playlist(self, pl_id, search_query): """Does a search for videos and adds the first result to the given @@ -232,7 +234,7 @@ def add_chart_entries_to_playlist(self, pl_id, entries): song_count = 0 for entry in entries: song_count += 1 - if song_count > 100: + if song_count > self.max_number_of_songs: break query = entry.artist + ' ' + entry.title @@ -254,6 +256,12 @@ def create_playlist_from_chart(self, chart_id, chart_name, .strptime(chart.date, '%Y-%m-%d') .strftime("%B %d, %Y")) + # Create list of max_number_of_songs length in correct order + if self.playlist_ordering == "ASCENDING": + entries = chart.entries[0:self.max_number_of_songs] + elif self.playlist_ordering == "DESCENDING": + entries = chart.entries[0:self.max_number_of_songs][::-1] + # Create a new playlist, if it doesn't already exist pl_title = "{0} - {1}".format(chart_name, chart_date) pl_description = ("This playlist contains the " + num_songs_phrase + @@ -269,7 +277,7 @@ def create_playlist_from_chart(self, chart_id, chart_name, return pl_id = self.youtube.create_new_playlist(pl_title, pl_description) - self.add_chart_entries_to_playlist(pl_id, chart.entries) + self.add_chart_entries_to_playlist(pl_id, entries) return def create_all(self): @@ -381,13 +389,11 @@ def main(): logger.setLevel(logging.INFO) config = load_config(logger) - #print(config['max_number_of_songs']) - #print(config['playlist_ordering']) - + youtube = YoutubeAdapter(logger, config['api_key'], get_script_dir()) billboard_adapter = BillboardAdapter() - playlist_creator = PlaylistCreator(logger, youtube, billboard_adapter) + playlist_creator = PlaylistCreator(logger, youtube, billboard_adapter, config) playlist_creator.create_all() From 5f697556e513bf920310a9921c1488095e16972e Mon Sep 17 00:00:00 2001 From: Rocky Roer Date: Thu, 11 Jul 2019 10:17:01 -0400 Subject: [PATCH 3/5] Started Adding Charts configurability to settings.cfg - Added Charts section and instructions for choosing charts in settings-example - Changed createbillboardplaylist to read in charts_to_create as list - Did not implement using this charts_to_create yet. --- createbillboardplaylist.py | 17 ++++++++++++++++- settings-example.cfg | 9 ++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/createbillboardplaylist.py b/createbillboardplaylist.py index 998b007..6cc2955 100755 --- a/createbillboardplaylist.py +++ b/createbillboardplaylist.py @@ -374,6 +374,18 @@ def load_config(logger): exit() config_values['playlist_ordering'] = config.get(section_name, 'playlist_ordering') + section_name = 'charts' + if not config.has_section(section_name): + logger.error("Error: The config file doesn't have an charts " + "section. Check the config file format.") + exit() + + if not config.has_option(section_name, 'charts_to_create'): + logger.error("Error: The charts_to_create value is missing: " + "Check the config file values.") + exit() + config_values['charts_to_create'] = config.get(section_name, 'charts_to_create').strip('[]').translate(None, '\" ').split(',') + return config_values @@ -390,11 +402,14 @@ def main(): config = load_config(logger) + for chart_id in config['charts_to_create']: + print(chart_id) + youtube = YoutubeAdapter(logger, config['api_key'], get_script_dir()) billboard_adapter = BillboardAdapter() playlist_creator = PlaylistCreator(logger, youtube, billboard_adapter, config) - playlist_creator.create_all() + #playlist_creator.create_all() if __name__ == '__main__': diff --git a/settings-example.cfg b/settings-example.cfg index 1489be9..0643843 100644 --- a/settings-example.cfg +++ b/settings-example.cfg @@ -17,4 +17,11 @@ api_key = BOzaTrDFLxICpI9ReyXiK3cVy9X2NYhcLz3gqTn max_number_of_songs = 5 # The ordering of videos in playlists should be ASCENDING or DESCENDING -playlist_ordering = ASCENDING \ No newline at end of file +playlist_ordering = ASCENDING + +[charts] +# Put in a list the charts that you would like to create playlists for. +# The available charts are: "hot-100", "billboard-200", "pop-songs", "rock-songs", "latin-songs", "r-b-hip-hop-songs", "dance-electronic-songs", "country-songs", "greatest-hot-100-singles", "hot-holiday-songs", "christian-songs","japan-hot-100", "summer-songs", +# Try it with any chart of songs by taking the last part of the url of the charts page: +# e.g. https://www.billboard.com/charts/streaming-songs --> "streaming-songs" +charts_to_create = ["rock-songs","r-b-hip-hop-songs"] \ No newline at end of file From 88efed6de35bab1fa8f253947326b00fa0a3f825 Mon Sep 17 00:00:00 2001 From: Rocky Roer Date: Thu, 11 Jul 2019 11:53:39 -0400 Subject: [PATCH 4/5] Started Implementing Using charts_to_create - Rewrote createbillboardplaylist to create only charts import from settings.cfg - Hardcoded (incomplete) dictionary object to handle playlist metadata --- createbillboardplaylist.py | 67 +++++++++++++------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/createbillboardplaylist.py b/createbillboardplaylist.py index 6cc2955..2482bc5 100755 --- a/createbillboardplaylist.py +++ b/createbillboardplaylist.py @@ -262,6 +262,11 @@ def create_playlist_from_chart(self, chart_id, chart_name, elif self.playlist_ordering == "DESCENDING": entries = chart.entries[0:self.max_number_of_songs][::-1] + print("\nAttempting to create {} playlist with the entries:".format(chart_id)) + for entry in entries: + print entry.rank, entry.artist, entry.title + + # Create a new playlist, if it doesn't already exist pl_title = "{0} - {1}".format(chart_name, chart_date) pl_description = ("This playlist contains the " + num_songs_phrase + @@ -280,50 +285,27 @@ def create_playlist_from_chart(self, chart_id, chart_name, self.add_chart_entries_to_playlist(pl_id, entries) return - def create_all(self): + def create_all(self, charts_to_create): """Create all of the default playlists with this week's Billboard charts.""" self.logger.info("### Script started at %s ###\n", time.strftime("%c")) - # Billboard Rock Songs - self.create_playlist_from_chart( - "rock-songs", - "Rock", - "top 50 ", - "http://www.billboard.com/charts/rock-songs", - ) - - # Billboard R&B/Hip-Hop Songs - self.create_playlist_from_chart( - "r-b-hip-hop-songs", - "R&B/Hip-Hop", - "top 50 ", - "http://www.billboard.com/charts/r-b-hip-hop-songs", - ) - - # Billboard Dance/Club Play Songs - self.create_playlist_from_chart( - "dance-club-play-songs", - "Dance/Club Play", - "top 50 ", - "http://www.billboard.com/charts/dance-club-play-songs", - ) - - # Billboard Pop Songs - self.create_playlist_from_chart( - "pop-songs", - "Pop", - "top 40 ", - "http://www.billboard.com/charts/pop-songs", - ) - - # Billboard Hot 100 - self.create_playlist_from_chart( - "hot-100", - "Hot 100", - "", - "http://www.billboard.com/charts/hot-100", - ) + chart_info = {"rock-songs": ("Rock", "top 50 "), + "r-b-hip-hop-songs": ("R&B/Hip-Hop", "top 50 "), + "dance-club-play-songs": ("Dance/Club Play", "top 50 "), + "pop-songs": ("Pop", "top 40 "), + "hot-100": ("Hot 100", "") + } + + for chart_id in charts_to_create: + chart_name = chart_info[chart_id][0] + num_songs_phrase = chart_info[chart_id][1] + web_url = "http://www.billboard.com/charts/rock-songs" + chart_id + self.create_playlist_from_chart( + chart_id, + chart_name, + num_songs_phrase, + web_url) self.logger.info("### Script finished at %s ###\n", time.strftime("%c")) @@ -402,14 +384,11 @@ def main(): config = load_config(logger) - for chart_id in config['charts_to_create']: - print(chart_id) - youtube = YoutubeAdapter(logger, config['api_key'], get_script_dir()) billboard_adapter = BillboardAdapter() playlist_creator = PlaylistCreator(logger, youtube, billboard_adapter, config) - #playlist_creator.create_all() + playlist_creator.create_all(config['charts_to_create']) if __name__ == '__main__': From e5c522b4076db42534ccc0b5551816fe98270f57 Mon Sep 17 00:00:00 2001 From: Rocky Roer Date: Thu, 11 Jul 2019 16:41:11 -0400 Subject: [PATCH 5/5] Reworked ChartData into Billboard Adapter Class - Extended ChartData object to have name, url, num_songs_phrase attributes assigned when chart is downloaded - Modified some function declarations and calls to only depend on chart-id - Moved most ChartData logic into BillBoard Adapter Class --- createbillboardplaylist.py | 65 ++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/createbillboardplaylist.py b/createbillboardplaylist.py index 2482bc5..1a1452f 100755 --- a/createbillboardplaylist.py +++ b/createbillboardplaylist.py @@ -202,7 +202,27 @@ class BillboardAdapter(object): # pylint: disable=too-few-public-methods def get_chart_data(cls, chart_id, date=None): """Returns the chart data for a given chart and date. If no date is given, it returns the current week's chart.""" - return billboard.ChartData(chart_id, date) + + chart_info = { + "rock-songs": ("Rock", "top 50 "), + "r-b-hip-hop-songs": ("R&B/Hip-Hop", "top 50 "), + "dance-club-play-songs": ("Dance/Club Play", "top 50 "), + "pop-songs": ("Pop", "top 40 "), + "hot-100": ("Hot 100", "") + } + + chart = billboard.ChartData(chart_id, date) + if date == None: + chart_date = (datetime + .strptime(chart.date, '%Y-%m-%d') + .strftime("%B %d, %Y")) + setattr(chart, "date", chart_date) + + setattr(chart, "title", chart_info[chart_id][0]) + setattr(chart, "url", "http://www.billboard.com/charts/rock-songs" + chart_id) + setattr(chart, "num_songs_phrase", chart_info[chart_id][1]) + + return chart class PlaylistCreator(object): @@ -246,32 +266,31 @@ def add_chart_entries_to_playlist(self, pl_id, entries): self.logger.info("\n---\n") - def create_playlist_from_chart(self, chart_id, chart_name, - num_songs_phrase, web_url): + def create_playlist_from_chart(self, chart_id): """Create and populate a new playlist with the current Billboard chart with the given ID""" # Get the songs from the Billboard web page chart = self.billboard.get_chart_data(chart_id) - chart_date = (datetime - .strptime(chart.date, '%Y-%m-%d') - .strftime("%B %d, %Y")) - + # Create list of max_number_of_songs length in correct order if self.playlist_ordering == "ASCENDING": entries = chart.entries[0:self.max_number_of_songs] elif self.playlist_ordering == "DESCENDING": entries = chart.entries[0:self.max_number_of_songs][::-1] - + else: + entries = chart.entries[0:self.max_number_of_songs] + print("\nAttempting to create {} playlist with the entries:".format(chart_id)) for entry in entries: print entry.rank, entry.artist, entry.title - # Create a new playlist, if it doesn't already exist - pl_title = "{0} - {1}".format(chart_name, chart_date) - pl_description = ("This playlist contains the " + num_songs_phrase + - "songs in the " + chart_name + " Songs chart for " - "the week of " + chart_date + ". " + web_url) + pl_title = "{0} - {1}".format(chart.title, chart.date) + pl_description = ("This playlist contains the " + + chart.num_songs_phrase + "songs in the " + + chart.title + " Songs chart " + + "for the week of " + chart.date + ". " + + chart.url) # Check for an existing playlist with the same title if self.youtube.playlist_exists_with_title(pl_title): @@ -289,24 +308,10 @@ def create_all(self, charts_to_create): """Create all of the default playlists with this week's Billboard charts.""" self.logger.info("### Script started at %s ###\n", time.strftime("%c")) - - chart_info = {"rock-songs": ("Rock", "top 50 "), - "r-b-hip-hop-songs": ("R&B/Hip-Hop", "top 50 "), - "dance-club-play-songs": ("Dance/Club Play", "top 50 "), - "pop-songs": ("Pop", "top 40 "), - "hot-100": ("Hot 100", "") - } - + for chart_id in charts_to_create: - chart_name = chart_info[chart_id][0] - num_songs_phrase = chart_info[chart_id][1] - web_url = "http://www.billboard.com/charts/rock-songs" + chart_id - self.create_playlist_from_chart( - chart_id, - chart_name, - num_songs_phrase, - web_url) - + self.create_playlist_from_chart(chart_id) + self.logger.info("### Script finished at %s ###\n", time.strftime("%c"))