A /watch link may now open one of its owner's own folders (migration 0108: folder_id beside job_id, exactly one set; a folder's links go with it). The page shows what is in the folder when it is opened, newest first: a video or a song plays in the same player a file link has, above the list, each keeping its own resume point; a document only downloads, never opens as a page. A file moved out stops opening through the link at its next request. Password, expiry and stream-only work as on a file link; the unfurl names the folder and counts its files, never their names. Source folders and the Hub cannot be shared. The folder menu gets "Copy link" and "Share with a link…" (the Share dialog's link half); the rail marks a folder a live link opens. Two fixes in the Share dialog on the way: revoking the last link no longer mints a new one at once, and the new-link form goes back to downloadable after making a link.
41 lines
1.5 KiB
Python
41 lines
1.5 KiB
Python
"""A share link may open a folder
|
|
|
|
Revision ID: 0108_folder_links
|
|
Revises: 0107_link_token_at_rest
|
|
Create Date: 2026-10-15
|
|
|
|
A `/watch` link opened one file. Now it may open one of its owner's own folders instead: the link's
|
|
page lists what is in the folder when it is opened. `folder_id` sits beside `job_id`, exactly one of
|
|
the two set; a folder's links go with the folder.
|
|
|
|
The downgrade has nowhere to keep a folder link, so it deletes them: they stop opening.
|
|
"""
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0108_folder_links"
|
|
down_revision: str | None = "0107_link_token_at_rest"
|
|
branch_labels: str | None = None
|
|
depends_on: str | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column(
|
|
"download_links",
|
|
sa.Column("folder_id", sa.Integer(), sa.ForeignKey("file_folders.id", ondelete="CASCADE")),
|
|
)
|
|
op.create_index("ix_download_links_folder_id", "download_links", ["folder_id"])
|
|
op.alter_column("download_links", "job_id", nullable=True)
|
|
op.create_check_constraint(
|
|
"ck_download_link_target", "download_links", "(job_id IS NULL) <> (folder_id IS NULL)"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DELETE FROM download_links WHERE folder_id IS NOT NULL")
|
|
op.drop_constraint("ck_download_link_target", "download_links", type_="check")
|
|
op.alter_column("download_links", "job_id", nullable=False)
|
|
op.drop_index("ix_download_links_folder_id", table_name="download_links")
|
|
op.drop_column("download_links", "folder_id")
|