<commit_before>import album
import artist
import dispatch
import schedule
import threading

pre_sync = dispatch.Signal()
post_sync = dispatch.Signal()


class RainwaveChannel(object):
    '''A :class:`RainwaveChannel` object represents one channel on the Rainwave
    network.

    .. note::

        You should not instantiate an object of this class directly, but rather
        obtain one from :attr:`RainwaveClient.channels`.
    '''

    #: The :class:`RainwaveClient` object that belongs to the channel.
    client = None

    def __init__(self, client, raw_info):
        self.client = client
        self._raw_info = raw_info
        self._do_sync = False

    def __repr__(self):
        return '<RainwaveChannel [{}]>'.format(self.name)

    def __str__(self):
        return '{}, {}'.format(self.name, self.description)

    def _do_async_get(self):
        d = self.client.call(u'/async/{}/get'.format(self.id))
        self._raw_timeline = d

    def _do_sync_thread(self):
        self._do_sync = True
        while self._do_sync:
            pre_sync.send(self)
            if hasattr(self, u'_raw_timeline'):
                d = self.client.call(u'sync/{}/sync'.format(self.id))
            else:
                d = self.client.call(u'sync/{}/init'.format(self.id))
            if self._do_sync:
                self._cache_raw_timeline(d)
                post_sync.send(self, channel=self)

    def _get_album_raw_info(self, album_id):
        args = {u'album_id': album_id}
        d = self.client.call(u'async/{}/album'.format(self.id), args)
        return d[u'playlist_album']

    def _get_artist_raw_info(self, artist_id):
        raw_info = {}
        for artist in self.artists:
            if artist.id == artist_id:
                raw_info = artist._raw_info
        args = {u'artist_id': artist_id}
        d = self.client.call(u'async/{}/artist_detail'.format(self.id), args)
        return dict(raw_info.items() + d[u'artist_detail'].items())

    def _new_schedule(self, raw_schedule):
        if raw_schedule[u'sched_type'] == 0:
            return schedule.RainwaveElection(self, raw_schedule)

    @property
    def albums(self):
        '''A list of :class:`RainwaveAlbum` objects in the playlist of the
        channel.'''

        if not hasattr(self, u'_raw_albums'):
            path = u'async/{}/all_albums'.format(self.id)
            self._raw_albums = self.client.call(path)
        if not hasattr(self, u'_albums'):
            self._albums = []
            for raw_album in self._raw_albums[u'playlist_all_albums']:
                new_album = album.RainwaveAlbum(self, raw_album)
                self._albums.append(new_album)
        return self._albums

    @property
    def artists(self):
        '''A list of :class:`RainwaveArtist` objects in the playlist of the
        channel.'''

        if not hasattr(self, u'_raw_artists'):
            path = u'async/{}/artist_list'.format(self.id)
            self._raw_artists = self.client.call(path)
        if not hasattr(self, u'_artists'):
            self._artists = []
            for raw_artist in self._raw_artists[u'artist_list']:
                new_artist = artist.RainwaveArtist(self, raw_artist)
                self._artists.append(new_artist)
        return self._artists

    @property
    def description(self):
        '''A description of the channel.'''
        return self._raw_info[u'description']

    @property
    def id(self):
        '''The ID of the channel.'''
        return self._raw_info[u'id']

    @property
    def name(self):
        '''The name of the channel.'''
        return self._raw_info[u'name']

    @property
    def oggstream(self):
        '''The URL of the OGG stream for the channel.'''
        return self._raw_info[u'oggstream']

    @property
    def schedule_current(self):
        '''The current :class:`RainwaveSchedule` for the channel.'''
        if not self._do_sync:
            self._do_async_get()
        return self._new_schedule(self._raw_timeline[u'sched_current'])

    @property
    def schedule_history(self):
        '''A list of the past :class:`RainwaveSchedule` objects for the
        channel.'''
        if not self._do_sync:
            self._do_async_get()
        sched_history = []
        for raw_sched in self._raw_timeline[u'sched_history']:
            sched_history.append(self._new_schedule(raw_sched))
        return sched_history

    @property
    def schedule_next(self):
        '''A list of the next :class:`RainwaveSchedule` objects for the
        channel.'''
        if not self._do_sync:
            self._do_async_get()
        sched_next = []
        for raw_sched in self._cache_raw_timeline[u'sched_next']:
            sched_next.append(self._new_schedule(raw_sched))
        return sched_next

    @property
    def stream(self):
        '''The URL of the MP3 stream for the channel.'''
        return self._raw_info[u'stream']

    def get_album_by_id(self, id):
        '''Returns a :class:`RainwaveAlbum` for the given album ID. Raises an<commit_msg>fix a few residual references to _cache_raw_timeline<commit_after>import album
import artist
import dispatch
import schedule
import threading

pre_sync = dispatch.Signal()
post_sync = dispatch.Signal()


class RainwaveChannel(object):
    '''A :class:`RainwaveChannel` object represents one channel on the Rainwave
    network.

    .. note::

        You should not instantiate an object of this class directly, but rather
        obtain one from :attr:`RainwaveClient.channels`.
    '''

    #: The :class:`RainwaveClient` object that belongs to the channel.
    client = None

    def __init__(self, client, raw_info):
        self.client = client
        self._raw_info = raw_info
        self._do_sync = False

    def __repr__(self):
        return '<RainwaveChannel [{}]>'.format(self.name)

    def __str__(self):
        return '{}, {}'.format(self.name, self.description)

    def _do_async_get(self):
        d = self.client.call(u'/async/{}/get'.format(self.id))
        self._raw_timeline = d

    def _do_sync_thread(self):
        self._do_sync = True
        while self._do_sync:
            pre_sync.send(self)
            if hasattr(self, u'_raw_timeline'):
                d = self.client.call(u'sync/{}/sync'.format(self.id))
            else:
                d = self.client.call(u'sync/{}/init'.format(self.id))
            if self._do_sync:
                self._raw_timeline = d
                post_sync.send(self, channel=self)

    def _get_album_raw_info(self, album_id):
        args = {u'album_id': album_id}
        d = self.client.call(u'async/{}/album'.format(self.id), args)
        return d[u'playlist_album']

    def _get_artist_raw_info(self, artist_id):
        raw_info = {}
        for artist in self.artists:
            if artist.id == artist_id:
                raw_info = artist._raw_info
        args = {u'artist_id': artist_id}
        d = self.client.call(u'async/{}/artist_detail'.format(self.id), args)
        return dict(raw_info.items() + d[u'artist_detail'].items())

    def _new_schedule(self, raw_schedule):
        if raw_schedule[u'sched_type'] == 0:
            return schedule.RainwaveElection(self, raw_schedule)

    @property
    def albums(self):
        '''A list of :class:`RainwaveAlbum` objects in the playlist of the
        channel.'''

        if not hasattr(self, u'_raw_albums'):
            path = u'async/{}/all_albums'.format(self.id)
            self._raw_albums = self.client.call(path)
        if not hasattr(self, u'_albums'):
            self._albums = []
            for raw_album in self._raw_albums[u'playlist_all_albums']:
                new_album = album.RainwaveAlbum(self, raw_album)
                self._albums.append(new_album)
        return self._albums

    @property
    def artists(self):
        '''A list of :class:`RainwaveArtist` objects in the playlist of the
        channel.'''

        if not hasattr(self, u'_raw_artists'):
            path = u'async/{}/artist_list'.format(self.id)
            self._raw_artists = self.client.call(path)
        if not hasattr(self, u'_artists'):
            self._artists = []
            for raw_artist in self._raw_artists[u'artist_list']:
                new_artist = artist.RainwaveArtist(self, raw_artist)
                self._artists.append(new_artist)
        return self._artists

    @property
    def description(self):
        '''A description of the channel.'''
        return self._raw_info[u'description']

    @property
    def id(self):
        '''The ID of the channel.'''
        return self._raw_info[u'id']

    @property
    def name(self):
        '''The name of the channel.'''
        return self._raw_info[u'name']

    @property
    def oggstream(self):
        '''The URL of the OGG stream for the channel.'''
        return self._raw_info[u'oggstream']

    @property
    def schedule_current(self):
        '''The current :class:`RainwaveSchedule` for the channel.'''
        if not self._do_sync:
            self._do_async_get()
        return self._new_schedule(self._raw_timeline[u'sched_current'])

    @property
    def schedule_history(self):
        '''A list of the past :class:`RainwaveSchedule` objects for the
        channel.'''
        if not self._do_sync:
            self._do_async_get()
        sched_history = []
        for raw_sched in self._raw_timeline[u'sched_history']:
            sched_history.append(self._new_schedule(raw_sched))
        return sched_history

    @property
    def schedule_next(self):
        '''A list of the next :class:`RainwaveSchedule` objects for the
        channel.'''
        if not self._do_sync:
            self._do_async_get()
        sched_next = []
        for raw_sched in self._raw_timeline[u'sched_next']:
            sched_next.append(self._new_schedule(raw_sched))
        return sched_next

    @property
    def stream(self):
        '''The URL of the MP3 stream for the channel.'''
        return self._raw_info[u'stream']

    def get_album_by_id(self, id):
        '''Returns a :class:`RainwaveAlbum` for the given album ID. Raises an
{'fraction_non_alphanumeric': 0.11384281200631911, 'fraction_numerical': 0.00039494470774091627, 'mean_word_length': 2.3842298696959574, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4964662', 'n_tokens_mistral': 3147, 'n_tokens_neox': 3041, 'n_words': 708}