Returns: list : The user's loved projects
(self, *, limit=40, offset=0, get_full_project: bool = False)
| 580 | return commons.parse_object_list(_projects, project.Project, self._session) |
| 581 | |
| 582 | def loves(self, *, limit=40, offset=0, get_full_project: bool = False) -> list[project.Project]: |
| 583 | """ |
| 584 | Returns: |
| 585 | list<projects.projects.Project>: The user's loved projects |
| 586 | """ |
| 587 | # We need to use beautifulsoup webscraping so we cant use the api_iterative function |
| 588 | if offset < 0: |
| 589 | raise exceptions.BadRequest("offset parameter must be >= 0") |
| 590 | if limit < 0: |
| 591 | raise exceptions.BadRequest("limit parameter must be >= 0") |
| 592 | |
| 593 | # There are 40 projects on display per page |
| 594 | # So the first page you need to view is 1 + offset // 40 |
| 595 | # (You have to add one because the first page is idx 1 instead of 0) |
| 596 | |
| 597 | # The final project to view is at idx offset + limit - 1 |
| 598 | # (You have to -1 because the index starts at 0) |
| 599 | # So the page number for this is 1 + (offset + limit - 1) // 40 |
| 600 | |
| 601 | # But this is a range so we have to add another 1 for the second argument |
| 602 | pages = range(1 + offset // 40, 2 + (offset + limit - 1) // 40) |
| 603 | _projects = [] |
| 604 | |
| 605 | for page in pages: |
| 606 | # The index of the first project on page #n is just (n-1) * 40 |
| 607 | first_idx = (page - 1) * 40 |
| 608 | |
| 609 | with requests.no_error_handling(): |
| 610 | page_content = requests.get( |
| 611 | f"https://scratch.mit.edu/projects/all/{self.username}/loves/?page={page}", |
| 612 | headers=self._headers, |
| 613 | ).content |
| 614 | |
| 615 | soup = BeautifulSoup(page_content, "html.parser") |
| 616 | |
| 617 | # We need to check if we are out of bounds |
| 618 | # If we are, we can jump out early |
| 619 | # This is detectable if Scratch gives you a '404' |
| 620 | |
| 621 | # We can't just detect if the 404 text is within the whole of the page content |
| 622 | # because it would break if someone made a project with that name |
| 623 | |
| 624 | # This page only uses <h1> tags for the 404 text, so we can just use a soup for those |
| 625 | h1_tag = soup.find("h1") |
| 626 | if h1_tag is not None: |
| 627 | # Just to confirm that it's a 404, in case I am wrong. It can't hurt |
| 628 | if "Whoops! Our server is Scratch'ing its head" in h1_tag.text: |
| 629 | break |
| 630 | |
| 631 | # Each project element is a list item with the class name 'project thumb item' so we can just use that |
| 632 | for i, project_element in enumerate(soup.find_all("li", {"class": "project thumb item"})): |
| 633 | # Remember we only want certain projects: |
| 634 | # The current project idx = first_idx + i |
| 635 | # We want to start at {offset} and end at {offset + limit} |
| 636 | |
| 637 | # So the offset <= current project idx <= offset + limit |
| 638 | if offset <= first_idx + i <= offset + limit: |
| 639 | # Each of these elements provides: |