Skip to content

Commit

Permalink
Soporte perfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
pablouser1 committed Sep 10, 2022
1 parent 67f7679 commit f52fd40
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 102 deletions.
6 changes: 5 additions & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.filmin" name="Filmin" version="1.4.2" provider-name="pablouser1">
<addon id="plugin.video.filmin" name="Filmin" version="1.5.0" provider-name="pablouser1">
<requires>
<import addon="xbmc.python" version="3.0.0" />
<import addon="script.module.requests" version="2.22.0+matrix.1" />
Expand All @@ -18,6 +18,10 @@
<license>GPL-3.0-or-later</license>
<website>https://github.com/pablouser1/plugin.video.filmin</website>
<source>http://github.com/pablouser1/plugin.video.filmin</source>
<news>v1.5.0 (10/09/2022)
Implementado sistema de perfiles
IMPORTANTE: Cierra sesión antes de utilizar si vienes de una versión anterior
</news>
<assets>
<icon>resources/icon.png</icon>
<fanart>resources/fanart.jpg</fanart>
Expand Down
8 changes: 8 additions & 0 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,11 @@ msgstr ""
msgctxt "#40022"
msgid "Logged in successfully"
msgstr ""

msgctxt "#40023"
msgid "Profile ID"
msgstr ""

msgctxt "#40024"
msgid "Change profile"
msgstr ""
8 changes: 8 additions & 0 deletions resources/language/resource.language.es_es/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,11 @@ msgstr "Escribe tu nombre de usuario / correo electrónico"
msgctxt "#40021"
msgid "Type your password"
msgstr "Escribe tu contraseña"

msgctxt "#40023"
msgid "Profile ID"
msgstr "ID del perfil"

msgctxt "#40024"
msgid "Change profile"
msgstr "Cambiar perfil"
54 changes: 39 additions & 15 deletions resources/lib/api.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
import requests
from xbmc import getLanguage, ISO_639_1
from .exceptions.ApiException import ApiException
from .exceptions.ApiV3Exception import ApiV3Exception
from .exceptions.UApiException import UApiException

class Api:
BASE_URL = "https://apiv3.filmin.es"
API_URL = "https://apiv3.filmin.es"
UAPI_URL = "https://uapi.filmin.es"
s = requests.Session()

# Both extracted from the Android app
CLIENT_ID = "zXZXrpum7ayGcWlo"
CLIENT_SECRET = "yICstBCQ8CKB8RF6KuDmr9R20xtfyYbm"

DEVICE_MODEL = 'Kodi'
DEVICE_OS_VERSION = '12'
CLIENT_VERSION = "4.2.440"

def __init__(self):
self.s.headers["X-CLIENT-ID"] = self.CLIENT_ID
self.s.headers["X-Client-Id"] = self.CLIENT_ID
self.s.headers["clientlanguage"] = getLanguage(ISO_639_1, True)
self.s.headers["clientversion"] = '4.2.316' # Latest Filmin version Android
self.s.headers["devicemodel"] = 'Kodi'

def makeRequest(self, endpoint: str, method = 'GET', body: dict = {}, query: dict = {}):
res = self.s.request(method, self.BASE_URL + endpoint, json=body, params=query)
self.s.headers["clientversion"] = self.CLIENT_VERSION
self.s.headers["X-Client-Version"] = self.CLIENT_VERSION

self.s.headers["devicemodel"] = self.DEVICE_MODEL
self.s.headers["X-Device-Model"] = self.DEVICE_MODEL

self.s.headers['deviceosversion'] = self.DEVICE_OS_VERSION
self.s.headers['X-Device-OS-Version'] = self.DEVICE_OS_VERSION

def makeRequest(self, endpoint: str, method = 'GET', body: dict = {}, query: dict = {}, useUapi: bool = False):
base_url = self.UAPI_URL if useUapi else self.API_URL
res = self.s.request(method, base_url + endpoint, json=body, params=query)
res_json = res.json()
if res.ok:
return res_json
raise ApiException(res_json['errors'])

if useUapi:
raise UApiException(res_json['error'])
else:
raise ApiV3Exception(res_json['errors'])

def login(self, username: str, password: str)-> dict:
res = self.makeRequest('/oauth/access_token', 'POST', {
Expand All @@ -33,12 +51,13 @@ def login(self, username: str, password: str)-> dict:
})
return res

def profiles(self)-> list:
res = self.makeRequest('/auth/profiles', useUapi=True)
return res

def logout(self):
self.makeRequest('/oauth/logout', 'POST')

def setToken(self, token: str):
self.s.headers["Authorization"] = f'Bearer {token}'

def user(self):
res = self.makeRequest(endpoint='/user')
return res['data']
Expand Down Expand Up @@ -94,11 +113,9 @@ def collection(self, collection_id: int)-> list:

def watching(self)-> list:
items = []
res = self.makeRequest(endpoint='/user/watching', query={
'limit': 5
})
res = self.makeRequest(endpoint='/auth/keep-watching', useUapi=True)
for item in res['data']:
items.append(item['entity']['data'])
items.append(item['media'])

return items

Expand Down Expand Up @@ -163,3 +180,10 @@ def getStreams(self, item_id: int):
})

return versions

# -- HELPERS -- #
def setToken(self, token: str):
self.s.headers["Authorization"] = f'Bearer {token}'

def setProfileId(self, profile_id: str):
self.s.headers['x-user-profile-id'] = profile_id
2 changes: 2 additions & 0 deletions resources/lib/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ def run():
# Check if user already has a session
if config.hasLoginData():
token_info = config.getToken()
profile_id = config.getProfileId()
api.setToken(token_info['access'])
api.setProfileId(profile_id)
# Ask for credentials if not
else:
askLogin()
Expand Down
6 changes: 6 additions & 0 deletions resources/lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ def getToken(self)-> dict:
def getUserId(self)-> int:
return self.addon.getSettingInt('user_id')

def getProfileId(self)-> str:
return self.addon.getSettingString('profile_id')

def canBuy(self)-> bool:
return self.addon.getSettingBool('tickets')

Expand All @@ -32,3 +35,6 @@ def setAuth(self, access_token: str, refresh_token: str, username: str, user_id:
self.addon.setSettingString('refresh_token', refresh_token)
self.addon.setSettingString('username', username)
self.addon.setSettingInt('user_id', user_id)

def setProfileId(self, profile_id: str):
self.addon.setSettingString('profile_id', profile_id)
5 changes: 2 additions & 3 deletions resources/lib/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
def __enum(**enums):
return type('Enum', (), enums)
from .helpers.Misc import enum

ROUTES = __enum(
ROUTES = enum(
HOME='home',
CATALOG='catalog',
SEARCH='search',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from xbmcgui import Dialog

class ApiException(Exception):
class ApiV3Exception(Exception):
"""
Throw exception when HTTP code is diferent from 2XX
"""
Expand Down
9 changes: 9 additions & 0 deletions resources/lib/exceptions/UApiException.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from xbmcgui import Dialog

class UApiException(Exception):
"""
Throw exception when HTTP code is diferent from 2XX
"""
def __init__(self, error: dict):
super().__init__()
Dialog().ok('Filmin API Error', error['title'])
31 changes: 31 additions & 0 deletions resources/lib/helpers/Art.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Art:
@staticmethod
def apiv3(artworks: list)-> dict:
"""
Sorts art for Filmin Menus
"""
poster = None
card = None
thumb = None
for art in artworks:
if art['image_type'] == 'poster':
poster = art['path']
elif art['image_type'] == 'card':
card = art['path']
elif art['image_type'] == 'poster-mini':
thumb = art['path']

return {
"poster": poster,
"card": card,
"thumb": thumb
}

@staticmethod
def uapi(item: dict)-> dict:
return {
"poster": item.get("image_poster"),
"card": item.get("image_card"),
"landscape": item.get("image_highlighted")
# "thumb":
}
88 changes: 88 additions & 0 deletions resources/lib/helpers/ListItemExtra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from xbmcgui import ListItem
from .Art import Art

class ListItemExtra:
@staticmethod
def video(url: str, item: dict) -> ListItem:
if item.get('_type'):
list_item = ListItemExtra.videoUapi(url, item)
else:
list_item = ListItemExtra.videoApiv3(url, item)

# Common
list_item.setProperty('isPlayable', 'true')
list_item.setIsFolder(False)
return list_item

@staticmethod
def folder(url: str, item: dict) -> ListItem:
if item.get('_type'):
list_item = ListItemExtra.folderUapi(url, item)
else:
list_item = ListItemExtra.folderApiv3(url, item)

return list_item

@staticmethod
def videoUapi(url: str, item: dict) -> ListItem:
list_item = ListItem(item['title'], path=url)
info = {
"title": item["title"],
"year": item["year"],
"plot": item["excerpt"],
"director": item['director_names'],
"rating": item["avg_votes"],
"duration": item["duration_in_minutes"] * 60 # Filmin returns duration in minutes, Kodi wants it in seconds
}
list_item.setInfo('video', info)
# ART
list_item.setArt(Art.uapi(item))
return list_item

@staticmethod
def videoApiv3(url: str, item: dict) -> ListItem:
list_item = ListItem(item['title'], path=url)
info = {
"title": item["title"],
"originaltitle": item["original_title"],
"year": item["year"],
"plot": item["excerpt"],
"director": item["first_director"],
"rating": float(item["avg_votes_press"]) if item.get("avg_votes_press") else None,
"userrating": item["avg_votes_users"] if item.get("avg_votes_users") else None,
"duration": item["duration"] * 60 # Filmin returns duration in minutes, Kodi wants it in seconds
}
list_item.setInfo('video', info)
# ART
art = Art.apiv3(item["imageResources"]["data"])
list_item.setArt(art)
return list_item

@staticmethod
def folderUapi(url: str, item: dict) -> ListItem:
list_item = ListItem(item['title'], path=url)
info = {
"title": item["title"],
"year": item["year"],
"plot": item["excerpt"],
"director": item['director_names'],
"rating": item["avg_votes"],
"duration": item["duration_in_minutes"] * 60 # Filmin returns duration in minutes, Kodi wants it in seconds
}
# ART
list_item.setArt(Art.uapi(item))
return list_item

@staticmethod
def folderApiv3(url: str, item: dict) -> ListItem:
list_item = ListItem(item['title'], path=url)
info = {
"title": item["title"],
"plot": item.get('excerpt')
}
list_item.setInfo('video', info)
if 'imageResources' in item:
art = Art.apiv3(item["imageResources"]["data"])
list_item.setArt(art)

return list_item
2 changes: 2 additions & 0 deletions resources/lib/helpers/Misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def enum(**enums):
return type('Enum', (), enums)
17 changes: 8 additions & 9 deletions resources/lib/helpers/Render.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import xbmcplugin

from ..common import _HANDLE, _URL, params
from .Types import videos_type, folders_type
from .listitem import setInfoVideo, setInfoFolder
from .Types import Types
from .ListItemExtra import ListItemExtra

class Render:
@staticmethod
Expand All @@ -26,13 +26,12 @@ def static(items: list)-> list:
@staticmethod
def videos(items: list)-> list:
"""
Render folders fetched from Filmin API
Render videos fetched from Filmin API
"""
listing = []
for item in items:
url = '{0}?menu=player&id={1}'.format(_URL, item["id"])
list_item = setInfoVideo(url, item)
list_item.setProperty('IsPlayable', 'true')
list_item = ListItemExtra.video(url, item)
listing.append((url, list_item, False))
return listing

Expand All @@ -44,25 +43,25 @@ def folders(items: list, menu: str = '')-> list:
listing = []
for item in items:
if not menu:
if item['type'] == folders_type[0]:
if item['type'] == Types.folders[0]:
menu = 'seasons'
url = '{0}?menu={1}&id={2}'.format(_URL, menu, item["id"])
if menu == 'episodes':
# Add show id to URL
url += '&item_id={0}'.format(params['id'])
list_item = setInfoFolder(url, item)
list_item = ListItemExtra.folder(url, item)
listing.append((url, list_item, True))
return listing

@staticmethod
def mix(items: list, goTo: str = '')-> list:
"""
Render folder with containing both another folders and videos
Render folder containing both folders and videos
"""
videos = []
folders = []
for item in items:
if item["type"] in videos_type:
if item["type"] in Types.videos:
videos.append(item)
else:
folders.append(item)
Expand Down
10 changes: 3 additions & 7 deletions resources/lib/helpers/Types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
videos_type = (
"short", "film", "episode"
)

folders_type = (
"serie", "season"
)
class Types:
videos = ("short", "film", "episode")
folders = ("serie", "season")
Loading

0 comments on commit f52fd40

Please sign in to comment.