class ytmUploader: """class to use ytmusicapi (https://github.com/sigma67/ytmusicapi) to upload spotifyplaylists""" def __init__(self, yt_channel_id, auth_filepath='headers_auth.json'): self.ytmusic = YTMusic(auth_filepath) self.yt_channel_id = yt_channel_id def uploadSpotifyPlaylist(self, spotifyPL): plTitle = spotifyPL.get('playlistName') print(f"Retrieving songs for album {plTitle}") formerPlaylistID = self.getPlaylistID(plTitle) # convert Spotify songs to ytmusic video ids # Has to be done regardless, to update if exists videoIDs = self.ytVideoIDs(spotifyPL) if formerPlaylistID: # Delete first print(f"Deleting: {plTitle}") self.ytmusic.delete_playlist(formerPlaylistID) # Create playlist print(f"Creating: {plTitle}") plID = self.ytmusic.create_playlist(title=plTitle, description=f"{plTitle} from Spotify - magic from playmaker script", privacy_status="PUBLIC", video_ids=list(videoIDs)) def ytVideoIDs(self, spotifyPL): ids = [] """ plTitle = spotifyPL.get('playlistName') if plTitle == 'RapCaviar': return rapCaviarIDs elif plTitle == 'Most Necessary': return mostNecessaryIDs elif plTitle == 'Get Turnt': return getTurntIDs """ for song in spotifyPL.get('tracks'): songname = song[0] ytSongs = self.ytmusic.search(songname, "songs") if ytSongs: topMatchingSong = ytSongs[0] ids.append(topMatchingSong.get('videoId')) return ids def getPlaylistID(self, plTitle): userInfo = self.ytmusic.get_user(self.yt_channel_id) if 'playlists' in userInfo: for song in userInfo.get('playlists').get('results'): if song["title"] == plTitle: return song["playlistId"] return ""
def get_playlist(self, genre, playlist_id): if playlist_id == None and genre == None: return None ytmusic = YTMusic() if playlist_id == None: with open("data/playlist_ids.json", 'r') as f: jsondump = json.load(f) playlist_id = jsondump[genre] playlist_data = ytmusic.get_playlist(playlist_id, 1000)['tracks'] else: playlist_data = ytmusic.get_playlist(playlist_id, 1000)['tracks'] playlist = Queue() for s in playlist_data: title = s['title'] duration = s['duration'] yt_id = s['videoId'] artists = [] for artist in s['artists']: artists.append(artist['name']) song = Song(title=title, duration=duration, artists=artists, yt_id=yt_id) playlist.append(song) return playlist
def UpdateLikeSongsSorted(databaseConnectionString: str): ytmusic = YTMusic("headers_auth.json") mongoClient = MongoClient(databaseConnectionString) db = mongoClient['scrobble'] playlistId = "PLJpUfuX6t6dScH3Ua2f2EsmRC4PolZp8I" likedSongs = db['scrobbleCount'].find( { "likeStatus": 1, "ytmusicPlayable": { "$ne": False } }, sort=[("time", 1)], limit=100) videoIds = [song['ytmusicId'] for song in likedSongs] ClearPlaylist(ytmusic=ytmusic, playlistId=playlistId) results = [ AddToPlaylist(ytmusic=ytmusic, playlistId=playlistId, videoId=videoId) for videoId in videoIds ] if results: nowFormatted = datetime.now().strftime("%Y-%m-%d at %H:%M") ytmusic.edit_playlist(playlistId=playlistId, description=f"Updated on {nowFormatted}") return
def main(): parser = argparse.ArgumentParser() parser.add_argument("-l", "--listId", help="Playlist ID", nargs=1, required=True) parser.add_argument("-v", "--videoId", help="Video ID", nargs=1, required=True) parser.add_argument("-s", "--setVideoId", help="Set Video ID", nargs=1, required=True) args = parser.parse_args() logger = logging.getLogger("app") logger.setLevel(logging.DEBUG) log_formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") log_hnd_screen = logging.StreamHandler(stream=sys.stdout) log_hnd_screen.setLevel(logging.DEBUG) log_hnd_screen.setFormatter(log_formatter) logger.addHandler(log_hnd_screen) ytmusic = YTMusic('ytm_auth.json') locale.setlocale(locale.LC_ALL, 'en_GB') list_id = args.listId[0] set_video_id = args.setVideoId[0] video_id = args.videoId[0] logger.info("Playlist: " + list_id) logger.info("SetVideoID: "+ set_video_id) logger.info("VideoID: "+ video_id) playlistItem = { "videoId": video_id, "setVideoId": set_video_id, } status = ytmusic.remove_playlist_items(playlistId=list_id, videos=[playlistItem]) logger.info(pformat(status))
def setup(): global ytmusic if os.path.exists("headers_auth_raw.txt"): with open("headers_auth_raw.txt", "r") as headersRaw: YTMusic.setup(filepath="headers_auth.json", headers_raw=headersRaw.read()) os.remove("headers_auth_raw.txt") ytmusic = YTMusic('headers_auth.json')
def setup_yt_music(): headers = request.json.get('headers') try: os.remove('headers_auth.json') except FileNotFoundError: pass YTMusic.setup(filepath='headers_auth.json', headers_raw=headers) return {'result': 'New headers_auth.json set'}
def ClearPlaylist(ytmusic: YTMusic, playlistId: str) -> str: trackCount = GetPlaylistTrackCount(ytmusic, playlistId=playlistId) if trackCount and trackCount > 0: currentPlaylist = ytmusic.get_playlist(playlistId, limit=trackCount) return ytmusic.remove_playlist_items(playlistId, currentPlaylist['tracks']) else: return
def search_ytmusic(phrase: str) -> str: ytmusic = YTMusic() results = ytmusic.search(query=phrase) for result in results: if result['resultType'] == 'video' or result['resultType'] == 'song': return result['videoId'] # TODO: Notify user if youtube did not find any music in the command channel logging.error('Youtube Music did not find any music.')
def __init__(self): """ Constructor.""" self.__youtube_music_api = YTMusic(HEADERS_AUTH_FILE_PATH) if YoutubeMusicApiSingleton.__instance__ is None: YoutubeMusicApiSingleton.__instance__ = self else: raise Exception( "You cannot create another YoutubeMusicApiSingleton class, this class is a singleton" )
def get_auth_obj(): auth_obj = None try: auth_obj = YTMusic(const.HEADERS_FILE) except (KeyError, AttributeError): logging.info(f"Creating {const.HEADERS_FILE} file...") auth_obj = YTMusic(YTMusic.setup(filepath=const.HEADERS_FILE)) return auth_obj
async def async_check_data(hass, user_input): """Check validity of the provided date.""" ret = {} if (CONF_COOKIE in user_input and CONF_HEADER_PATH in user_input): # sadly config flow will not allow to have a multiline text field # we get a looong string that we've to rearrange into multiline for ytmusic # the only thing we need is cookie + x-goog-authuser, lets try to cut this out # so the fields are written like 'identifier':'value', but some values actually have ':' inside, bummer. c = user_input[CONF_COOKIE] clean_cookie = "" clean_x_goog_authuser = "" ## lets try to find the cookie part cookie_pos = c.lower().find('cookie') if (cookie_pos >= 0): #_LOGGER.debug("found cookie in text") cookie_end = c[cookie_pos:] cookie_end_split = cookie_end.split(':') if (len(cookie_end_split) >= 3): #_LOGGER.debug("found three or more sections") cookie_length_last_field = cookie_end_split[2].rfind(' ') if (cookie_length_last_field >= 0): #_LOGGER.debug("found a space") cookie_length = len(cookie_end_split[0]) + 1 + len( cookie_end_split[1]) + 1 + cookie_length_last_field clean_cookie = c[cookie_pos:cookie_pos + cookie_length] #_LOGGER.debug(clean_cookie) ## lets try to find x-auth-part xauth_pos = c.lower().find('x-goog-authuser: '******'x-goog-authuser: '******'x-goog-authuser: '******' ') #_LOGGER.debug(xauth_len) if (xauth_len >= 0): #_LOGGER.debug("found space in text") clean_x_goog_authuser = c[xauth_pos:(xauth_pos + len('x-goog-authuser: '******'ve failed to find the cookie, the only thing we can do is at least to help with some breaks c = c.replace('cookie', '\ncookie') c = c.replace('Cookie', '\nCookie') c = c.replace('x-goog-authuser', '\nx-goog-authuser') c = c.replace('X-Goog-AuthUser', '\nX-Goog-AuthUser') #_LOGGER.debug("feeding with: ") #_LOGGER.debug(c) YTMusic.setup(filepath=user_input[CONF_HEADER_PATH], headers_raw=c) [ret, msg, api] = await async_try_login(hass, user_input[CONF_HEADER_PATH], "") return ret
def authenticate(reset=False): global ytmusic if reset: os.remove(authFile) if authFile.exists(): ytmusic = YTMusic(authFile) else: YTMusic.setup(filepath=authFile) exit(0)
def upload(self, mp3_files, dry_run=False, overwrite=False): for mp3_file in mp3_files: track = self.get_track(mp3_file=mp3_file) if track is not None and not overwrite: logging.info("Skipping %s", mp3_file) else: logging.info("Uploading %s", mp3_file) if not dry_run: if track is not None: self.delete(tracks=[track]) YTMusic.upload_song(mp3_file.file_path)
def play_music(text): ytmusic = YTMusic() songs = ytmusic.search(f'{text}') pprint(songs) for material in songs: material_type = material['resultType'] if material_type=='song': id = material['videoId'] break webbrowser.open_new_tab(f'https://music.youtube.com/watch?v={id}')
def __init__( self, auth_file_path="/home/vvasuki/sysconf/kunchikA/google/vishvas/yt_music.json" ): YTMusic.setup(filepath=auth_file_path) # Returns a list of dictionaries, one for each audio track, each with the following keys: ('browseId', 'title', 'album', 'album_artist', 'artist', 'track_number', 'track_size', 'disc_number', 'total_disc_count'). self.uploaded_tracks = YTMusic.get_library_upload_songs(limit=50000) self.albums = sorted( set(map(lambda track: track["album"], self.uploaded_tracks))) self.artists = sorted( set(map(lambda track: track["artist"], self.uploaded_tracks)))
def __init__(self): super(YoutubeMusicSkill, self).__init__() self.yt = YTMusic() self.idle_count = 0 self.ducking = False self.mouth_text = None self._playlists = None self.saved_tracks = None self.regexes = {} self.last_played_type = None # The last uri type that was started self.is_playing = False
def youtube_lyrics(youtube_id: str) -> Tuple[str, str, Dict]: """Gets lyrics (if exists) given the youtube id. NOTE: get_lyrics operation is wasteful as it discards a watch playlist while getting lyrics.""" ytmusic = YTMusic() # TODO: Make more efficent use of watch_playlist watch_playlist = ytmusic.get_watch_playlist(videoId=youtube_id) browse_id = watch_playlist["lyrics"] if browse_id: result = ytmusic.get_lyrics(browse_id) return result["lyrics"], result["source"], watch_playlist return None, None, watch_playlist
def playlist(id, doubles=False, skipErrors=False): if id is None: if not os.path.exists(os.path.expanduser("~/.ymusic.json")): auth() list = YTMusic( os.path.expanduser("~/.ymusic.json")).get_liked_songs(limit) else: if re.match(r"^https://", id): id = id.split('list=')[1] try: list = YTMusic().get_playlist(id, limit) except: if not os.path.exists(os.path.expanduser("~/.ymusic.json")): auth() list = YTMusic(os.path.expanduser("~/.ymusic.json")).get_playlist( id, limit) if doubles is False: for song in list['tracks']: if song['videoId'] is not None: fname = re.sub('[^-а-яА-Яa-zA-Z0-9_.() ]+', '', song['title']).strip() if song['album'] is not None: fname = os.path.join( re.sub('[^-а-яА-Яa-zA-Z0-9_.() ]+', '', song['album']['name']).strip(), fname) if not os.path.exists(fname + '.mp3'): if skipErrors is False: foreach(song, fname) else: try: foreach(song, fname) except: print('Error: vid: ' + song['videoId']) else: print('[youtube-music] Skiping: ' + fname) print('Finish') else: titles = [] ids = [] for song in list['tracks']: if song['videoId'] is not None: id = song['videoId'] title = song['title'].split(' [')[0].split(' (')[0] if title in titles: print('\n' + title) print('https://music.youtube.com/watch?v=' + ids[titles.index(title)]) print('https://music.youtube.com/watch?v=' + id) else: titles.append(title) ids.append(id)
class Youtube: def __init__(self): self.ytmusic = YTMusic() self.thumburl = "https://i.ytimg.com/vi/{vid_id}/sddefault.jpg" self.vidurl = "https://www.youtube.com/watch?v={vid_id}" def _get_cleaned_track_data(self, result: dict) -> Song: try: vid_id = result['videoId'] title = result['title'] album = title artist = result['artists'][0][ 'name'] if 'artists' in result else result['author'] imgurl = self.thumburl.format(vid_id=vid_id) return Song(vidurl=self.vidurl.format(vid_id=vid_id), title=title, artist=artist, album=album, imgurl=imgurl) except Exception as e: return None def get_playlist_tracks(self, playlist_id: str, limit: int = None) -> Union[Iterator[Song], None]: ''' Get a genrator of Songs from the given playlist_id Works only for public playlists. ''' if limit is None: limit = 10000 try: tracks = self.ytmusic.get_playlist(playlist_id, limit=limit)['tracks'] except KeyError: print('Invalid playlist or playlist is empty.') return None for track in tracks: yield self._get_cleaned_track_data(track) def get_song(self, song_name: str) -> Union[Song, None]: '''Get Song from a song name''' vidurl = get_yt_url(song_name) vid_id = re.search(r"watch\?v=(\S{11})", vidurl).group(1) return self._get_cleaned_track_data(self.ytmusic.get_song(vid_id))
def ytm_plsearch(): cursor = db_connect() ytmusic = YTMusic('ytm_auth.json') term = request.form.get("term") recently_played = None results = None if term is None: try: recently_played = ytmusic.get_history() recently_played = ytm_attach_playlists(recently_played, cursor) except KeyError as ex: ex_type, ex_value, ex_trace = sys.exc_info() flash( "Failed to retrieve history: KeyError: " + str(ex) + "\n" + ("".join( traceback.format_exception(ex_type, ex_value, ex_trace))), "error") else: sql = """ SELECT DISTINCT ytm_playlist_entries.entry_name, ytm_playlist_entries.duration, ytm_playlist_entries.artist_name, ytm_playlist_entries.album_name, ytm_playlist_entries.ytm_videoid AS videoId FROM ytm_playlist_entries LEFT JOIN ytm_playlists ON ytm_playlist_entries.ytm_playlistid = ytm_playlists.ytm_playlistid WHERE ytm_playlist_entries.deleted = 0 AND ytm_playlists.deleted = 0 AND ( ytm_playlist_entries.entry_name LIKE CONCAT('%%', %(term)s, '%%') OR ytm_playlist_entries.artist_name LIKE CONCAT('%%', %(term)s, '%%') OR ytm_playlist_entries.album_name LIKE CONCAT('%%', %(term)s, '%%') ) ORDER BY ytm_playlist_entries.artist_name ASC, ytm_playlist_entries.entry_name ASC, ytm_playlist_entries.album_name ASC """ params = { "term": term, } cursor.execute(sql, params) results = cursor.fetchall() results = ytm_attach_playlists(results, cursor) return render_template("ytm/plsearch.html", history=recently_played, results=results, term=term, ytm_list_artist_names=ytm_list_artist_names)
def __init__(self,bot): self.bot = bot with open("data/score.json",'r') as f: jsondump = json.load(f) self.scores = jsondump #stores the cumulative score of a server with open("data/playlist_ids.json", 'r') as f: jsondump = json.load(f) self.playlist_ids = jsondump self.ytmusic = YTMusic() self.pop_playlist = self.ytmusic.get_playlist(self.playlist_ids['pop'],300)['tracks'] self.local_scores = {} #stores the score for each game self.guess_data = { #"server": {"channelid":"","song playing":"","played":""} }
def MixPlaylists(ytmusic: YTMusic, playlistId1: str, playlistId2: str, finalPlaylistId: str) -> bool: playlist1 = ytmusic.get_playlist(playlistId1) playlist1Songs = playlist1['tracks'] playlist2 = ytmusic.get_playlist(playlistId2) playlist2Songs = playlist2['tracks'] songIds = [track['videoId'] for track in playlist1Songs + playlist2Songs] ClearPlaylist(ytmusic=ytmusic, playlistId=finalPlaylistId) random.shuffle(songIds) results = [ AddToPlaylist(ytmusic=ytmusic, playlistId=finalPlaylistId, videoId=videoId) for videoId in songIds ] return sum(results) > 0
def GetSongId(ytmusic: YTMusic, db: Database, videoId: str) -> ObjectId: try: songDocument = db['songs'].find_one({"ytmusicId": videoId}, {"ytmusicId": 1}) if songDocument is None: watchPlaylist = ytmusic.get_watch_playlist(videoId) browseId = None songInfo = watchPlaylist['tracks'][0] documentData = { "title": songInfo['title'], "artists": [artist['name'] for artist in songInfo['artists']], "ytmusicId": songInfo['videoId'] } if "album" in songInfo: documentData['album'] = songInfo['album']['name'] browseId = songInfo['album']['id'] likeStatus = GetLikeStatus(ytmusic, videoId, browseId, db) if likeStatus: documentData['likeStatus'] = likeStatus.value result = db['songs'].insert_one(documentData) songId = result.inserted_id else: songId = songDocument['_id'] return songId except Exception as e: print(f"GetSongId Error: {e}") return None
def main(): parser = argparse.ArgumentParser() parser.add_argument("-l", "--listId", help="Playlist ID", nargs=1) args = parser.parse_args() logger = logging.getLogger("app") logger.setLevel(logging.DEBUG) log_formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s") log_hnd_screen = logging.StreamHandler(stream=sys.stdout) log_hnd_screen.setLevel(logging.ERROR) log_hnd_screen.setFormatter(log_formatter) logger.addHandler(log_hnd_screen) ytmusic = YTMusic('ytm_auth.json') # loc = locale.getlocale() locale.setlocale(locale.LC_ALL, 'en_GB') only_list_id = None if args.listId is None: logger.info("No list id given - listing all playlists") list_playlists(ytmusic) else: only_list_id = args.listId[0] logger.info("Listing tracks for playlist: " + only_list_id) list_tracks(ytmusic, only_list_id)
def setUpClass(cls) -> None: cls._logger = logging.getLogger(__name__) cls._downloader = Downloader(cls._logger, Path(".", "..").resolve()) cls._time_zone = pytz.timezone("Australia/Brisbane") cls._client = YouTubeMusic(cls._logger, cls._downloader, cls._time_zone) cls._client._client = YTMusic()
def main(): path = sys.argv[1] if not os.path.exists(path): print("Invalid file path. %s" % path) return api = YTMusic('headers_auth.json') #if not api.is_authenticated(): # print("Login Error\n") # return print("start upload...\n") upload_info = api.upload_song(path) print(upload_info) print("upload is completed.\n")
def youtube_music(phrase: str) -> str: """Searches Youtube Music using the phase and returns the top video result's ID Args: phrase (str): phrase to search Youtube Music Returns: str: youtube video ID """ ytmusic = YTMusic() results = ytmusic.search(query=phrase) for result in results: if result["resultType"] == "video" or result["resultType"] == "song": return result["videoId"] # TODO: Notify user if youtube did not find any music in the command channel logging.error("Youtube Music did not find any music.")
def setup(): try: return YTMusic(os.path.join(__package__, "headers_auth.json")) except KeyError: sys.exit("Cookie invalid. Did you paste your cookie into headers_auth.json?") except AttributeError: sys.exit("Headers not found. Most likely the headers_auth.json file could not be located.")
def upload( directory: str = '.', oauth: str = os.environ['HOME'] + '/oauth', remove: bool = False, oneshot: bool = False, listener_only: bool = False, deduplicate_api: str = None, log: str = None, ) -> None: global last_snapshot handler = logging.handlers.WatchedFileHandler( log) if log else logging.StreamHandler() handler.setLevel(logging.DEBUG) handler.setFormatter( logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(handler) logger.info("Init Daemon - Press Ctrl+C to quit") api = YTMusic(oauth) if not api: raise ValueError("Error with credentials") observer = None deduplicate = DeduplicateApi(deduplicate_api) if deduplicate_api else None if not oneshot: if os.path.isdir(directory): last_snapshot = DirectorySnapshot(directory) event_handler = MusicToUpload() event_handler.api = api event_handler.oauth = oauth event_handler.path = directory event_handler.remove = remove event_handler.logger = logger event_handler.deduplicate_api = deduplicate observer = Observer() observer.schedule(event_handler, directory, recursive=True) observer.start() if not listener_only: files = [ file for file in glob.glob(glob.escape(directory) + '/**/*', recursive=True) ] for file_path in files: upload_file(api, file_path, logger, remove=remove, deduplicate_api=deduplicate) if oneshot: sys.exit(0) try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()
def get_ytm_api(self): try: ytmusic = YTMusic() return ytmusic except Exception as e: self.logger.error("Something went wrong getting Youtube Music API") self.logger.error(e) return None