Skip to content

Commit

Permalink
fixes + VI range + user feed
Browse files Browse the repository at this point in the history
  • Loading branch information
Egsago-n committed Jun 30, 2023
1 parent 6c0b8e7 commit 71de804
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/phub/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'''
## PHUB - A light API for interacting with PornHub. ##
#### PHUB - A light API for interacting with PornHub. ####
See https://github.com/Egsagon/PHUB for documentation.
'''
Expand Down
7 changes: 6 additions & 1 deletion src/phub/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
@click.option('--noconfirm', '-n', help = 'Prevent confirming things.')
@click.option('ui', '-ui', help = 'Start in UI mode.', default = False)

def main(url: str, key: str, quality: str, output: str, noconfirm: str, ui: str) -> None:
def main(url: str = None,
key: str = None,
quality: str = None,
output: str = None,
noconfirm: str = None,
ui: str = None) -> None:
'''
Small downloading CLI script for PHUB.
See https://github.com/Egsagon/PHUB.
Expand Down
115 changes: 113 additions & 2 deletions src/phub/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Self, Callable
from typing import TYPE_CHECKING, Self, Callable, Generator
if TYPE_CHECKING: from phub.core import Client

import os
import json
from functools import cached_property
from datetime import datetime, timedelta

from bs4 import BeautifulSoup as Soup

from phub import utils
from phub import consts
from phub import parser
Expand Down Expand Up @@ -550,5 +552,114 @@ def __next__(self) -> Video:

self.index += 1
return result

def range(self, start: int, stop: int, step: int = 1) -> Generator[Video , None, None]:
'''
#### Emit a generator containing a slice of videos. ####
--------------------------------------------------------
Arguments:
- `start` -- Start index.
- `stop` -- Stop index.
- `step` (=`1`) -- Step.
--------------------------------------------------------
Yields `Video` objects.
'''

for index in range(start, stop, step):
yield self[index]

@dataclass
class FeedItem:
type: consts.FeedType
content: str = field(repr = False)
author: str = field(repr = False)
url: str = field(repr = False)
date: str = field(repr = False)

class Feed:

def __init__(self, client: Client) -> None:
'''
#### Generate a new feed object. ####
-------------------------------------
Arguments:
- `client` -- Client object to use.
'''

self.client = client
self.cache: dict[int, list[FeedItem]] = {}

def _get_page(self, page: int) -> list[FeedItem]:
'''
#### Fetch a specific feed page. ####
-------------------------------------
# EOF
Arguments:
- `page` -- Page index.
-------------------------------------
Returns a list of 14 `FeedItem` elements.
'''

# Fetch and parse page
url = 'feeds'
if page > 0: url += f'_ajax?ajax=1&page={page + 1}'
raw = self.client._call('GET', url).text

soup = Soup(raw, 'html.parser')
sections: list[Soup] = soup.find_all('section', {'class': 'feedItemSection'})

items = []
for section in sections:

badge = section.find('span', {'class': 'usernameBadgesWrapper'})
stream = section.find('a', {'class': 'stream_link'})

author = None
if badge:
author = User.get(client = self.client,
url = badge.find('a').get('href'))

url = date = None
if stream:
url = stream.get('href')
date = stream.text

content = section.find('div', {'class': 'feedInfo'}).text
if date: content = content.replace(date, '')

items += [FeedItem(
type = section.get('data-table'),
content = utils.hard_strip(content),
author = author,
url = url,
date = utils.hard_strip(date)
)]

# Save to cache
self.cache[page] = items
return items

def get(self, index: int) -> FeedItem:
'''
#### Get a specific feed item. ####
-----------------------------------
Arguments:
- `index` -- The feed element index.
-------------------------------------
Returns a `FeedItem` object.
'''

page_index = index // 14

page = self.cache.get(page_index)
if page is None:
page = self._get_page(page_index)

return page[ index % 14 ]

# EOF
13 changes: 13 additions & 0 deletions src/phub/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,17 @@ class regexes:
# Old
# video_model = re.compile( r'<span class=\"usernameBadgesWrapper\"><a rel=\"\" href=\"(.*?)\" class=\"bolded\">(.*?)</a>').findall # Get video author if model

class FeedType:
'''
Types representation (most) feed elements.
'''

SUBSCRIBED = 'stream_subscriptions_pornstars'
ACHIEVEMENT = 'stream_achievements'
UPLOAD = 'stream_videos_uploaded'
SITE_UPLOAD = 'stream_sites_subscriptions'
COMMENTED = 'stream_grouped_comments_videos'
# TODO more stream types


# EOF
10 changes: 10 additions & 0 deletions src/phub/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ def liked(self) -> classes.VideoIterator:
return classes.VideoIterator(self.client, url,
corrector = utils.remove_video_ads)

@cached_property
def feed(self) -> classes.Feed:
'''
#### Get the account feed. ####
-------------------------------
Returns a `Feed` object.
'''

return classes.Feed(self.client)

class Client:
'''
Represents a Client capable of interacting with PornHub.
Expand Down
29 changes: 27 additions & 2 deletions src/phub/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ def remove_video_ads(li: list) -> list:

def log(cls: str, *text, level: int = 1, r: bool = False) -> None:
'''
Homemade logging.
#### Homemade logging. ####
---------------------------
Arguments:
- `*` -- Log content.
- `level` (=`1`) -- Log level.
- `r` (=`False`) -- Wether log is inline.
'''

global DEBUG_RESET
Expand All @@ -172,6 +178,22 @@ def log(cls: str, *text, level: int = 1, r: bool = False) -> None:
elif not r and DEBUG_RESET: print(f'\n{raw}', file = DEBUG_FILE)
else: print(raw, file = DEBUG_FILE)

def hard_strip(text: str, sep: str = ' ') -> str:
'''
#### Remove all useless spaces from a text. ####
------------------------------------------------
Arguments:
- `text` -- The text to strip.
- `sep` (=` `) -- Separator between each words.
------------------------------------------------
Returns a stripped version of the text.
'''

if not text: return
return sep.join(text.split())


class download_presets:
'''
Expand Down Expand Up @@ -202,10 +224,13 @@ def bar(*args, **kwargs) -> Callable:
Display current progress a a bar.
'''

bar = tqdm.tqdm(*args, **kwargs)
bar = tqdm.tqdm = None

def wrapper(current: int, total: int) -> None:

if bar is None:
bar = tqdm.tqdm(*args, **kwargs)

bar.total = total
bar.update(1)
if current == total: bar.close()
Expand Down

0 comments on commit 71de804

Please sign in to comment.