def test_index(): slt = SortedKeyList(range(100), key=modulo) slt._reset(7) for pos, val in enumerate(sorted(range(100), key=modulo)): assert val == slt.index(pos) assert slt.index(9, 0, 1000) == 90 slt = SortedKeyList((0 for rpt in range(100)), key=modulo) slt._reset(7) for start in range(100): for stop in range(start, 100): assert slt.index(0, start, stop + 1) == start for start in range(100): assert slt.index(0, -(100 - start)) == start assert slt.index(0, -1000) == 0
def test_index_valueerror5(): slt = SortedKeyList(key=modulo) with pytest.raises(ValueError): slt.index(1)
def test_index_valueerror3(): slt = SortedKeyList([0] * 10, key=modulo) slt._reset(4) with pytest.raises(ValueError): slt.index(0, 7, 3)
def test_index_valueerror6(): slt = SortedKeyList(range(10), key=negate) slt._reset(4) with pytest.raises(ValueError): slt.index(6, 5)
def test_index_valueerror4(): slt = SortedKeyList([0] * 10, key=negate) slt._reset(4) with pytest.raises(ValueError): slt.index(1)
def test_index_valueerror10(): slt = SortedKeyList(range(10), key=modulo) slt._reset(4) with pytest.raises(ValueError): slt.index(19)
def stress_index2(slt): values = list(slt)[:3] * 200 slt = SortedKeyList(values) for idx, val in enumerate(slt): assert slt.index(val, idx) == idx
def test_index_valueerror9(): slt = SortedKeyList(key=modulo) slt._reset(4) with pytest.raises(ValueError): slt.index(5)
def test_index_valueerror7(): slt = SortedKeyList([0] * 10 + [1] * 10 + [2] * 10, key=modulo) slt._reset(4) with pytest.raises(ValueError): slt.index(1, 0, 10)
class artistCollection: """ Artist's class, each album is in a directory. """ def __init__(self, mainMusicBase): self.artists = SortedKeyList( key=attrgetter('name')) #[] #Artist Collection self.musicBase = mainMusicBase def addArtist(self, artist): #Add an artist in artists list, if artist.artistID == 0: artist.artistID = self.musicBase.db.insertArtist(artist) self.artists.add(artist) return artist def getArtist(self, artistName): newArt = artist(artistName, 0) artistList = self.findSortedArtist(newArt) if len(artistList) == 0: artistList = self.findArtists(newArt.name) if len(artistList) == 0: #If the artist is not found in artistCol, we add it and return the curArt = self.addArtist(newArt) #curArt = self.artists[len(self.artists)-1] --line to delete elif len(artistList) == 1: #If artists is found curArt = artistList[0] else: #If there is more than 1 artist, ask for the good one to user #For the moment, just return None curArt = None return curArt def findArtists(self, sname): artistList = [] for art in filterByName(self.artists, sname): artistList.append(art) return artistList def findSortedArtist(self, art): artistList = [] try: artFound = self.artists.index(art) except ValueError: artFound = None if artFound: artistList.append(artFound) return artistList def getArtistByID(self, id): for art in filterByID(self.artists, id): return art def printArtists(self): for art in self.artists: art.printInfos() def loadArtists(self): for row_art in self.musicBase.db.getSelect( "SELECT artistID, name FROM artists ORDER BY name"): art = artist(row_art[1], row_art[0]) self.addArtist(art) def artistCompare(art1, art2): return art1.name > art2.name def sortArtists(self): self.artists = sorted(self.artists, key=attrgetter('name'))
class Stream: _key: str = attr.ib() _data: List[dict] = attr.ib() meta: Dict[str, Any] = attr.ib(factory=dict) streams: List['Stream'] = attr.ib(factory=list) # for joined streams twitch: str = attr.ib(init=False) type: StreamType = attr.ib(init=False) games: List[Tuple['Game', SegmentReference]] = attr.ib(init=False) segments: List[Segment] = attr.ib(init=False) timecodes: Timecodes = attr.ib(init=False) @staticmethod def _segment_key(s) -> int: if hasattr(s, 'fallbacks') and 'offset' in s.fallbacks: offset = s.fallbacks['offset'] else: offset = s.offset() return int(offset) def __attrs_post_init__(self): self.twitch = self._key if ',' in self.twitch: self.type = StreamType.JOINED elif self.twitch.startswith('00'): self.type = StreamType.NO_CHAT else: self.type = StreamType.DEFAULT self.games = [] self.segments = SortedKeyList(key=self._segment_key) self.timecodes = Timecodes(timecodes.get(self.twitch) or {}) for segment in self._data: Segment(stream=self, **segment) # Workaround for SortedKeyList.__init__ def __new__(cls, *args, **kwargs): return object.__new__(cls) @property @cached('duration-twitch-{0[0].twitch}') def _duration(self) -> int: line = last_line(self.subtitles_path) if line is not None: return int(Timecode(line.split(' ')[2].split('.')[0])) @property def duration(self) -> Timecode: if self.type is StreamType.JOINED: return Timecode(sum(int(s.duration) for s in self.streams)) elif self.type is StreamType.NO_CHAT: return Timecode(max(int(s.abs_end) for s in self)) else: return Timecode(self._duration) @property def abs_start(self) -> Timecode: return Timecode(0) @property def abs_end(self) -> Timecode: return self.duration @property @cached('date-{0[0].twitch}') def _unix_time(self) -> str: args = ['--pretty=oneline', '--reverse', '-S', self.twitch] rev = repo.git.log(args).split(' ')[0] return repo.commit(rev).authored_date @property def date(self) -> datetime: if self.type is StreamType.JOINED: return self.streams[0].date elif self.type is StreamType.NO_CHAT: return datetime.strptime(self.twitch[2:8], '%y%m%d') else: return datetime.fromtimestamp(self._unix_time) @property def subtitles_prefix(self) -> str: """Returns public URL prefix of subtitles for this segment.""" year = str(self.date.year) key = f'$PREFIX/chats/{year}' if key not in config['repos']['mounts']: raise Exception(f'Repository for year {year} is not configured') prefix = config['repos']['mounts'][key]['prefix'] return prefix @property def subtitles(self) -> str: """Returns full public URL of subtitles for this stream.""" if self.type is StreamType.NO_CHAT: return None return f'{self.subtitles_prefix}/v{self.twitch}.ass' @property def subtitles_path(self) -> str: """Returns relative path of subtitles in current environment.""" return _(f'chats/{self.date.year}/v{self.twitch}.ass') @property def subtitles_style(self) -> SubtitlesStyle: style = SubtitlesStyle(tcd_config['ssa_style_format'], tcd_config['ssa_style_default']) if self.meta.get('chromakey'): style['Alignment'] = '5' else: style['Alignment'] = '1' return style @cached_property def blacklist(self) -> BlacklistTimeline: bl = BlacklistTimeline() for segment in self: for subref in segment.all_subrefs: bl.add(subref.blacklist, subref.abs_start, subref.abs_end) return bl @property @cached('messages-{0[0].twitch}') def _messages(self) -> int: lines = count_lines(self.subtitles_path) return (lines - 10) if lines else None @property def messages(self) -> int: if self.type is StreamType.JOINED: return sum([s.messages for s in self.streams]) else: return self._messages or 0 def __getitem__(self, index: int) -> Segment: return self.segments[index] def __contains__(self, segment: Segment) -> bool: return segment in self.segments def __len__(self) -> int: return len(self.segments) def index(self, segment: Segment) -> int: return self.segments.index(segment) def add(self, segment: Segment): self.segments.add(segment) def remove(self, index: int): self.segments.remove(index) @join() def to_json(self) -> str: if len(self) > 1: yield '[\n' first = True for segment in self: if not first: yield ',\n' else: first = False yield indent(segment.to_json(), 2) yield '\n]' else: yield self[0].to_json() def __str__(self) -> str: return self.to_json()