Skip to content

Commit a44a1ef

Browse files
Merge pull request #24 from FundyJo/main
- Add season filter for improved episode navigation and sorting.
2 parents 688b3b6 + 87ba748 commit a44a1ef

File tree

7 files changed

+141
-14
lines changed

7 files changed

+141
-14
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ termux-setup-storage
109109
List of supported Anime sites
110110

111111
- [x] [AniWorld.to] & [SerienStream.to]
112-
- [ ] Filme
113112
- [ ] [bs.to](https://bs.to/)
114113
- [ ] [www3.streamcloud.info](https://www3.streamcloud.info/)
115114
- [ ] [www.crunchyroll.com](https://www.crunchyroll.com)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ maintainers = [{name="Commandcracker"}]
77
license = {file = "LICENSE.txt"}
88
readme = "README.md"
99
dependencies = [
10-
"textual>=3.1.1",
10+
"textual>=3.3.0",
1111
"textual-image[textual]>=0.8.2",
1212
"beautifulsoup4>=4.13.4",
1313
"httpx[http2]>=0.28.1",

src/gucken/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import warnings
22
warnings.filterwarnings('ignore', message='Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning')
33

4-
__version__ = "0.3.7"
4+
__version__ = "0.3.8"

src/gucken/gucken.py

Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from textual import events, on, work
2222
from textual.app import App, ComposeResult
2323
from textual.binding import Binding, BindingType
24-
from textual.containers import Center, Container, Horizontal, ScrollableContainer, Vertical
24+
from textual.containers import Center, Container, Horizontal, ScrollableContainer, Vertical, Grid
2525
from textual.reactive import reactive
2626
from textual.screen import ModalScreen
2727
from textual.widgets import (
@@ -310,15 +310,21 @@ def compose(self) -> ComposeResult:
310310
)
311311
yield Input(id="input", placeholder="Search for a Anime")
312312
yield ListView(id="results")
313+
313314
with TabPane("Info", id="info", disabled=True): # Info "ℹ"
314315
with ScrollableContainer(id="res_con"):
315316
yield Horizontal(
316317
Image(id="image"),
317318
Markdown(id="markdown"),
318319
id="res_con_2"
319320
)
320-
321+
yield Select.from_values(
322+
[], # Leere Liste zu Beginn
323+
id="season_filter",
324+
prompt="Alle Staffeln"
325+
)
321326
yield ClickableDataTable(id="season_list")
327+
322328
with TabPane("Settings", id="setting"): # Settings "⚙"
323329
# TODO: dont show unneeded on android
324330
with ScrollableContainer(id="settings_container"):
@@ -389,6 +395,56 @@ async def input_changed(self, event: Input.Changed):
389395
if event.control.id == "input":
390396
self.lookup_anime(event.value)
391397

398+
@on(Select.Changed)
399+
def on_season_filter_changed(self, event: Select.Changed) -> None:
400+
if event.control.id == "season_filter":
401+
table = self.query_one("#season_list", DataTable)
402+
if not self.current_info:
403+
return
404+
405+
table.clear(columns=True)
406+
table.add_columns("FT", "S", "F", "Title", "Hoster", "Sprache")
407+
408+
# Liste der gefilterten Episoden speichern
409+
self.filtered_episodes = []
410+
411+
# Temporäre Listen für die Sortierung
412+
regular_episodes = []
413+
movie_episodes = []
414+
415+
# Episoden in reguläre und Filme aufteilen
416+
for ep in self.current_info.episodes:
417+
if event.value == Select.BLANK or str(ep.season) == event.value:
418+
if str(ep.season) == "0":
419+
movie_episodes.append(ep)
420+
else:
421+
regular_episodes.append(ep)
422+
423+
# Zusammenführen der Listen: erst reguläre Episoden, dann Filme
424+
sorted_episodes = regular_episodes + movie_episodes
425+
self.filtered_episodes = sorted_episodes
426+
427+
# Anzeigen der sortierten Episoden
428+
c = 0
429+
for ep in sorted_episodes:
430+
hl = []
431+
for h in ep.available_hoster:
432+
hl.append(hoster.get_key(h))
433+
434+
ll = []
435+
for l in sort_favorite_lang(ep.available_language, self.language):
436+
ll.append(l.name)
437+
438+
c += 1
439+
table.add_row(
440+
c,
441+
"F" if str(ep.season) == "0" else ep.season,
442+
ep.episode_number,
443+
escape(ep.title),
444+
" ".join(sort_favorite_hoster_by_key(hl, self.hoster)),
445+
" ".join(ll),
446+
)
447+
392448
@on(SortableTable.SortChanged)
393449
async def sortableTable_sortChanged(
394450
self,
@@ -637,13 +693,29 @@ async def on_key(self, event: events.Key) -> None:
637693
async def play_selected(self):
638694
dt = self.query_one("#season_list", DataTable)
639695
# TODO: show loading
640-
#dt.set_loading(True)
696+
# dt.set_loading(True)
641697
index = self.app.query_one("#results", ListView).index
642698
series_search_result = self.current[index]
699+
700+
# Verwende filtered_episodes falls vorhanden, sonst current_info.episodes
701+
if hasattr(self, 'filtered_episodes') and self.filtered_episodes:
702+
episodes_to_use = self.filtered_episodes
703+
# cursor_row entspricht direkt dem Index in der gefilterten Liste
704+
selected_index = dt.cursor_row
705+
else:
706+
episodes_to_use = self.current_info.episodes
707+
# Finde die entsprechende Episode basierend auf der Tabellenzeile
708+
selected_row = dt.get_row_at(dt.cursor_row)
709+
# Suche die passende Episode anhand der angezeigten Informationen
710+
selected_index = next((i for i, ep in enumerate(episodes_to_use)
711+
if
712+
(str(ep.season) if ep.season != 0 else "F") == str(selected_row[1]) # Prüfe Staffel
713+
and str(ep.episode_number) == str(selected_row[2])), 0) # Prüfe Episodennummer
714+
643715
self.play(
644716
series_search_result=series_search_result,
645-
episodes=self.current_info.episodes,
646-
index=dt.cursor_row,
717+
episodes=episodes_to_use,
718+
index=selected_index,
647719
)
648720
#dt.set_loading(False)
649721

@@ -665,6 +737,22 @@ async def open_info(self) -> None:
665737

666738
series = await self.get_series(series_search_result)
667739
self.current_info = series
740+
741+
season_filter = self.query_one("#season_filter", Select)
742+
unique_seasons = sorted(set(ep.season for ep in series.episodes))
743+
744+
# Sortiere die Staffeln so, dass Filme (Staffel 0) am Ende erscheint
745+
regular_seasons = [s for s in unique_seasons if s != 0]
746+
movies_season = [s for s in unique_seasons if s == 0]
747+
sorted_seasons = regular_seasons + movies_season
748+
749+
season_filter_options = []
750+
for season in sorted_seasons:
751+
label = "Filme" if season == 0 else f"Staffel {season}"
752+
season_filter_options.append((label, str(season)))
753+
season_filter.set_options(season_filter_options)
754+
season_filter.value = Select.BLANK
755+
668756
await md.update(series.to_markdown())
669757

670758
if gucken_settings_manager.settings["settings"]["image_display"]:
@@ -677,8 +765,25 @@ async def open_info(self) -> None:
677765
table.clear(columns=True)
678766
table.add_columns("FT", "S", "F", "Title", "Hoster", "Sprache")
679767

680-
c = 0
768+
# Sortiere die Episoden entsprechend
769+
770+
# Sortiere die Episoden entsprechend der gewünschten Reihenfolge
771+
sorted_episodes = []
772+
# Zuerst Specials (S)
681773
for ep in series.episodes:
774+
if ep.season == "S":
775+
sorted_episodes.append(ep)
776+
# Dann numerische Staffeln
777+
for ep in series.episodes:
778+
if isinstance(ep.season, (int, str)) and ep.season not in ["S", 0]:
779+
sorted_episodes.append(ep)
780+
# Zum Schluss Filme (F)
781+
for ep in series.episodes:
782+
if ep.season == 0:
783+
sorted_episodes.append(ep)
784+
785+
c = 0
786+
for ep in sorted_episodes:
682787
hl = []
683788
for h in ep.available_hoster:
684789
hl.append(hoster.get_key(h))
@@ -688,9 +793,17 @@ async def open_info(self) -> None:
688793
ll.append(l.name)
689794

690795
c += 1
796+
# Zeige die Staffeln in der gewünschten Reihenfolge
797+
if ep.season == "S":
798+
season_display = "S"
799+
elif ep.season == 0:
800+
season_display = "F"
801+
else:
802+
season_display = ep.season
803+
691804
table.add_row(
692805
c,
693-
ep.season,
806+
season_display,
694807
ep.episode_number,
695808
escape(ep.title),
696809
" ".join(sort_favorite_hoster_by_key(hl, self.hoster)),

src/gucken/provider/aniworld.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,11 @@ async def get_series(search_result: AniWorldSearchResult) -> AniWorldSeries:
207207
.find_all("a")
208208
)
209209
count = 0
210+
210211
for staffel in staffeln:
211-
# TODO: Filme
212-
if staffel.text != "Filme":
212+
if staffel.text == "Filme":
213+
funcs.append(get_episodes_from_url(0, search_result.url)) # Filme als Staffel 1 behandeln
214+
else:
213215
count += 1
214216
if count > 1:
215217
funcs.append(get_episodes_from_url(count, search_result.url))

src/gucken/provider/serienstream.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,11 @@ async def get_series(search_result: SerienStreamSearchResult) -> SerienStreamSer
216216
.find_all("a")
217217
)
218218
count = 0
219+
219220
for staffel in staffeln:
220-
# TODO: Filme
221-
if staffel.text != "Filme":
221+
if staffel.text == "Filme":
222+
funcs.append(get_episodes_from_url(0, search_result.url)) # Filme als Staffel 1 behandeln
223+
else:
222224
count += 1
223225
if count > 1:
224226
funcs.append(get_episodes_from_url(count, search_result.url))

src/gucken/resources/gucken.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,14 @@ Tab {
134134
height: auto;
135135
width: 1fr;
136136
}
137+
138+
/* * This is a workaround for the horizontal scrollable container
139+
* to have a margin at the bottom, so it does not touch the next element.
140+
*/
141+
.season-filter {
142+
margin-bottom: 1; /* Abstand nach unten */
143+
}
144+
145+
ScrollableContainer > Horizontal#res_con_2 {
146+
margin-bottom: 1;
147+
}

0 commit comments

Comments
 (0)