feat(favorites): ulubione studia licza takze filmy

Studio bywa WYLACZNIE filmowe: Jonathan Jordan XXX 0 scen / 654 filmy,
Kink Clips 0/616, EAGLE 0/523. Lista ulubionych pokazywala wtedy samo
"0 scenes", czyli wygladala na pusta, mimo ze badge "+N" liczyl filmy
juz od 2026-07-28 (_merged_new_counts).

Backend: `FavoriteStudioOut.movie_count`, liczony NA ZYWO. Ulubionych
studiow jest kilkaset na urzadzenie, a filmow o rzedy wielkosci mniej niz
scen, wiec agregat jest tani i nie trzeba kolejnej zdenormalizowanej
kolumny do utrzymania.

Apka pokazuje oba liczniki, a gdy jednego nie ma, pomija go, zeby nie
robic z tego "0 scenes · 654 movies".

Domyka to temat zaczety zakladka Movies na ekranie studia: dodanie do
ulubionych od strony filmu juz dzialalo (gwiazdka w StudioMoviesScreen).
This commit is contained in:
goon-foss 2026-08-13 20:30:17 +02:00
parent 1a75433281
commit f3f16b5c67
4 changed files with 56 additions and 2 deletions

View file

@ -27,7 +27,7 @@ from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlalchemy import exists, select
from sqlalchemy import exists, func, select
from sqlalchemy.orm import Session
from app.api.device import get_device_id
@ -307,6 +307,10 @@ class FavoriteStudioOut(BaseModel):
slug: str
network: str | None = None
scene_count: int
#: Filmów z żywym playbackiem. Studio może mieć ZERO scen i setki filmów
#: (Jonathan Jordan XXX 0/654, Kink Clips 0/616, EAGLE 0/523), a lista ulubionych
#: pokazywała wtedy samo „0" i wyglądała na pustą, mimo że „+N" liczyło już filmy.
movie_count: int = 0
new_count: int
last_seen_at: datetime
created_at: datetime
@ -334,6 +338,29 @@ def list_favorite_studios(
# scene_count: zdenormalizowany Studio.scene_count (refresh w tle, migracja 0019).
scene_counts: dict = {st.id: st.scene_count for _, st in rows}
# movie_count liczymy NA ŻYWO: ulubionych studiów jest kilkaset na urządzenie,
# a filmów o rzędy wielkości mniej niż scen, więc agregat jest tani i nie trzeba
# kolejnej zdenormalizowanej kolumny do utrzymania.
from app.models.movie import Movie
from app.models.movie_playback_source import MoviePlaybackSource
studio_ids = [st.id for _, st in rows]
movie_counts: dict = {
sid: n
for sid, n in session.execute(
select(Movie.studio_id, func.count())
.where(
Movie.studio_id.in_(studio_ids),
exists(
select(1).where(
MoviePlaybackSource.movie_id == Movie.id,
MoviePlaybackSource.dead_at.is_(None),
)
),
)
.group_by(Movie.studio_id)
).all()
}
new_counts = _merged_new_counts(session, device_id, kind="studio")
items: list[FavoriteStudioOut] = []
@ -348,6 +375,7 @@ def list_favorite_studios(
slug=st.slug,
network=st.network,
scene_count=scene_counts.get(st.id, 0),
movie_count=movie_counts.get(st.id, 0),
new_count=nc,
last_seen_at=fav.last_seen_at,
created_at=fav.created_at,

View file

@ -16,6 +16,13 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
{
id: '2026-08-13c',
date: 'August 2026',
items: [
'Followed studios now show how many movies they have, not just scenes. Studios that only put out movies used to look empty in your favourites.',
],
},
{
id: '2026-08-13b',
date: 'August 2026',

View file

@ -463,6 +463,22 @@ function MovieRow({
);
}
// Studio bywa WYŁĄCZNIE filmowe (Jonathan Jordan XXX 0 scen / 654 filmy, Kink Clips
// 0/616), a wiersz pokazywał wtedy „0 scenes", czyli wyglądał na pusty, mimo że badge
// „+N" liczył filmy od 2026-07-28. Pokazujemy oba, a gdy jednego nie ma, pomijamy go,
// żeby nie robić z tego „0 scenes · 654 movies".
function studioCounts(fav: FavoriteStudioOut): string {
const parts: string[] = [];
const movies = fav.movie_count ?? 0;
if (fav.scene_count > 0 || movies === 0) {
parts.push(`${formatCount(fav.scene_count)} ${fav.scene_count === 1 ? 'scene' : 'scenes'}`);
}
if (movies > 0) {
parts.push(`${formatCount(movies)} ${movies === 1 ? 'movie' : 'movies'}`);
}
return parts.join(' · ');
}
function StudioRow({
fav,
onPress,
@ -487,7 +503,7 @@ function StudioRow({
{fav.name}
</Text>
<Text style={styles.rowMuted}>
{formatCount(fav.scene_count)} {fav.scene_count === 1 ? 'scene' : 'scenes'}
{studioCounts(fav)}
{fav.network ? ` · ${fav.network}` : ''}
</Text>
</View>

View file

@ -347,6 +347,9 @@ export interface FavoriteStudioOut {
slug: string;
network: string | null;
scene_count: number;
/** Filmów z żywym playbackiem. Studio bywa WYŁĄCZNIE filmowe (0 scen,
* setki filmów), a lista pokazywała wtedy samo „0 scenes". */
movie_count?: number;
new_count: number;
last_seen_at: string;
created_at: string;