decoratori - Stream4me/addon GitHub Wiki
Per degli esempi vedere gli altri canali (la maggior parte li usano), o anche il canale di esempio
NB: i decoratori si usano in questo modo
@decoratore
def funzione(parametri):
....
Per questi in particolare ricordatevi di mettere sempre
return locals()
alla fine, in quanto il decoratore chiamerà la funzione stessa, si farà ritornare i parametri e agirà di conseguenza.
Decoratore per la creazione dei menu (incluse le thumbnail), da utilizzare per la funzione mainlist. Utilizza le seguenti variabili:
- film
- tvshow
- anime
creano i corrispettivi menu "di primo livello" (ed i sottomenù,leggi sotto)
Si tratta di liste, in cui il primo valore è l'url relativo (il dominio è sottinteso quello del canale, se l'url relativo è vuoto è possibile ometterlo) e gli altri tuple che rappresentano i sottomenù, costituite in questo modo:
(nome menu, [url, funzione, argomenti])
film = [
# (nome menu, [url, funzione, argomenti])
('HD', ['', 'menu', 'Film HD Streaming']),
('Generi', ['', 'menu', 'Film per Genere']),
('Anni', ['', 'menu', 'Film per Anno'])
]
tvshow = ['/serietv/',
('Per Lettera', ['/serietv/', 'menu', 'Serie-Tv per Lettera']),
('Per Genere', ['/serietv/', 'menu', 'Serie-Tv per Genere']),
('Per anno', ['/serietv/', 'menu', 'Serie-Tv per Anno'])
]
- top
crea solo menu "di primo livello"
L'utilizzo è identico, solo che non possiede il primo valore (e non saranno sottomenù ma menu di primo livello).
top = [('Novità',['', 'peliculas', 'new', 'tvshow']),
('Aggiornamenti', ['', 'peliculas', 'last', 'tvshow']),
('Popolari', ['', 'peliculas', 'most_view', 'tvshow'])]
- search
search è utilizzato quando nel sito c'è un solo campo di ricerca per tutti i tipi di fonti (ricerca unica per film e serie ad esempio), in sua assenza viene automaticamente creata una voce "cerca" per ogni menu di primo livello, es: Cerca Film...
Il decoratore si occupera poi di includere la configurazione del canale.
Gli item risultanti avranno già il contentType settato (film/tvshow) se appartenenti ai relativi sottomenu
Esempio comprensivo di tutti i campi:
@support.menu
def mainlist(item):
film = [
# (nome menu, [url, funzione, argomenti])
('HD', ['', 'menu', 'Film HD Streaming']),
('Generi', ['', 'menu', 'Film per Genere']),
('Anni', ['', 'menu', 'Film per Anno'])
]
tvshow = ['/serietv/',
('Per Lettera', ['/serietv/', 'menu', 'Serie-Tv per Lettera']),
('Per Genere', ['/serietv/', 'menu', 'Serie-Tv per Genere']),
('Per anno', ['/serietv/', 'menu', 'Serie-Tv per Anno'])
]
top = [('Novità',['', 'peliculas', 'new', 'tvshow']),
('Aggiornamenti', ['', 'peliculas', 'last', 'tvshow']),
('Popolari', ['', 'peliculas', 'most_view', 'tvshow'])]
search = ''
return locals()
Decoratore per fare lo scraping vero e proprio, è un interfaccia alle funzioni di alfa/pelisalacarta che permette di fare uso quasi soltanto delle regex.
Per rendere le pagine un po' più standard, prima di effettuare lo scraping, effettua alcune operazioni sulla pagina:
- sostiuisce i ritorni a capo con uno spazio
- uniforma le virgolette (tutte le ' diventano ")
- elimina gli spazi multipli tra i tag html
Per funzionare è necessario settare alcune variabili, ecco la lista completa:
- patron
Si tratta della regex vera e propria, quella che deve ottenere i titoli, l'url e tutte le informazioni di un film/serie.
Deve essere costituita solo da named capturing groups, i quali possono essere:
['url', 'title', 'title2', 'season', 'episode', 'thumb', 'quality', 'year', 'plot', 'duration', 'genere', 'rating', 'type', 'lang', 'other', 'size', 'seed']
Questi possibili nomi sono abbastanza auto-esplicativi, inoltre scrape effettua già di per se operazioni "di bonifica", come ad esempio inserire il dominio quando manca nell'url scrapato(url relativo) o anche convertire duration in secondi (per essere meglio interpretato da kodi)
patron = r'div class="card-image">.*?<img src="(?P<thumb>[^ ]+)" alt.*?<a href="(?P<url>[^ >]+)">(?P<title>[^<[(]+)<\/a>.*?<strong><span style="[^"]+">(?P<genre>[^<>0-9(]+)\((?P<year>[0-9]{4}).*?</(?:p|div)>(?P<plot>.*?)</div'
- patronBlock
Nel caso la parte contenente i contenuti fosse indistinguibile da altri contenuti (ed è quindi necessario fare scraping solo di un pezzo di html), è possibile scrivere una regex per ottenere parte di codice, che poi sarà processata da 'patron'.
E' possibile inoltre che questa regex ritorni più pezzi di codice (ad esempio se è una lista di episodi divisa per stagione).
patronBlock può contenere i seguenti named capturing groups: block, season, lang, quality
in block ci va il blocco (pezzo) di codice da processare poi con 'patron', season, lang e quality invece vanno a sovrascrivere le omonime proprietà in patron, questo è utilissimo quando le info riguardo alla stagione, alla lingua (ITA/SUB-ITA) o alla qualità sono presenti una sola volta (riprendendo l'esempio di prima, se la lingua viene indicata stagione per stagione)
patronBlock = r'(?P<block><div class="sp-head[a-z ]*?" title="Espandi">\s*STAGIONE [0-9]+ - (?P<lang>[^\s]+)(?: - (?P<quality>[^-<]+))?.*?[^<>]*?</div>.*?)<div class="spdiv">\[riduci\]</div>'
- patronNext
Regex che deve ottenere il link alla prossima pagina, basta un semplice capturing group
patronNext='<a class="?page-link"? href="?([^>]+)"?><i class="fa fa-angle-right">'
- patronMenu
Uguale a patron ma per i menu, in pratica disabilita la ricerca del contenuto su tmdb Da utilizzare quando si creano dei sottomenu dinamici, come ad esempio "Genere" o "Per lettera" ecc..
- action
La funzione che deve partire quando si apre un risultato ottenuto da scrape, se omessa è di default findvideos (la funzione standard per ottenere le fonti)
- blacklist
Lista contenente titoli da NON visualizzare
# esclusione degli articoli 'di servizio'
blacklist = ['BENVENUTI', 'Richieste Serie TV', 'CB01.UNO ▶ TROVA L’INDIRIZZO UFFICIALE ',
'Aggiornamento Quotidiano Serie TV', 'OSCAR 2019 ▶ CB01.UNO: Vota il tuo film preferito! 🎬',
'Openload: la situazione. Benvenuto Verystream', 'Openload: lo volete ancora?']
- data
La pagina html da processare, se omessa di default viene scaricata e al sorgente vengono applicate alcune sostituizioni atte a renderla "più standard".
A volte può essere utile avere il codice html della pagina per effettuare azioni di conseguenza
data = support.httptools.downloadpage(item.url, headers=headers).data
if 'https://vcrypt.net' in data:
patron = '(?:<br /> |<p>)(?P<title>[^<]+)<a href="(?P<url>[^"]+)"'
else:
patron = '<br /> <a href="(?P<url>[^"]+)" target="_blank" rel="noopener[^>]+>(?P<title>[^<]+)</a>'
- headers
Gli header da passare quando scrape scarica la pagina (inutile se data è passato), a volte i siti danno risultati diversi, ad esempio di browser in browser, o non permettono di vedere la pagine se non hai prima visitato quella precedente (Referer)
headers = [['Referer', host]]
- debug
Se messa a True non appena la funzione viene chiamata, si aprirà una pagina di regex101 contenente il codice html scaricato (o i singolo blocchi, se patronBlock è impostato) e la regex contenuta in 'patron'.
Creata appositamente per testare le regex senza il bisogno di stamparsi nei log la pagina e copia-incollare tutto
- debugBlock
Uguale a debug ma per l'attributo patronBlock
- anime
Se è True attiva l'autorenumberer (per rinominare gli episodi in modo da poterli aggiungere in videoteca)
- addVideolibrary
Attiva/disattiva l'aggiunta in videoteca, True di default
- search
Mostra solo i risultati che contengono questa stringa (cerca nel titolo)
@support.scrape
def search(item, texto):
search = texto
....
- typeActionDict
Permette di associare ai valori trovati con il gruppo 'type' (nella regex), una action (funzione da chiamare)
# il sito mostra film e serie tutte insieme, ma possiede un parametro che indica di che cosa si tratta
# patron lo estrae (gruppo 'type') e in base al tipo di contenuto viene utilizzata la funzione apposita
typeActionDict={'findvideos': ['movies'], 'episodios': ['tvshows']}
- typeContentDict
Simile a typeActionDict, ma per il contentType (film, serie ecc..), se film e serie sono insieme indichiamo a S4Me quale valore significa film e quale serietv.
(support si comporta in maniera diversa a seconda del tipo di contenuto)
typeContentDict={'film': ['movies'], 'serie': ['tvshows']}
- pagination
Se settato divide il risultato in più pagine, facendo in modo che S4Me debba caricare solo una parte dei reali risultati.
Utile se la pagina web mostra tantissimi risultati e il conseguente scraping diventa lento (es: Ultimi 100 film aggiunti)
pagination = 30 # 30 risultati per pagina
pagination = '' # basta che la variabile sia settata e prende il default, ovvero 20 risultati
- sceneTitle
Se settato a True, il titolo verrà passato a guessit, che separerà tutte le info aggiuntive contenute.
Se il sito dovesse mostrare il nome della release (file) è l'ideale per ottenere le info riguardanti la qualità senza troppi problemi (support.scrape penserà ad inserire le info negli item e a mostrarle nel titolo se necessario). Esempio di dati estratti
>>> guessit.guessit('Il.Caso.Collini.2019.iTALiAN.BDRiP.XviD-PRiME[MT]')
MatchesDict([('title', 'Il Caso Collini'), ('year', 2019), ('language', <Language [it]>), ('source', 'Blu-ray'), ('other', 'Rip'), ('video_codec', 'Xvid'), ('release_group', 'PRiME[MT]'), ('type', 'movie')])
- deflang
La lingua predefinita dei contenuti presenti, utile ad esempio per i siti che mostrano praticamente solo SUB-ITA e pertanto non lo indicano, dando la cosa per scontata
deflang = 'Sub-ITA'
Inoltre è possibile creare degli hook creando funzioni con i seguenti nomi:
- itemHook(item)
Chiamata ogni qualvolta un item sta per essere aggiunto, prende come parametro item e deve ritornare l'item con tutte le modifiche che ritieni neccessarie.
def itemHook(item):
item.show = item.episode + item.title
return item
- itemlistHook(itemlist)
Chiamata quando tutti gli item (risultati) sono stati aggiunti, nel caso sia necessaria una manipolazione "finale", ad esempio rimozione/accorpamento duplicati, o, nel caso qui sotto, ordinamento.
def itemlistHook(itemlist):
itemlist.sort(key=lambda item: item.title)
return itemlist
- fullItemlistHook(itemlist)
Chiamata poco prima di ritornare l'itemlist a kodi, contiene non solo i risultati dello scraping ma anche gli item "di contorno"("Successivo", "Aggiungi in videoteca" ecc..).
Può essere utili in alcuni casi, ad esempio per modificare parametri degli item "speciali" sopracitati
# modifico l'url della pagina successiva (itemlist[-1], dato che "pagina successiva" è sempre l'ultimo item)
def fullItemlistHook(itemlist):
msgId = int(itemlist[-1].url.split('/')[-1])
itemlist[-1].url = host + '?before=' + str(msgId) + '&after=' + str(msgId-20)
return itemlist