mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2026-04-27 14:00:34 +00:00
Merge 3f7f9057cc into c2eb46106b
This commit is contained in:
commit
792d04813f
10 changed files with 338 additions and 5 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -75,4 +75,9 @@ pyinstrument/
|
|||
# Now managed by templates
|
||||
addon.xml
|
||||
|
||||
*.zip
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
|
||||
*.log
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ FROM jellyfin
|
|||
WHERE media_folder = ?
|
||||
"""
|
||||
get_item_by_parent_movie_obj = ["{KodiId}", "movie"]
|
||||
get_item_by_parent_extra_obj = ["{KodiId}", "extra"]
|
||||
get_item_by_parent_tvshow_obj = ["{ParentId}", "tvshow"]
|
||||
get_item_by_parent_season_obj = ["{ParentId}", "season"]
|
||||
get_item_by_parent_episode_obj = ["{ParentId}", "episode"]
|
||||
|
|
@ -106,6 +107,18 @@ add_reference_movie_obj = [
|
|||
"{LibraryId}",
|
||||
"{JellyfinParentId}",
|
||||
]
|
||||
add_reference_extra_obj = [
|
||||
"{Id}",
|
||||
"{ExtraId}",
|
||||
"{FileId}",
|
||||
"{PathId}",
|
||||
"Extra",
|
||||
"extra",
|
||||
"{MovieId}",
|
||||
"{Checksum}",
|
||||
"{LibraryId}",
|
||||
"{JellyfinParentId}",
|
||||
]
|
||||
add_reference_boxset_obj = [
|
||||
"{Id}",
|
||||
"{SetId}",
|
||||
|
|
|
|||
|
|
@ -95,6 +95,21 @@ def get_movies_by_boxset(boxset_id):
|
|||
yield items
|
||||
|
||||
|
||||
def get_extra_by_movie(movie_id):
|
||||
|
||||
query = {
|
||||
"url": "Items/%s/SpecialFeatures" % movie_id,
|
||||
"params": {
|
||||
"EnableUserData": True,
|
||||
"EnableImages": True,
|
||||
"UserId": "{UserId}",
|
||||
"Fields": api.info(),
|
||||
},
|
||||
}
|
||||
for items in _get_items(query):
|
||||
yield items
|
||||
|
||||
|
||||
def get_episode_by_show(show_id):
|
||||
|
||||
query = {
|
||||
|
|
@ -213,9 +228,16 @@ def _get_items(query, server_id=None):
|
|||
test_params["Limit"] = 1
|
||||
test_params["EnableTotalRecordCount"] = True
|
||||
|
||||
items["TotalRecordCount"] = _get(url, test_params, server_id=server_id)[
|
||||
"TotalRecordCount"
|
||||
]
|
||||
response = _get(url, test_params, server_id=server_id)
|
||||
|
||||
if "TotalRecordCount" in response:
|
||||
items["TotalRecordCount"] = response["TotalRecordCount"]
|
||||
elif isinstance(response, list):
|
||||
yield {
|
||||
"Items": response,
|
||||
"TotalRecordCount": len(response),
|
||||
"StartIndex": 0,
|
||||
}
|
||||
|
||||
except Exception as error:
|
||||
LOG.exception(
|
||||
|
|
|
|||
|
|
@ -341,6 +341,8 @@ class FullSync(object):
|
|||
message=movie["Name"],
|
||||
)
|
||||
obj.movie(movie)
|
||||
obj.add_extras(movie)
|
||||
|
||||
processed_ids.append(movie["Id"])
|
||||
|
||||
with self.video_database_locks() as (videodb, jellyfindb):
|
||||
|
|
|
|||
|
|
@ -740,6 +740,8 @@ class UpdateWorker(threading.Thread):
|
|||
LOG.debug("{} - {}".format(item["Type"], item["Name"]))
|
||||
if item["Type"] == "Movie":
|
||||
movies.movie(item)
|
||||
movies.remove_extras(item)
|
||||
movies.add_extras(item)
|
||||
elif item["Type"] == "BoxSet":
|
||||
movies.boxset(item)
|
||||
elif item["Type"] == "Series":
|
||||
|
|
|
|||
|
|
@ -80,6 +80,22 @@ class Artwork(object):
|
|||
elif artwork.get(art):
|
||||
self.update(*(artwork[art],) + args + (KODI[art],))
|
||||
|
||||
def add_extra(self, artwork, *args):
|
||||
"""Add all artworks."""
|
||||
KODI = {
|
||||
"Thumb": ["landscape", "thumb", "poster"],
|
||||
"Primary": ["landscape", "thumb", "poster"],
|
||||
}
|
||||
|
||||
for art in KODI:
|
||||
if art == "Primary":
|
||||
for kodi_image in KODI["Primary"]:
|
||||
self.update(*(artwork["Primary"],) + args + (kodi_image,))
|
||||
|
||||
elif art == "Thumb":
|
||||
for kodi_image in KODI["Thumb"]:
|
||||
self.update(*(artwork["Thumb"],) + args + (kodi_image,))
|
||||
|
||||
def delete(self, *args):
|
||||
"""Delete artwork from kodi database"""
|
||||
self.cursor.execute(QU.delete_art, args)
|
||||
|
|
|
|||
|
|
@ -47,14 +47,25 @@ class Movies(Kodi):
|
|||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
def create_entry_extra(self):
|
||||
self.cursor.execute(QU.create_extra)
|
||||
|
||||
return self.cursor.fetchone()[0] + 1
|
||||
|
||||
def get(self, *args):
|
||||
try:
|
||||
self.cursor.execute(QU.get_movie, args)
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def get_extra(self, *args):
|
||||
try:
|
||||
self.cursor.execute(QU.get_extra, args)
|
||||
return self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
def add(self, *args):
|
||||
self.cursor.execute(QU.add_movie, args)
|
||||
|
||||
|
|
@ -66,6 +77,18 @@ class Movies(Kodi):
|
|||
def update(self, *args):
|
||||
self.cursor.execute(QU.update_movie, args)
|
||||
|
||||
def update_extra(self, *args):
|
||||
self.cursor.execute(QU.update_video_version_extra, args)
|
||||
|
||||
def update_video_version_type_extra(self, *args):
|
||||
self.cursor.execute(QU.update_video_version_type_extra, args)
|
||||
|
||||
def delete_extra(self, kodi_id, file_id):
|
||||
self.cursor.execute(QU.delete_video_version_extra, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_video_version_type_extra, (kodi_id,))
|
||||
self.cursor.execute(QU.delete_file, (file_id,))
|
||||
self.cursor.execute(QU.delete_streams, (file_id,))
|
||||
|
||||
def delete(self, kodi_id, file_id):
|
||||
|
||||
self.cursor.execute(QU.delete_movie, (kodi_id,))
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ create_movie = """
|
|||
SELECT coalesce(max(idMovie), 0)
|
||||
FROM movie
|
||||
"""
|
||||
# Kodi extras start at id 40801
|
||||
create_extra = """
|
||||
SELECT coalesce(max(id), 40800)
|
||||
FROM videoversiontype
|
||||
"""
|
||||
create_musicvideo = """
|
||||
SELECT coalesce(max(idMVideo), 0)
|
||||
FROM musicvideo
|
||||
|
|
@ -120,6 +125,12 @@ FROM movie
|
|||
WHERE idMovie = ?
|
||||
"""
|
||||
get_movie_obj = ["{MovieId}"]
|
||||
get_extra = """
|
||||
SELECT *
|
||||
FROM videoversiontype
|
||||
WHERE id = ?
|
||||
"""
|
||||
get_extra_obj = ["{ExtraId}"]
|
||||
get_rating = """
|
||||
SELECT rating_id
|
||||
FROM rating
|
||||
|
|
@ -418,6 +429,17 @@ add_video_version_obj = [
|
|||
"{VideoVersionItemType}",
|
||||
40400,
|
||||
]
|
||||
update_video_version_extra = """
|
||||
INSERT OR REPLACE INTO videoversion(idFile, idMedia, media_type, itemType, idType)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
update_video_version_extra_obj = [
|
||||
"{FileId}",
|
||||
"{MovieId}",
|
||||
"movie",
|
||||
"1",
|
||||
"{VideoVersionIdType}",
|
||||
]
|
||||
get_videoversion_itemtype = """
|
||||
SELECT itemType FROM videoversiontype WHERE id = ?
|
||||
"""
|
||||
|
|
@ -653,6 +675,16 @@ update_unique_id_episode_obj = [
|
|||
"{ProviderName}",
|
||||
"{Unique}",
|
||||
]
|
||||
update_video_version_type_extra = """
|
||||
INSERT OR REPLACE INTO videoversiontype(id, name, owner, itemType)
|
||||
VALUES (?, ?, ?, ?)
|
||||
"""
|
||||
update_video_version_type_extra_obj = [
|
||||
"{VideoVersionIdType}",
|
||||
"{Title}",
|
||||
"1",
|
||||
"1",
|
||||
]
|
||||
update_country = """
|
||||
INSERT OR REPLACE INTO country_link(country_id, media_id, media_type)
|
||||
VALUES (?, ?, ?)
|
||||
|
|
@ -797,6 +829,7 @@ DELETE FROM movie
|
|||
WHERE idMovie = ?
|
||||
"""
|
||||
delete_movie_obj = ["{KodiId}", "{FileId}"]
|
||||
delete_extra_obj = ["{KodiId}", "{FileId}"]
|
||||
delete_video_version = """
|
||||
DELETE FROM videoversion
|
||||
WHERE idFile = ?
|
||||
|
|
@ -840,6 +873,16 @@ WHERE media_id = ?
|
|||
AND media_type = ?
|
||||
AND type LIKE ?
|
||||
"""
|
||||
delete_video_version_extra = """
|
||||
DELETE FROM videoversion
|
||||
WHERE idType = ?
|
||||
AND itemType = 1
|
||||
"""
|
||||
delete_video_version_type_extra = """
|
||||
DELETE FROM videoversiontype
|
||||
WHERE id = ?
|
||||
AND itemType = 1
|
||||
"""
|
||||
get_missing_versions = """
|
||||
SELECT idFile,idMovie
|
||||
FROM movie
|
||||
|
|
|
|||
|
|
@ -189,6 +189,172 @@ class Movies(KodiDb):
|
|||
obj["Title"],
|
||||
)
|
||||
|
||||
def add_extras(self, item):
|
||||
for extras in server.get_extra_by_movie(item["Id"]):
|
||||
for extra in extras["Items"]:
|
||||
LOG.debug("Extras: {}".format(extra))
|
||||
extra["MovieId"] = item["Id"]
|
||||
if extra.get("Path"):
|
||||
self.extra(extra)
|
||||
|
||||
@stop
|
||||
@jellyfin_item
|
||||
def extra(self, item, e_item):
|
||||
"""If item does not exist, entry will be added.
|
||||
If item exists, entry will be updated.
|
||||
|
||||
Create additional entry for widgets.
|
||||
"""
|
||||
server_address = self.server.auth.get_server_info(self.server.auth.server_id)[
|
||||
"address"
|
||||
]
|
||||
API = api.API(item, server_address)
|
||||
obj = self.objects.map(item, "Extra")
|
||||
update = True
|
||||
|
||||
if "Location" in obj and obj["Location"] == "Virtual":
|
||||
LOG.info("Skipping virtual item %s: %s", obj["Title"], obj["Id"])
|
||||
return
|
||||
|
||||
try:
|
||||
obj["ExtraId"] = e_item[0]
|
||||
obj["FileId"] = e_item[1]
|
||||
obj["PathId"] = e_item[2]
|
||||
obj["LibraryId"] = e_item[6]
|
||||
obj["LibraryName"] = self.jellyfin_db.get_view_name(obj["LibraryId"])
|
||||
except TypeError:
|
||||
update = False
|
||||
LOG.debug("ExtraId %s not found", obj["Id"])
|
||||
|
||||
temp_obj = dict(obj)
|
||||
temp_obj["Id"] = obj["MovieId"]
|
||||
|
||||
library = self.library or find_library(self.server, temp_obj)
|
||||
if not library:
|
||||
# This item doesn't belong to a whitelisted library
|
||||
return
|
||||
|
||||
obj["ExtraId"] = self.create_entry_extra()
|
||||
obj["LibraryId"] = library["Id"]
|
||||
obj["LibraryName"] = library["Name"]
|
||||
|
||||
else:
|
||||
if self.get_extra(*values(obj, QU.get_extra_obj)) is None:
|
||||
|
||||
update = False
|
||||
LOG.info(
|
||||
"ExtraId %s missing from kodi. repairing the entry.",
|
||||
obj["ExtraId"],
|
||||
)
|
||||
|
||||
try:
|
||||
temp_obj = dict(obj)
|
||||
temp_obj["Id"] = obj["MovieId"]
|
||||
temp_obj["MovieId"] = self.jellyfin_db.get_item_by_id(
|
||||
*values(temp_obj, QUEM.get_item_obj)
|
||||
)[0]
|
||||
except TypeError:
|
||||
LOG.info("Failed to process extra %s to movie.", temp_obj["Title"])
|
||||
|
||||
return update
|
||||
|
||||
obj["Path"] = API.get_file_path(obj["Path"])
|
||||
obj["Index"] = obj["Index"] or -1
|
||||
obj["Resume"] = API.adjust_resume((obj["Resume"] or 0) / 10000000.0)
|
||||
obj["Runtime"] = round(float((obj["Runtime"] or 0) / 10000000.0), 6)
|
||||
obj["DateAdded"] = Local(obj["DateAdded"]).split(".")[0].replace("T", " ")
|
||||
obj["DatePlayed"] = (
|
||||
None
|
||||
if not obj["DatePlayed"]
|
||||
else Local(obj["DatePlayed"]).split(".")[0].replace("T", " ")
|
||||
)
|
||||
obj["PlayCount"] = API.get_playcount(obj["Played"], obj["PlayCount"])
|
||||
obj["Artwork"] = API.get_all_artwork(
|
||||
self.objects.map(item, "ArtworkParent"), True
|
||||
)
|
||||
obj["Video"] = API.video_streams(obj["Video"] or [], obj["Container"])
|
||||
obj["Audio"] = API.audio_streams(obj["Audio"] or [])
|
||||
obj["Streams"] = API.media_streams(obj["Video"], obj["Audio"], obj["Subtitles"])
|
||||
|
||||
self.get_path_filename(obj)
|
||||
|
||||
obj["MovieId"] = temp_obj["MovieId"]
|
||||
|
||||
if obj["Countries"]:
|
||||
self.add_countries(*values(obj, QU.update_country_obj))
|
||||
|
||||
tags = list(obj["Tags"] or [])
|
||||
tags.append(obj["LibraryName"])
|
||||
|
||||
if obj["Favorite"]:
|
||||
tags.append("Favorite extras")
|
||||
|
||||
obj["Tags"] = tags
|
||||
|
||||
self.extra_add(obj)
|
||||
|
||||
self.update_file(*values(obj, QU.update_file_obj))
|
||||
self.add_playstate(*values(obj, QU.add_bookmark_obj))
|
||||
self.add_streams(*values(obj, QU.add_streams_obj))
|
||||
self.artwork.add_extra(obj["Artwork"], obj["FileId"], "videoversion")
|
||||
self.item_ids.append(obj["Id"])
|
||||
|
||||
return not update
|
||||
|
||||
def extra_add(self, obj):
|
||||
"""Add object to kodi."""
|
||||
obj["PathId"] = self.add_path(*values(obj, QU.add_path_obj))
|
||||
obj["FileId"] = self.add_file(*values(obj, QU.add_file_obj))
|
||||
obj["VideoVersionIdType"] = obj["ExtraId"]
|
||||
|
||||
self.update_extra(*values(obj, QU.update_video_version_extra_obj))
|
||||
self.update_video_version_type_extra(
|
||||
*values(obj, QU.update_video_version_type_extra_obj)
|
||||
)
|
||||
self.jellyfin_db.add_reference(*values(obj, QUEM.add_reference_extra_obj))
|
||||
|
||||
LOG.debug(
|
||||
"ADD extra [%s/%s/%s] %s: %s",
|
||||
obj["PathId"],
|
||||
obj["FileId"],
|
||||
obj["ExtraId"],
|
||||
obj["Id"],
|
||||
obj["Title"],
|
||||
)
|
||||
|
||||
@stop
|
||||
@jellyfin_item
|
||||
def remove_extras(self, item, e_item):
|
||||
"""Remove movieid, fileid, jellyfin reference.
|
||||
Remove artwork, boxset
|
||||
"""
|
||||
|
||||
try:
|
||||
item["KodiId"] = e_item[0]
|
||||
item["FileId"] = e_item[1]
|
||||
item["Media"] = e_item[4]
|
||||
except TypeError:
|
||||
return
|
||||
|
||||
for extra in self.jellyfin_db.get_item_by_parent_id(
|
||||
*values(item, QUEM.get_item_by_parent_extra_obj)
|
||||
):
|
||||
temp_obj = dict()
|
||||
temp_obj["Id"] = extra[0]
|
||||
temp_obj["KodiId"] = extra[1]
|
||||
temp_obj["FileId"] = extra[2]
|
||||
|
||||
self.delete_extra(*values(temp_obj, QU.delete_extra_obj))
|
||||
|
||||
self.jellyfin_db.remove_item(*values(temp_obj, QUEM.delete_item_obj))
|
||||
|
||||
LOG.debug(
|
||||
"DELETE Extra [%s/%s] %s",
|
||||
temp_obj["FileId"],
|
||||
temp_obj["KodiId"],
|
||||
temp_obj["Id"],
|
||||
)
|
||||
|
||||
def trailer(self, obj):
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -54,6 +54,47 @@
|
|||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Played": "UserData/Played"
|
||||
},
|
||||
"Extra": {
|
||||
"Id": "Id",
|
||||
"MovieId": "MovieId",
|
||||
"Index": "IndexNumber",
|
||||
"Title": "MediaSources/0/Name",
|
||||
"SortTitle": "SortName",
|
||||
"Path": "Path",
|
||||
"Location": "LocationType",
|
||||
"Genres": "Genres",
|
||||
"UniqueId": "ProviderIds/Imdb",
|
||||
"Rating": "CommunityRating",
|
||||
"Year": "ProductionYear",
|
||||
"Premiere": "PremiereDate,ProductionYear",
|
||||
"Plot": "Overview",
|
||||
"People": "People",
|
||||
"Writers": "People:?Type=Writer$Name",
|
||||
"Directors": "People:?Type=Director$Name",
|
||||
"Cast": "People:?Type=Actor$Name",
|
||||
"Tagline": "Taglines/0",
|
||||
"Mpaa": "OfficialRating",
|
||||
"Country": "ProductionLocations/0",
|
||||
"Countries": "ProductionLocations",
|
||||
"Studios": "Studios:?$Name",
|
||||
"Studio": "Studios/0/Name",
|
||||
"Runtime": "RunTimeTicks,CumulativeRunTimeTicks",
|
||||
"LocalTrailer": "LocalTrailerCount",
|
||||
"Trailer": "RemoteTrailers/0/Url",
|
||||
"DateAdded": "DateCreated",
|
||||
"Played": "UserData/Played",
|
||||
"PlayCount": "UserData/PlayCount",
|
||||
"DatePlayed": "UserData/LastPlayedDate",
|
||||
"Favorite": "UserData/IsFavorite",
|
||||
"Resume": "UserData/PlaybackPositionTicks",
|
||||
"Tags": "Tags",
|
||||
"Subtitles": "MediaSources/0/MediaStreams:?Type=Subtitle$Language",
|
||||
"Audio": "MediaSources/0/MediaStreams:?Type=Audio",
|
||||
"Video": "MediaSources/0/MediaStreams:?Type=Video",
|
||||
"Container": "MediaSources/0/Container",
|
||||
"JellyfinParentId": "ParentId",
|
||||
"CriticRating": "CriticRating"
|
||||
},
|
||||
"Boxset": {
|
||||
"Id": "Id",
|
||||
"Title": "Name",
|
||||
|
|
@ -360,4 +401,4 @@
|
|||
"rating": "CommunityRating",
|
||||
"firstaired": "ProductionYear"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue