Beispiel #1
0
def start():
    global index
    global player
    global playing
    global first_time
    global flag
    try:
        song_name=onlyfiles[index].replace(".mp3","")
        label.configure(text=song_name)
        player=MediaPlayer(join(file_dir,onlyfiles[index]))
        player.play()
        time.sleep(1)
        playing = True
        first_time = False
        button2.configure(image=pausePh)
        millis=get_len(player)
        seconds = (millis / 1000) % 60
        seconds = int(seconds)
        minutes = (millis / (1000 * 60)) % 60
        minutes = int(minutes)
        timeformat = '{:02d}:{:02d}'.format(minutes, seconds)
        labelpos.configure(text='Total-' + timeformat)
        flag=True
        t1=threading.Thread(target=position,args=(millis,))
        t1.start()
    except(IndexError):
        index=0
        start()
Beispiel #2
0
def toggle_playing():
    global p
    if p.is_playing():
        p.pause()
    else:
        p.play()

    # We need the next part to start the song again, as soon as the song finishes
    if p.get_time() == 0:
        p.stop()
        p = MediaPlayer(song_path)
        p.play()
Beispiel #3
0
def playMessage(msg):
    """
        Cria um arquivo na pasta audios,
        dizendo uma mensagem passada por parametro
        espera 15s e deleta o arquivo
    """
    tts = gTTS(msg, lang="pt-br")
    file = "./audios/temp.mp3"

    tts.save(file)
    player = MediaPlayer(file)
    player.play()
    sleep(10)
    os.remove(file)
Beispiel #4
0
	def insert_alert(self, id, msg):
		# el try catch es porque self.alerts no existe en el 
		# estado inicial
		s_timespamp = datetime.now().strftime('%d-%m-%Y %I:%M %p')
		try:
			self.alerts.append((id, s_timespamp, msg))
		except:
			self.alerts = [(id, s_timespamp, msg)]

		s = ""
		for a in self.alerts:
			# dejo el s[0] para debugging
			s += str(a[1]) + " - " + str(a[2]) + "\n"
		self.txtArea.setPlainText( s )
		tts = gTTS(text=msg, lang='es')
		tts.save("msg.mp3")
		p = MediaPlayer("msg.mp3")
		p.play()
			
		return id
Beispiel #5
0
class Stream:

	def __init__(self, url):
		self.player = MediaPlayer(url)
		self.stream_url = url
		self._volume = 0
		self.player.audio_set_volume(self._volume)
		self.player.play()
		self.timer = None

	@property
	def volume(self):
		return self._volume

	@volume.setter
	def volume(self, val):
		assert int(val) == val
		self._volume = int(val)
		self.set_volume(int(val))

	def grad_change_vol(self, d_vol, time):
		mod = d_vol - self.volume
		if not mod == 0:
			one_step_mod = mod/abs(mod)
			secs_per_step=time/abs(mod)
			for _ in range(abs(mod)):
				self.volume += one_step_mod
				sleep(secs_per_step)

	def set_volume(self, vol):
		self.player.audio_set_volume(vol)

	def set_sleep_timer(self, sleep_timer_class=My_Sleep_Timer):
		if self.timer != None:
			self.timer.terminate()
		self.timer = sleep_timer_class(self)
		self.timer.start()

	def quit(self):
		self.timer.terminate()
		self.player.stop()
Beispiel #6
0
class AudioFile():
    """
    Class for the audiofile.
    Provides all methods to interact with the audio:
    play, check answer, ...
    """
    def __init__(self):
        self._filename = _read_file()
        self._audio = MediaPlayer(AUDIO_DIR + "/" + self.filename)
        self.play()

    def play(self):
        """play the audio of the file"""
        self._audio.stop()
        self._audio.play()
        logger.info('playing ' + self.filename)

    def check_answer(self, answer):
        """
        Check if the provided answer is correct.
        The format should be sound + tone and no seperator between syllables
        ie shang4hai3
        """
        return answer == self._filename.split("__")[0].replace("_", "")

    def get_pinyin(self):
        return get_pinyin(self.filename)

    def get_id(self):
        return get_id(self.filename)

    def get_extension(self):
        return get_extension(self.extension)

    def __del__(self):
        self._audio.release()

    @property
    def filename(self):
        return self._filename
class AudioPlayer:
    def __init__(self):
        self.current_audio = MediaPlayer()

    def play(self, audio_path):
        self.audio_path = audio_path
        self.current_audio = MediaPlayer('file://' + audio_path)
        self.current_audio.play()

    def pause(self):
        self.current_audio.set_pause(True)

    def is_playing(self):
        return self.current_audio.is_playing()

    def resume(self):
        self.current_audio.set_pause(False)

    def set_volume(self, volume):
        volume = int(volume)
        self.current_audio.audio_set_volume(volume)

    def seek(self, target):
        self.current_audio.set_position(target)

    def seek_information(self):
        return (self.current_audio.get_position(), 1.0)

    def remaining(self):
        ms_left = (self.current_audio.get_length() -
                   self.current_audio.get_time()) / 1000

        return '-' + str(int(ms_left / 60)) + ':' + str(int(
            ms_left % 60)).zfill(2)

    def replay(self):
        self.play(self.audio_path)
Beispiel #8
0
def music_fa():
    """this def for get music fa"""
    name_artist = args.n
    famle_artist = args.f
    track_name_artist = args.track
    url = "https://www.radiojavan.com/search?query="+name_artist+"+"+famle_artist+"+"+track_name_artist
    url_req = requests.get(url).text
    soup = BeautifulSoup(url_req, "lxml")
    soup_name_artist = soup.find("span", class_="artist_name")
    soup_name_track = soup.find("span", class_="song_name")

    get_txt_name = get_text(str(soup_name_artist))
    get_txt_track = get_text(str(soup_name_track))
    get_txt_name = get_txt_name.replace(" ", "-")
    get_txt_track = get_txt_track.replace(" ", "-")
    url_ext = url_download+get_txt_name+"-"+get_txt_track+".mp3"
    url_ext_2 = url_download_2+get_txt_name+"-"+get_txt_track+".mp3"
    url_ext_3 = url_download_3+get_txt_name+"-"+get_txt_track+".mp3"
    url_ext_3 = url_ext_3.replace("-&", "")
    url_ext_2 = url_ext_2.replace("-&", "")
    url_ext = url_ext.replace("-&", "")
    

    #show play list track artist




    try:
        #checkd url steram found or not found!
        u = urlopen(url_ext_2).readline()
        if u == b'Not found':
            if args.download:
                print("download done!")
                download(url_ext)
                exit()
            msg_player = "Mix Player\n"
            msg = " start play music "
            rows, columns = popen('stty size', 'r').read().split() # get size terminal
            msg_x = msg.center(int(columns))
            msg_y = msg_player.center(int(columns))
            system("clear")
            echo(style(msg_y, blink=True, bold=True, fg="red"))
            echo(style(msg_x, blink=True, bold=True, fg="red"))
            name = "\n\n\nartist: "+get_txt_name
            track = "\ntrack: "+get_txt_track
            echo(style(name, bold=True,fg="reset",))
            echo(style(track, bold=True,fg="reset"))
            soup_name_track_all = soup.find_all("span", class_="song_name")
            j = 0
            Music5 = " - - -  - - 5 New music - - - - - "
            Music5 = Music5.center(int(columns))
            echo(style(Music5, bold=True,fg="reset"))
            track_5 = soup_name_track_all[0:5]
            for i in track_5:
                global playlist_arti
                global playlist_artist_2
                get_txt_track_all = get_text(str(i))
                get_txt_track_all = get_txt_track_all.replace(" ", "-")
                all_tk = get_txt_track_all.center(int(columns))
                j = j+1
                print("\n\n",j,"-")
                echo(style(all_tk, bold=True,fg="reset"))
            MediaPlay2 = MediaPlayer(url_ext)
            MediaPlay2.play()
            sleep(240) # sleep for play muisc
           
        else:
            if args.download:
                    download(url_ext_2)
                    print("download done!")
                    exit()
            msg_player = "------------Mix Player-----------\n"
            msg = " start play music  \n\n"
            rows, columns = popen('stty size', 'r').read().split()
            
            msg_x = msg.center(int(columns))
            msg_y = msg_player.center(int(columns))
            system("clear")
            echo(style(msg_y, blink=True, bold=True,fg="red"))
            echo(style(msg_x, blink=True, bold=True,fg="red"))
            name  = "\n\n\nartist: "+get_txt_name
            track = "\ntrack: "+get_txt_track
            echo(style(name, bold=True,fg="reset"))
            echo(style(track, bold=True,fg="reset"))
            soup_name_track_all = soup.find_all("span", class_="song_name")
            j = 0
            Music5 = " - - -  - - 5 New music - - - - - "
            Music5 = Music5.center(int(columns))
            echo(style(Music5, bold=True,fg="reset"))
            track_5 = soup_name_track_all[0:5]
            for i in track_5:
                global playlist_arti
                global playlist_artist_2
                get_txt_track_all = get_text(str(i))
                get_txt_track_all = get_txt_track_all.replace(" ", "-")
                all_tk = get_txt_track_all.center(int(columns))
                j = j+1
                print("\n\n",j,"-")
                echo(style(all_tk, bold=True,fg="reset"))
            MediaPlay = MediaPlayer(url_ext_2) # strt stream muisc
            MediaPlay.play()
            sleep(240)
        
    
            # def PlaylistArtist():
                # for track_in_playlist in soup_name_track_all:
                    # playlists = get_text(str(track_in_playlist))
                    # print(playlists)





    except BaseException:
        system("clear")
        print("Exit Mix Player")
Beispiel #9
0
            if not os.path.exists(mp3_dir):
                os.makedirs(mp3_dir, exist_ok=True)
            if not os.path.exists(wav_dir):
                os.makedirs(wav_dir, exist_ok=True)

            if to_say in overrides.keys():
                to_say = overrides[to_say]
            tts = gTTS(to_say.replace("-", " "), lang=language)
            tts.save(mp3)

            sound = AudioSegment.from_mp3(mp3)
            sound.export(wav, format="wav")

            if playback:
                player = MediaPlayer(wav)
                player.play()
                time.sleep(0.1)
                duration = player.get_length() / 1000
                print(file_basename + " (" + str(duration) + "s)")
                time.sleep(duration)
            else:
                print(mp3)

        except KeyboardInterrupt:
            print("\n-- Ctrl^C ---")
            break

        except Exception:
            traceback.print_exc()
            break
Beispiel #10
0
class Player:
    """The class provides basic media playback functions.
    
    The class provides functions to load local media and play it.
    """
    def __init__(self):
        """Initializes empty class variables.
        
        The class initializes the following class variables:
        vlc_player      The variable, which is used to play music via an
                        instance of a vlc.MediaPlayer. It is initialized with
                        'None'.
        wish_list       A list of file paths, that determine the files that
                        are left to play. It is initialized as an empty list.
        playing_thread  A thread that calls 'self.play()' and which is joined in
                        the destructor. It is initialized and started.
        keep_playing    A boolean that signals the method 'self.play()' and thus
                        the thread 'self.playing_thread' when to stop the
                        playback and prepare to be destructed. It is initialized
                        with 'True'.
        is_paused       The boolean signals the method 'play()' when the current
                        playback is paused and is toggled by the method
                        'self.pause()'. It is initialized with 'False'..


        Semaphores
        ----------
        change_player_list  A 'threading.Semaphore' that should be acquired and
                            released when ever 'self.wish_list' or
                            'self.wish_list'.
        """
        self.vlc_player = MediaPlayer()
        self.change_player_list = Semaphore()
        self.wish_list = list()
        self.keep_playing = True
        self.is_paused = False
        self.playing_thread = Thread(target=self.play)
        self.playing_thread.start()

    def __del__(self):
        """The destructor prepares the object to be destructed, by joining all
        threads and stopping the music.
        """
        if isinstance(self.vlc_player, MediaPlayer):
            self.vlc_player.stop()

        # wait for the playing_thread to terminate
        self.keep_playing = False
        self.playing_thread.join()

        # delet created instance variables
        del self.vlc_player
        del self.keep_playing
        del self.is_paused
        del self.playing_thread
        del self.change_player_list

    def pause(self):
        """Pauses or resumes playing music.
        
        Calling this method toggles the 'pause' function of 'self.vlc_player' in
        case it is an instance of 'vlc.MediaPlayer'. Also the instance variable
        'self.is_paused' toggled, such that the method 'self.play()'
        recognizes that the current title is paused.
        """
        if isinstance(vlc_player, MediaPlayer):
            self.vlc_player.pause()
            if self.keep_playing:
                self.is_paused = False
            else:
                self.is_paused = True

    def play(self):
        """The method starts media playback and continues until
        'self.keep_playing' is set to False.

        The method starts media playback for file paths mentioned in
        'self.wish_list'. For the playback the instance variable
        'self.vlc_player', which declared as a 'MediaPlayer' in case it is of
        any other type while there is at least one element in 'self.wish_list'.
        When all elements of 'self.wish_list' are played or removed, the method
        waits until new elements are added to 'self.wish_list'.
        The method only returns if the variable 'self.keep_playing' is set to
        False.

        In order to access and edit 'self.vlc_player' the 'Semaphore'
        'self.change_player_list' is acquired and released when ever
        'self.vlc_player' or 'self.wish_list' is edited.
        """
        seconds_to_wait = 0.5
        while self.keep_playing:
            if not self.is_paused:
                # self is expected to play until 'self.wish_list' is empty
                if len(self.wish_list) > 0:
                    # there are currently some songs to play
                    if isinstance(self.vlc_player, MediaPlayer):
                        # the 'vls_player' is initialized with the expected type
                        if self.vlc_player.is_playing():
                            # currently playing, nothing need to happen,
                            # so it can sleep to spare some resources
                            time.sleep(seconds_to_wait)
                        else:
                            # there is no song playing at the moment, but there are
                            # tracks in 'self.wish_list' that still needs to be
                            # played

                            self.change_player_list.acquire()

                            # load the media for the next song into vlc_player
                            self.vlc_player.set_media(Media(self.wish_list[0]))
                            self.vlc_player.play()
                            # remove just loaded song from the wish_list
                            self.wish_list.remove(self.wish_list[0])

                            self.change_player_list.release()

                # the length of 'self.wish_list' is not greater than 0, so there
                # is no song to play next.
                else:
                    time.sleep(seconds_to_wait)
            else:
                # self is expected to pause, until the user requests to continue
                # the playback
                time.sleep(seconds_to_wait)

    def skip(self):
        """The method does not finish the current track but starts playing the
        next title in 'self.wish_list'.

        In case 'self.vlc_player' is an instance of 'MediaPlayer' and
        there is at least one element in 'self.wish_list' the method
        waits to get the rights to change 'self.vlc_player' by acquiring
        'self.change_player_list'. After that, the method loads the first element
        of 'self.wish_list', loads that as new 'vlc.Media' for the
        'self.vlc_player' and starts playing the new media. After that the
        'self.change_player_list' is released.

        In order to access and edit 'self.vlc_player' the 'Semaphore'
        'self.change_player_list' is acquired and released when ever
        'self.vlc_player' or 'self.wish_list' is edited.
        """
        if isinstance(self.vlc_player, MediaPlayer):
            if len(self.wish_list) > 0:
                self.change_player_list.acquire()

                # edit vlc_player
                self.vlc_player.set_media(Media(self.wish_list[0]))
                self.vlc_player.play()
                # edit wish_list
                self.wish_list.remove(self.wish_list[0])

                self.change_player_list.release()

    def mute(self):
        """The method toggles the mute of the audio output of the media
        player 'self.vlc_player'.

        When 'self.vlc_player' is an instance of 'MediaPlayer', the mothed
        toggles the method 'self.vlc_player.audio_toggle_mute()'.
        """
        if isinstance(self.vlc_player, MediaPlayer):
            self.vlc_player.audio_toggle_mute()

    def queue(self, file_path):
        """The method adds the string 'file_path' to the end of the list 
        'self.wish_list' and starts playing.

        If the given parameter 'file_path' is of the type 'str' and a valid file
        path, it is appended to the list 'wish_list'.

        In order to access and edit the 'self.wish_list' the 'Semaphore'
        'self.change_player_list' is acquired and released when 'file_path' is
        added to 'self.wish_list'.

        Parameters:
        -----------
        file_path   A str of the file path towards a destination file that
                    should be played by the player instance and which is added
                    to 'self.wish_list'.
        """
        if isinstance(file_path, str) and os.path.isfile(file_path):
            self.change_player_list.acquire()
            # add new media file to wish_list
            self.wish_list.append(file_path)

            self.change_player_list.release()
Beispiel #11
0
class SoundService:

    __instance = None

    # player = None

    def __init__(self):
        # self.player = None
        self.player = None
        self.vlc_instance = Instance
        self.playing_radio = False
        self.playing_alarm = False

        # pass

    def get_player(self):
        return self.player

    def set_player(self, player):
        self.player = player

    @staticmethod
    def get_instance():
        if SoundService.__instance is None:
            SoundService.__instance = SoundService()
        return SoundService.__instance

    def play_default_alarm_sound(self):
        self.play_alarm_sound(DEFAULT_ALARM_FILEPATH)

    def play_alarm_sound(self, filepath):
        # here we use a MediaListPlayer instance as it offers the repeat functionality
        self.player = MediaListPlayer()
        self.player.set_playback_mode(PlaybackMode.loop)
        media_player = Media(filepath)
        if not FileService.file_exists(filepath):
            print(
                'The alarm could not be played. Fallback to well known default alarm'
            )
            media_player = Media(DEFAULT_ALARM_FILEPATH)
        media_list = MediaList()
        media_list.add_media(media_player)
        self.player.set_media_list(media_list)

        result = self.player.play()
        if result == -1:
            # TODO test this
            print('Something went wrong')

        self.playing_alarm = True

    def stop_alarm_sound(self):
        self.player.stop()
        self.playing_alarm = False

    def pause_alarm_sound(self):
        self.player.pause()
        self.playing_radio = False

    def play_webradio(self, url):
        self.player = MediaPlayer(url)
        self.player.play()
        self.playing_radio = True

    def pause_webradio(self):
        self.player.pause()
        self.playing_radio = False

    def stop_webradio(self):
        self.player.stop()
        self.playing_radio = False

    def pause_playing(self):
        print('Pausing alarm')
        if self.playing_radio:
            self.pause_webradio()
        else:
            self.pause_alarm_sound()

    def stop_playing(self):
        print('Stopping alarm')
        if self.playing_radio:
            self.stop_webradio()
        else:
            self.stop_alarm_sound()
Beispiel #12
0
p = MediaPlayer(r'$VLC_PLAYABLE_AUDIO_FILE_LOCATION')
f = open(r'$LRC_FILE_LOCATION', 'r', encoding = 'utf-16le')

timecodes_ms = []
texts = []
for item in f.readlines():
	m = re.fullmatch('\[(?P<mm>\d\d):(?P<ss>\d\d).(?P<msms>\d\d)\](?P<txt>[\S ]+)?', item.strip())
	if m:
		if m.group('txt') != None:
			texts.append(m.group('txt'))
		else:
			texts.append('')
			
		timecodes_ms.append( (int(m.group('mm'))*60000) + (int(m.group('ss'))*1000) + (int(m.group('msms'))))
init = False
p.play()
while p.get_state() != State.Ended and p.get_state() != State.NothingSpecial:
	if p.get_state() != State.Opening:
		if init is False:
			duration = p.get_length()
			init = True
		pos_in_ms = duration*(p.get_position())
		if len(timecodes_ms)>0:
			if pos_in_ms>timecodes_ms[0]:
				if len(texts) == 1 and texts[0] == '':
					print('\n[THE END]\n(waiting for song file to end)')
				else:
					print(texts[0])
				del texts[0]
				del timecodes_ms[0]
	else:
Beispiel #13
0
class Song:
    """Represents a song in the library.
    """

    ID3_COLUMNS = ("title", "artist", "album", "genre", "year")
    NON_ID3_COLUMNS = ("length", "date_modified")

    def __init__(self, file_path, title = None, artist = None, album = None, genre = None, year = None, override_id3 = True):
        """ Given an absolute file path, and data about the song a initialize a Song object. Parses ID3 tags for additional metadata if it exists. If
        the override_id3 is true, the given name and artist will override the name and artist contained in the ID3 tag.

        @param file_path: str
        @param title: str
        @param artist: str
        @param album: str
        @param genre: str
        @param year: int
        @param override_id3: bool
        """
        self._file_path = file_path
        self._mp = None
        self._time = None # What time, in seconds, of the song playback to play at

        # Fill in column values, first by parsing ID3 tags and then manually
        self._columns = {}
        for tag_name, tag in zip(Song.ID3_COLUMNS, Song._get_ID3_tags(file_path)):
            self._columns[tag_name] = tag
        self._columns["length"] = int(MP3(file_path).info.length + 0.5) # Read length and round to nearest integer
        self._columns["date_modified"] = Song.get_date_modified(file_path)

        # If overriding, only do so for passed parameters
        if override_id3:
            self._columns["title"] = title if title is not None else self._columns["title"]
            self._columns["artist"] = artist if artist is not None else self._columns["artist"]
            self._columns["album"] = album if album is not None else self._columns["album"]
            self._columns["genre"] = genre if genre is not None else self._columns["genre"]
            self._columns["year"] = year if year is not None else self._columns["year"]

    def init(self):
        if self._mp is None: # Only initialize if not already initialized
            self._mp = MediaPlayer(self._file_path)

    def play(self, sleep_interval = 0.1):
        """ Plays this song.
        """
        # Create the MediaPlayer on demand to save system resources (and prevent VLC from freaking out).
        if self._mp is None:
            raise SongException("Song not initialized")

        self._mp.play()

        if self._time is not None:
            self._mp.set_time(int(self._time * 1000)) # Seconds to milliseconds
            self._time = None

        # Sleep a bit to allow VLC to play the song, so self.playing() returns properly
        time.sleep(sleep_interval)

    def pause(self):
        """ Pauses this song, if it's playing.
        """
        if self._mp is None:
            raise SongException("Song not initialized")

        self._mp.pause()

    def set_time(self, time):
        """ Sets the current play time of this song, so when the song is played (or if it's currently played)
        it will play from that time, or start playing from that time now if the song is currently playing. Given 
        time should be in seconds.

        @param time: int or float
        """
        if time < 0:
            raise SongException("Can't jump to negative timestamp")

        if time < self._columns["length"]:
            self._time = time
            if self.playing():
                self.stop()
                self.init()
                self.play()

    def get_current_time(self):
        """ Returns the current play time, in seconds, of this song, if it's playing.

        @return float
        """
        if self.playing():
            return self._mp.get_time() / 1000

    def set_volume(self, percentage):
        """ Sets the volume to the given percentage (between 0 and 100).

        @param percentage: int
        """
        if self._mp is None:
            raise SongException("Song not initialized")
        elif percentage < 0 or percentage > 100:
            raise SongException("Percentage out of range")

        if self.playing():
            self._mp.audio_set_volume(percentage)

    def get_volume(self):
        """ Returns the current volume of the song, if it's playing.

        @return int
        """
        if self._mp is None:
            raise SongException("Song not initialized")

        if self.playing():
            return self._mp.audio_get_volume()

    def mute(self):
        """ Mutes the song, if it's playing.
        """
        if self._mp is None:
            raise SongException("Song not initialized")

        if self.playing():
            self._mp.audio_set_mute(True)

    def unmute(self):
        """ Unmutes the song, if it's playing and is muted.
        """
        if self._mp is None:
            raise SongException("Song not initialized")

        if self.playing():
            self._mp.audio_set_mute(False)

    def playing(self):
        """ Returns if this song is playing or not (ie currently paused).

        @return: bool
        """
        if self._mp is None:
            raise SongException("Song not initialized")
        
        return self._mp.is_playing()

    def reset(self):
        """ Resets the song to the beginning.
        """
        if self._mp is None:
            raise SongException("Song not initialized")

        self._mp.stop()

    def stop(self):
        """ Terminates this song, freeing system resources and cleaning up.
        """
        if self._mp is not None:
            self._mp.stop()
            self._mp = None

    def delete_from_disk(self):
        """ Deletes this song from the hard drive, returning if the deletion was successful.

        @return bool
        """
        os.remove(self._file_path)

    def set_ID3_tag(tag, value):
        """ Sets this song's ID3 tag to the given value, returning if the set operation succeeded.
        
        @param tag: str
        @param value: str
        
        @return bool
        """
        if tag not in ID3_COLUMNS:
            return False

        tags = EasyID3(self.file_path)
        tags[tag] = value
        tags.save()
        return True

    @staticmethod
    def _get_ID3_tags(file_path):
        """ Given a file path to an mp3 song, returns the ID3 tags for title, artist, album, genre, and year (in that order), or 
        empty tuple if no tags are found.

        @param filename: str

        @return: tuple of ID3 tags
        """
        ret = [None for _ in range(len(Song.ID3_COLUMNS))]
        try:
            tags = EasyID3(file_path)
            for tag in Song.ID3_COLUMNS:
                if tag in tags:
                    ret.append(tags[tag][0])
                else:
                    ret.append(None)
        except ID3NoHeaderError:
            pass
        
        if not ret[Song.ID3_COLUMNS.index("title")]:
            ret[Song.ID3_COLUMNS.index("title")] = file_path.split("/")[-1][: -4] # Parse file name from absolute file path and delete file extension

        return tuple(ret)

    @staticmethod
    def get_date_modified(file_path):
        """ Gets the date modified of the file indicated by the given file path.

        @param file_path: str

        @return: datetime.datetime
        """
        return datetime.fromtimestamp(os.path.getmtime(file_path))


    @staticmethod
    def set_date_modified(file_path, date):
        """
        Sets the "date modified" of a file.

        @param file_path: str
        @param date: datetime.datetime
        """
        os.utime(file_path, (0, time.mktime(date.timetuple())))

    def __str__(self):
        ret = self["title"]

        if self["artist"] is not None:
            ret += " - " + self["artist"]

        return ret

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return False

        for col in self._columns:
            if col != "date_modified" and self[col] != other[col]: # Don't check if 'date modified' columns match
                return False

        return True

    # Getters, setters below

    def __getitem__(self, key):
        return self._columns[key]

    def __contains__(self, item):
        return item in self._columns