mirror of
https://github.com/tubearchivist/jellyfin.git
synced 2024-11-17 01:10:14 +00:00
174 lines
5.8 KiB
Python
174 lines
5.8 KiB
Python
|
"""set metadata to shows"""
|
||
|
|
||
|
import base64
|
||
|
import os
|
||
|
from time import sleep
|
||
|
|
||
|
from src.config import get_config
|
||
|
from src.connect import Jellyfin, TubeArchivist
|
||
|
from src.episode import Episode
|
||
|
|
||
|
|
||
|
class Library:
|
||
|
"""grouped series"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.yt_collection = self.get_yt_collection()
|
||
|
|
||
|
def get_yt_collection(self):
|
||
|
"""get collection id for youtube folder"""
|
||
|
path = "Items?Recursive=true&includeItemTypes=Folder"
|
||
|
folders = Jellyfin().get(path)
|
||
|
for folder in folders["Items"]:
|
||
|
if folder.get("Name").lower() == "youtube":
|
||
|
return folder.get("Id")
|
||
|
|
||
|
raise ValueError("youtube folder not found")
|
||
|
|
||
|
def validate_series(self):
|
||
|
"""validate all series"""
|
||
|
all_shows = self._get_all_series()
|
||
|
for show in all_shows["Items"]:
|
||
|
show_handler = Show(show)
|
||
|
folders = show_handler.create_folders()
|
||
|
show_handler.validate_show()
|
||
|
show_handler.validate_episodes()
|
||
|
show_handler.delete_folders(folders)
|
||
|
|
||
|
self.set_collection_art()
|
||
|
|
||
|
def _get_all_series(self):
|
||
|
"""get all shows indexed in jf"""
|
||
|
path = f"Items?Recursive=true&IncludeItemTypes=Series&fields=ParentId,Path&ParentId={self.yt_collection}" # noqa: E501
|
||
|
all_shows = Jellyfin().get(path)
|
||
|
|
||
|
return all_shows
|
||
|
|
||
|
def set_collection_art(self):
|
||
|
"""set collection ta art"""
|
||
|
with open("assets/collection-art.jpg", "rb") as f:
|
||
|
asset = f.read()
|
||
|
|
||
|
folders = Jellyfin().get("Library/MediaFolders")
|
||
|
for folder in folders["Items"]:
|
||
|
if folder.get("Name").lower() == "youtube":
|
||
|
jf_id = folder.get("Id")
|
||
|
path = f"Items/{jf_id}/Images/Primary"
|
||
|
Jellyfin().post_img(path, base64.b64encode(asset))
|
||
|
|
||
|
|
||
|
class Show:
|
||
|
"""interact with a single show"""
|
||
|
|
||
|
def __init__(self, show):
|
||
|
self.show = show
|
||
|
|
||
|
def _get_all_episodes(self):
|
||
|
"""get all episodes of show"""
|
||
|
series_id = self.show.get("Id")
|
||
|
path = f"Shows/{series_id}/Episodes?fields=Path"
|
||
|
all_episodes = Jellyfin().get(path)
|
||
|
|
||
|
return all_episodes["Items"]
|
||
|
|
||
|
def _get_expected_seasons(self):
|
||
|
"""get all expected seasons"""
|
||
|
episodes = self._get_all_episodes()
|
||
|
all_years = {os.path.split(i["Path"])[-1][:4] for i in episodes}
|
||
|
|
||
|
return all_years
|
||
|
|
||
|
def _get_existing_seasons(self):
|
||
|
"""get all seasons indexed of series"""
|
||
|
series_id = self.show.get("Id")
|
||
|
path = f"Shows/{series_id}/Seasons"
|
||
|
all_seasons = Jellyfin().get(path)
|
||
|
|
||
|
return [str(i.get("IndexNumber")) for i in all_seasons["Items"]]
|
||
|
|
||
|
def create_folders(self):
|
||
|
"""create season folders if needed"""
|
||
|
all_expected = self._get_expected_seasons()
|
||
|
all_existing = self._get_existing_seasons()
|
||
|
|
||
|
base = get_config().get("ta_video_path")
|
||
|
channel_name = os.path.split(self.show["Path"])[-1]
|
||
|
folders = []
|
||
|
for year in all_expected:
|
||
|
if year not in all_existing:
|
||
|
path = os.path.join(base, channel_name, year)
|
||
|
os.mkdir(path)
|
||
|
folders.append(path)
|
||
|
|
||
|
self._wait_for_seasons()
|
||
|
return folders
|
||
|
|
||
|
def delete_folders(self, folders):
|
||
|
"""delete temporary folders created"""
|
||
|
for folder in folders:
|
||
|
os.removedirs(folder)
|
||
|
|
||
|
def _wait_for_seasons(self):
|
||
|
"""wait for seasons to be created"""
|
||
|
jf_id = self.show["Id"]
|
||
|
path = f"Items/{jf_id}/Refresh?Recursive=true&ImageRefreshMode=Default&MetadataRefreshMode=Default" # noqa: E501
|
||
|
Jellyfin().post(path, False)
|
||
|
for _ in range(12):
|
||
|
all_existing = set(self._get_existing_seasons())
|
||
|
all_expected = self._get_expected_seasons()
|
||
|
if all_expected.issubset(all_existing):
|
||
|
return
|
||
|
|
||
|
print(f"[setup][{jf_id}] waiting for seasons to be created")
|
||
|
sleep(5)
|
||
|
|
||
|
raise TimeoutError("timeout reached for creating season folder")
|
||
|
|
||
|
def validate_show(self):
|
||
|
"""set show metadata"""
|
||
|
ta_channel = self._get_ta_channel()
|
||
|
self.update_metadata(ta_channel)
|
||
|
self.update_artwork(ta_channel)
|
||
|
|
||
|
def _get_ta_channel(self):
|
||
|
"""get ta channel metadata"""
|
||
|
episode = self._get_all_episodes()[0]
|
||
|
youtube_id = os.path.split(episode["Path"])[-1][9:20]
|
||
|
path = f"/video/{youtube_id}"
|
||
|
ta_video = TubeArchivist().get(path)
|
||
|
|
||
|
return ta_video.get("channel")
|
||
|
|
||
|
def update_metadata(self, ta_channel):
|
||
|
"""update channel metadata"""
|
||
|
path = "Items/" + self.show["Id"]
|
||
|
data = {
|
||
|
"Id": self.show["Id"],
|
||
|
"Name": ta_channel.get("channel_name"),
|
||
|
"Overview": ta_channel.get("channel_description"),
|
||
|
"Genres": [],
|
||
|
"Tags": [],
|
||
|
"ProviderIds": {},
|
||
|
}
|
||
|
Jellyfin().post(path, data)
|
||
|
|
||
|
def update_artwork(self, ta_channel):
|
||
|
"""set channel artwork"""
|
||
|
jf_id = self.show["Id"]
|
||
|
jf_handler = Jellyfin()
|
||
|
primary = TubeArchivist().get_thumb(ta_channel["channel_thumb_url"])
|
||
|
jf_handler.post_img(f"Items/{jf_id}/Images/Primary", primary)
|
||
|
jf_handler.post_img(f"Items/{jf_id}/Images/Logo", primary)
|
||
|
banner = TubeArchivist().get_thumb(ta_channel["channel_banner_url"])
|
||
|
jf_handler.post_img(f"Items/{jf_id}/Images/Banner", banner)
|
||
|
tvart = TubeArchivist().get_thumb(ta_channel["channel_tvart_url"])
|
||
|
jf_handler.post_img(f"Items/{jf_id}/Images/Backdrop", tvart)
|
||
|
|
||
|
def validate_episodes(self):
|
||
|
"""sync all episodes"""
|
||
|
all_episodes = self._get_all_episodes()
|
||
|
for video in all_episodes:
|
||
|
youtube_id = os.path.split(video["Path"])[-1][9:20]
|
||
|
jf_id = video["Id"]
|
||
|
Episode(youtube_id, jf_id).sync()
|