Skip to content

Conversation

@prime399
Copy link

@prime399 prime399 commented Dec 18, 2025

Summary by Sourcery

Extend URL shortening and Mega download handling with additional services and subfolder support.

New Features:

  • Add support for multiple new third-party URL shortener services in the short_url helper.
  • Allow Mega links to specify an optional subfolder path suffix to target a specific folder or file within a shared folder.
  • Support downloading from Mega folder links via the folder API when a sub-path is resolved.

Enhancements:

  • Improve Mega download logic to reuse a parsed Mega link across link-type checks and API calls.

rjriajul and others added 13 commits March 11, 2025 21:24
* perf: restructured code and fixed yt-dlp download issue
* feat: added Metadata Feature
* chore(release): v1.4.0-x (final end version)

Signed-off-by: Riajul <93116400+rjriajul@users.noreply.github.com>
Co-authored-by: clyfly <denwahgenshin@gmail.com>
Co-authored-by: SilentDemonSD <105407900+SilentDemonSD@users.noreply.github.com>
…ents.com, reel2earn.com, shortner.in, and thalashort.com
This is the final update to the master branch. From now on, the master branch is deprecated — no further updates, pull requests, or pushes will be accepted.

The master branch is now officially retired.

Signed-off-by: Riajul <93116400+rjriajul@users.noreply.github.com>
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 18, 2025

Reviewer's Guide

Extends URL shortener support for multiple new providers and enhances MEGA download handling to support sub-path selection within folder links and proper folder_api usage for downloads.

Sequence diagram for enhanced MEGA folder download with sub-path selection

sequenceDiagram
    actor User
    participant Listener
    participant add_mega_download
    participant AsyncMega
    participant MegaApi_api
    participant MegaApi_folder
    participant MegaListener

    User->>Listener: Provide mega_link (optional sub_path using pipe)
    Listener->>add_mega_download: add_mega_download(listener, path)

    add_mega_download->>add_mega_download: Parse listener.link into mega_link and sub_path
    add_mega_download->>AsyncMega: Create AsyncMega instance
    add_mega_download->>MegaApi_api: Create api MegaApi instance
    add_mega_download->>AsyncMega: Assign api to AsyncMega
    add_mega_download->>MegaListener: Attach MegaListener to api

    add_mega_download->>AsyncMega: async_api.login(MEGA_EMAIL, MEGA_PASSWORD)

    alt mega_link is file
        add_mega_download->>AsyncMega: getPublicNode(mega_link)
        AsyncMega-->>MegaListener: Set public_node
        add_mega_download->>add_mega_download: node = mega_listener.public_node
    else mega_link is folder
        add_mega_download->>MegaApi_folder: Create folder_api MegaApi instance
        add_mega_download->>AsyncMega: Assign folder_api to async_api
        add_mega_download->>MegaApi_folder: folder_api.addListener(mega_listener)
        add_mega_download->>AsyncMega: run(folder_api.loginToFolder, mega_link)
        AsyncMega-->>MegaListener: Set node for folder root
        add_mega_download->>add_mega_download: node = mega_listener.node
    end

    opt sub_path provided and link is folder
        add_mega_download->>add_mega_download: resolve_subnode(api, node, sub_path)
        add_mega_download-->>add_mega_download: node set to sub_path node or None
        alt node is None
            add_mega_download->>Listener: on_download_error(Subfolder not found)
            add_mega_download->>AsyncMega: logout()
            add_mega_download-->>Listener: Return
        else node found
            add_mega_download->>add_mega_download: Log resolved node name
        end
    end

    alt mega_listener.error is set
        add_mega_download->>Listener: on_download_error(mega_listener.error)
        add_mega_download->>AsyncMega: logout()
        add_mega_download-->>Listener: Return
    else no listener error
        add_mega_download->>add_mega_download: Prepare download path and name
        alt folder_api exists
            add_mega_download->>AsyncMega: run(folder_api.startDownload, node, path, listener.name, None, False, None)
        else file or non folder_api
            add_mega_download->>AsyncMega: startDownload(node, path, listener.name, None, False, None)
        end
        AsyncMega-->>Listener: Download completes
        add_mega_download->>AsyncMega: logout()
    end
Loading

Flow diagram for extended short_url provider handling

flowchart TD
    A[Start short_url with longurl and _shortener] --> B[Is provider modijiurl.com]
    B -- Yes --> B1[Encode longurl as long_url]
    B1 --> B2[Call modijiurl api with api and long_url]
    B2 --> B3[Check result status]
    B3 -- error --> B4[Log error message and return longurl]
    B3 -- success --> B5[Return result shortenedUrl]

    B -- No --> C[Is provider linkshortify.com]
    C -- Yes --> C1[Encode longurl as long_url]
    C1 --> C2[Call linkshortify api]
    C2 --> C3[Check result status]
    C3 -- error --> C4[Log error message and return longurl]
    C3 -- success --> C5[Return result shortenedUrl]

    C -- No --> D[Is provider inshorturl.com]
    D -- Yes --> D1[Encode longurl as long_url]
    D1 --> D2[Call inshorturl api]
    D2 --> D3[Check result status]
    D3 -- error --> D4[Log error message and return longurl]
    D3 -- success --> D5[Return result shortenedUrl]

    D -- No --> E[Is provider vplink.in]
    E -- Yes --> E1[Encode longurl as long_url]
    E1 --> E2[Call vplink api with alias CustomAlias]
    E2 --> E3[Check result status]
    E3 -- error --> E4[Log error message and return longurl]
    E3 -- success --> E5[Return result shortenedUrl]

    E -- No --> F[Is provider papajiurl.com]
    F -- Yes --> F1[Encode longurl as long_url]
    F1 --> F2[Call papajiurl api]
    F2 --> F3[Check result status]
    F3 -- error --> F4[Log error message and return longurl]
    F3 -- success --> F5[Return result shortenedUrl]

    F -- No --> G[Is provider linkcents.com]
    G -- Yes --> G1[Encode longurl as long_url]
    G1 --> G2[Call linkcents api]
    G2 --> G3[Check result status]
    G3 -- error --> G4[Log error message and return longurl]
    G3 -- success --> G5[Return result shortenedUrl]

    G -- No --> H[Is provider reel2earn.com]
    H -- Yes --> H1[Encode longurl as long_url]
    H1 --> H2[Call reel2earn api]
    H2 --> H3[Check result status]
    H3 -- error --> H4[Log error message and return longurl]
    H3 -- success --> H5[Return result shortenedUrl]

    H -- No --> I[Is provider shortner.in]
    I -- Yes --> I1[Encode longurl as long_url]
    I1 --> I2[Call shortner api]
    I2 --> I3[Check result status]
    I3 -- error --> I4[Log error message and return longurl]
    I3 -- success --> I5[Return result shortenedUrl]

    I -- No --> J[Is provider thalashort.com]
    J -- Yes --> J1[Encode longurl as long_url]
    J1 --> J2[Call thalashort api]
    J2 --> J3[Check result status]
    J3 -- error --> J4[Log error message and return longurl]
    J3 -- success --> J5[Return result shortenedUrl]

    J -- No --> K[Fallback to existing shortener handling]
    K --> L[Return result from fallback branch]
Loading

File-Level Changes

Change Details Files
Add support for several new URL shortener providers using a common API pattern with error handling fallback to the original URL.
  • Introduce conditional branches for new shortener domains (modijiurl.com, linkshortify.com, inshorturl.com, vplink.in, papajiurl.com, linkcents.com, reel2earn.com, shortner.in, thalashort.com).
  • For each new provider, URL-encode the long URL, call the provider-specific API endpoint with the configured API key, and parse JSON responses.
  • Log errors and return the original long URL when the provider API responds with an error status; otherwise return the shortenedUrl field.
bot/helper/ext_utils/shortener_utils.py
Support MEGA folder sub-path downloads and correctly route downloads via folder_api when working with folders.
  • Parse an optional sub-path from the listener.link string using the '
' delimiter, trimming whitespace and leading/trailing slashes.
  • Use the parsed mega_link instead of the raw listener.link when determining link type, fetching public nodes, and logging into folder links.
  • When a sub-path is provided for folder links, traverse the MEGA folder tree to resolve the corresponding child node; surface a clear error and abort if not found.
  • Switch to folder_api.startDownload via async_api.run when downloading from folder links, while retaining direct async_api.startDownload for file links.

  • Tips and commands

    Interacting with Sourcery

    • Trigger a new review: Comment @sourcery-ai review on the pull request.
    • Continue discussions: Reply directly to Sourcery's review comments.
    • Generate a GitHub issue from a review comment: Ask Sourcery to create an
      issue from a review comment by replying to it. You can also reply to a
      review comment with @sourcery-ai issue to create an issue from it.
    • Generate a pull request title: Write @sourcery-ai anywhere in the pull
      request title to generate a title at any time. You can also comment
      @sourcery-ai title on the pull request to (re-)generate the title at any time.
    • Generate a pull request summary: Write @sourcery-ai summary anywhere in
      the pull request body to generate a PR summary at any time exactly where you
      want it. You can also comment @sourcery-ai summary on the pull request to
      (re-)generate the summary at any time.
    • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
      request to (re-)generate the reviewer's guide at any time.
    • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
      pull request to resolve all Sourcery comments. Useful if you've already
      addressed all the comments and don't want to see them anymore.
    • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
      request to dismiss all existing Sourcery reviews. Especially useful if you
      want to start fresh with a new review - don't forget to comment
      @sourcery-ai review to trigger a new review!

    Customizing Your Experience

    Access your dashboard to:

    • Enable or disable review features such as the Sourcery-generated pull request
      summary, the reviewer's guide, and others.
    • Change the review language.
    • Add, remove or edit custom review instructions.
    • Adjust other review settings.

    Getting Help

    Copy link

    @sourcery-ai sourcery-ai bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Hey there - I've reviewed your changes - here's some feedback:

    • The new shortener branches all duplicate the same pattern (quoting the URL, building an API URL, calling cget, checking status, and returning shortenedUrl); consider extracting this into a helper function or data-driven mapping of domain → API base to reduce repetition and ease future additions.
    • For the new shortener responses, using direct result["status"] and result["shortenedUrl"] lookups assumes the API always returns the expected shape; consider using .get() and validating keys or status codes to avoid unexpected KeyError or type issues when the remote service misbehaves.
    • In add_mega_download, you now compute mega_link and call get_mega_link_type(mega_link) multiple times; caching the link type once and reusing it would simplify the branching, and you may also want to normalize sub_path parts (e.g., case sensitivity or trimming extra slashes) to make subfolder resolution more robust.
    Prompt for AI Agents
    Please address the comments from this code review:
    
    ## Overall Comments
    - The new shortener branches all duplicate the same pattern (quoting the URL, building an API URL, calling `cget`, checking `status`, and returning `shortenedUrl`); consider extracting this into a helper function or data-driven mapping of domain → API base to reduce repetition and ease future additions.
    - For the new shortener responses, using direct `result["status"]` and `result["shortenedUrl"]` lookups assumes the API always returns the expected shape; consider using `.get()` and validating keys or status codes to avoid unexpected `KeyError` or type issues when the remote service misbehaves.
    - In `add_mega_download`, you now compute `mega_link` and call `get_mega_link_type(mega_link)` multiple times; caching the link type once and reusing it would simplify the branching, and you may also want to normalize `sub_path` parts (e.g., case sensitivity or trimming extra slashes) to make subfolder resolution more robust.
    
    ## Individual Comments
    
    ### Comment 1
    <location> `bot/helper/mirror_leech_utils/download_utils/mega_download.py:65-74` </location>
    <code_context>
             child_nodes = [children.get(i) for i in range(children.size())]
             LOGGER.info(f"Found children: {[child.getName() for child in child_nodes]}")
    
    +    if sub_path and get_mega_link_type(mega_link) == "folder":
    +        def resolve_subnode(api_obj, base_node, rel_path):
    +            current = base_node
    +            for part in [p for p in rel_path.split("/") if p]:
    +                kids = api_obj.getChildren(current)
    +                match = None
    +                for i in range(kids.size()):
    +                    child = kids.get(i)
    +                    if child.getName() == part:
    +                        match = child
    +                        break
    +                if not match:
    +                    return None
    +                current = match
    +            return current
    +
    +        node = await sync_to_async(resolve_subnode, api, node, sub_path)
    +        if node is None:
    +            await listener.on_download_error(f"Subfolder not found: {sub_path}")
    </code_context>
    
    <issue_to_address>
    **issue (bug_risk):** Use the correct MegaApi instance (folder_api vs api) when resolving subfolders for folder links.
    
    Here `node` originates from `folder_api` (via `async_api.folder_api`), but `resolve_subnode` is called with the main `api` instance:
    
    ```python
    node = await sync_to_async(resolve_subnode, api, node, sub_path)
    ```
    If the node belongs to the `folder_api` tree, `api.getChildren(node)` may fail or always return no children, breaking subfolder resolution for folder links. Instead, choose the API based on where `node` came from, e.g.:
    
    ```python
    api_obj = folder_api or api
    node = await sync_to_async(resolve_subnode, api_obj, node, sub_path)
    ```
    so traversal stays within the correct API context.
    </issue_to_address>
    
    ### Comment 2
    <location> `bot/helper/mirror_leech_utils/download_utils/mega_download.py:31-35` </location>
    <code_context>
    
    
     async def add_mega_download(listener, path):
    +    mega_link = listener.link
    +    sub_path = None
    +    if "|" in mega_link:
    +        mega_link, sub_path = mega_link.split("|", 1)
    +        mega_link, sub_path = mega_link.strip(), sub_path.strip().strip("/")
    +
         async_api = AsyncMega()
    </code_context>
    
    <issue_to_address>
    **suggestion:** Consider validating or warning when a sub_path is provided for a file link, instead of silently ignoring it.
    
    Right now `sub_path` is parsed from `listener.link` but only used when `get_mega_link_type(mega_link) == "folder"`, so a value like `file_link|some/subpath` is silently ignored.
    
    Consider explicitly rejecting subpaths for file links, e.g.:
    
    ```python
    link_type = get_mega_link_type(mega_link)
    if sub_path and link_type == "file":
        await listener.on_download_error("Subpath is only supported for folder links")
        return
    ```
    
    This keeps behavior predictable and avoids silently discarding user input.
    
    Suggested implementation:
    
    ```python
    async def add_mega_download(listener, path):
        mega_link = listener.link
        sub_path = None
        if "|" in mega_link:
            mega_link, sub_path = mega_link.split("|", 1)
            mega_link, sub_path = mega_link.strip(), sub_path.strip().strip("/")
    
        link_type = get_mega_link_type(mega_link)
        if sub_path and link_type == "file":
            await listener.on_download_error("Subpath is only supported for folder links")
            return
    
        async_api = AsyncMega()
    
    ```
    
    ```python
        if link_type == "file":
    
    ```
    </issue_to_address>

    Sourcery is free for open source - if you like our reviews please consider sharing them ✨
    Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

    Comment on lines 65 to 74
    if sub_path and get_mega_link_type(mega_link) == "folder":
    def resolve_subnode(api_obj, base_node, rel_path):
    current = base_node
    for part in [p for p in rel_path.split("/") if p]:
    kids = api_obj.getChildren(current)
    match = None
    for i in range(kids.size()):
    child = kids.get(i)
    if child.getName() == part:
    match = child
    Copy link

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    issue (bug_risk): Use the correct MegaApi instance (folder_api vs api) when resolving subfolders for folder links.

    Here node originates from folder_api (via async_api.folder_api), but resolve_subnode is called with the main api instance:

    node = await sync_to_async(resolve_subnode, api, node, sub_path)

    If the node belongs to the folder_api tree, api.getChildren(node) may fail or always return no children, breaking subfolder resolution for folder links. Instead, choose the API based on where node came from, e.g.:

    api_obj = folder_api or api
    node = await sync_to_async(resolve_subnode, api_obj, node, sub_path)

    so traversal stays within the correct API context.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Labels

    None yet

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants