Skip to content

Commit

Permalink
🎉 New channel homepage
Browse files Browse the repository at this point in the history
- Add page to browse single channel videos/shorts/streams or latest
  videos
- Remove custom theme using query param
- Remove "mark as read" feature
  • Loading branch information
essembeh committed Nov 15, 2024
1 parent 8089fd1 commit 3a1f27e
Show file tree
Hide file tree
Showing 17 changed files with 513 additions and 359 deletions.
19 changes: 13 additions & 6 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,23 @@ async def test_page_content(client):
"PLw-vK1_d04zZCal3yMX_T23h5nDJ2toTk", # a playlist
"UCVooVnzQxPSTXTMzSi1s6uw", # a channel
"@CardMagicByJason", # a user
"@UCAAAAAAAAAAAAAAAAAAAAAA", # an unknown user
"UCAAAAAAAAAAAAAAAAAAAAAA", # an invalid channel
"foobar", # an invalid name
]
resp = await client.get("/" + ",".join(names))
assert resp.status_code == 200

soup = BeautifulSoup(resp.text, features="html.parser")
assert len(soup.find_all("div", class_="yourss-filterable")) == 45

# when no valid feed given, should return 404
resp = await client.get("/UCAAAAAAAAAAAAAAAAAAAAAA,@UCAAAAAAAAAAAAAAAAAAAAAA")
assert resp.status_code == 404

@pytest.mark.anyio
async def test_page_content_invalid_names(client):
valid_names = [
"PLw-vK1_d04zZCal3yMX_T23h5nDJ2toTk", # a playlist
"UCVooVnzQxPSTXTMzSi1s6uw", # a channel
"@CardMagicByJason", # a user
"@UCAAAAAAAAAAAAAAAAAAAAAA", # an unknown user
"UCAAAAAAAAAAAAAAAAAAAAAA", # an invalid channel
"foobar", # an invalid name
]
resp = await client.get("/" + ",".join(valid_names))
assert resp.status_code == 500
87 changes: 87 additions & 0 deletions yourss/routers/jinja.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from datetime import datetime
from typing import List

import arrow
from fastapi import Request
from jinja2 import Environment, FileSystemLoader
from starlette.templating import Jinja2Templates, _TemplateResponse

import yourss

from ..settings import current_config, templates_folder
from ..youtube import VideoDescription, YoutubeMetadata


def clean_title(text: str) -> str:
if current_config.clean_titles:
return text.capitalize()
return text


def date_humanize(date: datetime | str) -> str:
if isinstance(date, str):
return date
return arrow.get(date).humanize()


# Jinja customization
jinja_env = Environment(loader=FileSystemLoader(templates_folder))
jinja_env.filters["clean_title"] = clean_title
jinja_env.filters["date_humanize"] = date_humanize

jinja = Jinja2Templates(env=jinja_env)
TemplateResponse = jinja.TemplateResponse


def _template_page(template_name: str, request: Request, **kwargs) -> _TemplateResponse:
return jinja.TemplateResponse(
template_name,
{
"request": request,
"version": yourss.__version__,
"config": current_config,
"theme": current_config.theme,
}
| {k: v for k, v in kwargs.items() if v is not None},
)


def page_channel(request: Request, metadata: YoutubeMetadata) -> _TemplateResponse:
return _template_page("pages/channel.jinja-html", request, metadata=metadata)


def tab_latest(request: Request, videos: List[VideoDescription]) -> _TemplateResponse:
return _template_page("partials/tab.jinja-html", request, videos=videos)


def tab_videos(
request: Request, videos: List[VideoDescription], next_page_url: str | None
) -> _TemplateResponse:
return _template_page(
"partials/tab.jinja-html",
request,
videos=videos,
next_page_url=next_page_url,
)


def tab_shorts(
request: Request, videos: List[VideoDescription], next_page_url: str | None
) -> _TemplateResponse:
return _template_page(
"partials/tab.jinja-html",
request,
videos=videos,
next_page_url=next_page_url,
)


def next_page(
request: Request, videos: List[VideoDescription], next_page_url: str | None
) -> _TemplateResponse:
return _template_page(
"partials/next-page.jinja-html",
request,
videos=videos,
next_page_url=next_page_url,
)
25 changes: 16 additions & 9 deletions yourss/routers/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Callable, List
from typing import Any, Dict, List
from urllib.parse import urlencode

from starlette.templating import Jinja2Templates, _TemplateResponse
from ..youtube.model import BrowseData


def force_https(url: str) -> str:
Expand All @@ -16,11 +17,17 @@ def parse_channel_names(text: str, delimiter: str = ",") -> List[str]:
)


def custom_template_response(
jinja: Jinja2Templates, template_file: str, **kwargs
) -> Callable[..., _TemplateResponse]:
def func(**kwargs2) -> _TemplateResponse:
kwargs2.update(kwargs)
return jinja.TemplateResponse(template_file, kwargs2)
def build_url(base_url: str, params: Dict[str, Any]) -> str:
return base_url + "?" + urlencode(params)

return func

def next_page_url(browse_data: BrowseData, url: str, shorts: bool) -> str | None:
if browse_data.continuation_token is not None:
return build_url(
url,
{
"click_tracking_params": browse_data.click_tracking_params,
"continuation_token": browse_data.continuation_token,
"shorts": shorts,
},
)
Loading

0 comments on commit 3a1f27e

Please sign in to comment.