Initial commit

This commit is contained in:
2024-12-30 11:42:12 -07:00
commit 09ba4114c1
86 changed files with 7522 additions and 0 deletions

3
jellyfin/.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
__pycache__/
Dockerifile
*.http

9
jellyfin/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM python:3.10
RUN pip install pydantic requests python-dotenv
COPY jellyfin /app/jellyfin
WORKDIR /app
ENTRYPOINT [ "python" ]

View File

@@ -0,0 +1,85 @@
import os
import requests
import json
from dotenv import load_dotenv
load_dotenv()
# Set your Jellyfin server address and API key here
server_address = "https://jellyfin.alexmickelson.guru"
api_key = os.environ["JELLYFIN_TOKEN"]
# Set the API endpoints to get all songs and create a playlist
songs_endpoint = (
"/Users/b30951b36b37400498dbfd182d49a42e/Items"
+ "?SortBy=DateCreated,SortName"
+ "&SortOrder=Descending"
+ "&IncludeItemTypes=Audio"
+ "&Recursive=true"
+ "&Fields=AudioInfo,ParentId"
+ "&StartIndex=0"
+ "&ImageTypeLimit=1"
+ "&EnableImageTypes=Primary"
+ "&Limit=100"
+ "&ParentId=7e64e319657a9516ec78490da03edccb"
)
songs_endpoint = "/Users/b30951b36b37400498dbfd182d49a42e/Items"
# Set the parameters for the API request to get all songs
params = {
"api_key": api_key,
"SortBy": "SortName",
"ParentId": "7e64e319657a9516ec78490da03edccb",
}
# Make the API request to get all songs
response = requests.get(server_address + songs_endpoint, params=params)
# Parse the JSON response
data = json.loads(response.text)
# # Loop through the songs and print their names
for song in data["Items"]:
print(song["Name"], song["Id"])
# Create a list of all song IDs
song_ids = [song["Id"] for song in data["Items"]]
ids = ",".join(song_ids)
# print(ids)
playlist_data = {
"Name": "All Songs",
"UserId": "b30951b36b37400498dbfd182d49a42e",
"Ids": ids,
"MediaType": "Audio",
}
headers = {"Content-type": "application/json"}
params = {"api_key": api_key}
playlist_endpoint = "/Playlists"
# https://jellyfin.alexmickelson.guru/Playlists?Name=test playlist&Ids=f78ddd409c5ebb2405f5477d15e8e23c&userId=b30951b36b37400498dbfd182d49a42e
response = requests.post(
server_address + playlist_endpoint,
headers=headers,
params=params,
data=json.dumps(playlist_data),
)
# print(response.text)
playlist_id = response.json()["Id"]
# add_song_url = f"/Playlists/{playlist_id}/Items"
# params = {"api_key": api_key}
# body = {
# "Ids": ids,
# "UserId": "b30951b36b37400498dbfd182d49a42e",
# "MediaType": "Audio",
# }
# response = requests.post(
# server_address + add_song_url, headers=headers, params=params, json=body
# )
# print(response.text)
# print(response.status_code)
# print(response.headers)
# jellyfin_service.logout()

50
jellyfin/jellyfin.http Normal file
View File

@@ -0,0 +1,50 @@
# https://jellyfin.alexmickelson.guru/api-docs/swagger/index.html
# https://gist.github.com/nielsvanvelzen/ea047d9028f676185832e51ffaf12a6f
GET https://jellyfin.alexmickelson.guru/Users/b30951b36b37400498dbfd182d49a42e/Items
?SortBy=SortName&SortOrder=Ascending
&IncludeItemTypes=Playlist
&Recursive=true
&Fields=PrimaryImageAspectRatio,SortName,CanDelete
&StartIndex=0
&api_key={{$dotenv JELLYFIN_TOKEN}}
###
GET https://jellyfin.alexmickelson.guru/Users/b30951b36b37400498dbfd182d49a42e/Items
?IncludeItemTypes=Playlist
&Recursive=true
&ParentId=7e64e319657a9516ec78490da03edccb
&api_key={{$dotenv JELLYFIN_TOKEN}}
###
# get items from unindexed playlist
GET https://jellyfin.alexmickelson.guru/Playlists/2f191b23f0a49e70d6f90e9d82e408c6/Items
?Fields=PrimaryImageAspectRatio
&EnableImageTypes=Primary,Backdrop,Banner,Thumb
&UserId=b30951b36b37400498dbfd182d49a42e
&api_key={{$dotenv JELLYFIN_TOKEN}}
### remove item from unindexed
DELETE https://jellyfin.alexmickelson.guru/Playlists/2f191b23f0a49e70d6f90e9d82e408c6/Items
?EntryIds=186f4d63492b405b97865ff9a99ef3ab
&userId=b30951b36b37400498dbfd182d49a42e
Authorization: MediaBrowser Client="scriptclient", Device="script", DeviceId="asdfasdfasdfasdfasdf", Version="1.0.0", Token="f313e2045fc34ce3ac510ce9ba2be1fc"
### get all playlists
GET https://jellyfin.alexmickelson.guru/Users/b30951b36b37400498dbfd182d49a42e/Items
?api_key={{$dotenv JELLYFIN_TOKEN}}
&ParentId=29772619d609592f4cdb3fc34a6ec97d
### get token by user/pass
POST https://jellyfin.alexmickelson.guru/Users/AuthenticateByName
Content-Type: application/json
Authorization: MediaBrowser Client="scriptclient", Device="script", DeviceId="asdfasdfasdfasdfasdf", Version="1.0.0", Token=""
{
"Username": "alex",
"Pw": "{{$dotenv JELLYFIN_PASSWORD}}"
}
###
POST https://jellyfin.alexmickelson.guru/Sessions/Logout
Authorization: MediaBrowser Client="scriptclient", Device="script", DeviceId="asdfasdfasdfasdfasdf", Version="1.0.0", Token="c704c71900cc41d2a454a4f3b5132778"

View File

@@ -0,0 +1,163 @@
from functools import lru_cache
import os
from pprint import pprint
from typing import List, Optional
from pydantic import BaseModel, Field
import requests
from dotenv import load_dotenv
load_dotenv()
server_address = "https://jellyfin.alexmickelson.guru"
# api_key = os.environ["JELLYFIN_TOKEN"]
username = os.environ["JELLYFIN_USER"]
password = os.environ["JELLYFIN_PASSWORD"]
alex_user_id = "b30951b36b37400498dbfd182d49a42e"
all_songs_playlist_id = "2e176c02e7cc7f460c40bb1510723510"
unindexed_playlist_id = "2f191b23f0a49e70d6f90e9d82e408c6"
class Song(BaseModel):
Id: str
Name: str
Album: Optional[str] = Field(default=None)
Artists: Optional[List[str]] = Field(default=None)
class PlaylistSong(BaseModel):
Id: str
PlaylistItemId: str
Name: str
Album: Optional[str] = Field(default=None)
Artists: Optional[List[str]] = Field(default=None)
class Playlist(BaseModel):
Id: str
Name: str
Songs: List[PlaylistSong]
@lru_cache(maxsize=10)
def get_token():
auth_endpoint = f"{server_address}/Users/AuthenticateByName"
body = {"Username": username, "Pw": password}
response = requests.post(
auth_endpoint,
json=body,
headers={
"Content-Type": "application/json",
"Authorization": 'MediaBrowser Client="scriptclient", Device="script", DeviceId="testscriptasdfasdfasdf", Version="1.0.0", Token=""',
},
)
return response.json()["AccessToken"]
def get_auth_headers():
token = get_token()
return {
"Authorization": f'MediaBrowser Client="scriptclient", Device="script", DeviceId="asdfasdfasdfasdfasdf", Version="1.0.0", Token="{token}"'
}
def get_all_songs():
songs_endpoint = (
f"{server_address}/Users/{alex_user_id}/Items"
# + "?SortBy=DateCreated,SortName"
# + "&SortOrder=Descending"
+ "?IncludeItemTypes=Audio"
+ "&Recursive=true"
# + "&Fields=AudioInfo,ParentId"
# + "&StartIndex=0"
# + "&ImageTypeLimit=1"
# + "&EnableImageTypes=Primary"
# + "&Limit=100"
+ "&ParentId=7e64e319657a9516ec78490da03edccb"
)
params = {
"SortBy": "SortName",
}
response = requests.get(songs_endpoint, params=params, headers=get_auth_headers())
if not response.ok:
print(response.status_code)
print(response.text)
data = response.json()
songs = [Song(**song) for song in data["Items"]]
return songs
def add_song_to_playlist(song_id: str, playlist_id: str):
add_song_endpoint = f"{server_address}/Playlists/{playlist_id}/Items"
params = {"ids": song_id, "userId": alex_user_id}
response = requests.post(
add_song_endpoint, params=params, headers=get_auth_headers()
)
if not response.ok:
print(response.status_code)
print(response.text)
def remove_song_from_playlist(song_playlist_id: str, playlist_id: str):
url = f"{server_address}/Playlists/{playlist_id}/Items"
params = {
"EntryIds": song_playlist_id,
"userId": alex_user_id,
} # , "apiKey": api_key}
response = requests.delete(url, params=params, headers=get_auth_headers())
if not response.ok:
print(response.status_code)
print(response.text)
print(response.url)
print(song_playlist_id)
print(playlist_id)
print(response.content)
pprint(response.request.headers)
def get_songs_in_playlist(playlist_id: str):
url = f"{server_address}/Playlists/{playlist_id}/Items"
params = {"userId": alex_user_id}
response = requests.get(url, params=params, headers=get_auth_headers())
if not response.ok:
print(response.status_code)
print(response.text)
raise Exception(f"Error getting songs in playlist: {playlist_id}")
data = response.json()
songs = [PlaylistSong.parse_obj(song) for song in data["Items"]]
return songs
def get_all_playlists():
url = f"{server_address}/Users/{alex_user_id}/Items"
params = {
"IncludeItemTypes": "Playlist",
"Recursive": True,
"ParentId": "29772619d609592f4cdb3fc34a6ec97d",
}
response = requests.get(url, params=params, headers=get_auth_headers())
if not response.ok:
print(response.status_code)
print(response.text)
raise Exception("Error getting all playlists")
data = response.json()
print("got all playlists", len(data["Items"]))
playlists: List[Playlist] = []
for playlist in data["Items"]:
songs = get_songs_in_playlist(playlist["Id"])
playlist_object = Playlist(
Id=playlist["Id"], Name=playlist["Name"], Songs=songs
)
playlists.append(playlist_object)
return playlists
def logout():
url = f"{server_address}/Sessions/Logout"
response = requests.post(url, headers=get_auth_headers())
print("ending session: " + str(response.status_code))

View File

@@ -0,0 +1,20 @@
from jellyfin import jellyfin_service
if __name__ == "__main__":
all_songs = jellyfin_service.get_all_songs()
print("total songs", len(all_songs))
playlist_songs = jellyfin_service.get_songs_in_playlist(
jellyfin_service.all_songs_playlist_id
)
print("songs already in playlist", len(playlist_songs))
playlist_ids = [s.Id for s in playlist_songs]
for song in all_songs:
if song.Id not in playlist_ids:
print(f"adding song {song.Name} to playlist")
jellyfin_service.add_song_to_playlist(
song.Id, jellyfin_service.all_songs_playlist_id
)
jellyfin_service.logout()

View File

@@ -0,0 +1,34 @@
from pprint import pprint
from jellyfin import jellyfin_service
if __name__ == "__main__":
all_songs = jellyfin_service.get_all_songs()
playlists = jellyfin_service.get_all_playlists()
song_ids_in_playlist = list(
set(
song.Id
for playlist in playlists
for song in playlist.Songs
if playlist.Id != jellyfin_service.unindexed_playlist_id
and playlist.Id != jellyfin_service.all_songs_playlist_id
)
)
unindexed_playlist = next(
p for p in playlists if p.Id == jellyfin_service.unindexed_playlist_id
)
unindexed_songs_ids = [song.Id for song in unindexed_playlist.Songs]
for song in all_songs:
if song.Id not in song_ids_in_playlist:
if song.Id not in unindexed_songs_ids:
print(f"adding {song.Name} to unindexed playlist")
jellyfin_service.add_song_to_playlist(
song.Id, jellyfin_service.unindexed_playlist_id
)
for song in unindexed_playlist.Songs:
if song.Id in song_ids_in_playlist:
print(f"removing {song.Name} from unindexed playlist")
# pprint(song)
jellyfin_service.remove_song_from_playlist(
song.PlaylistItemId, jellyfin_service.unindexed_playlist_id
)
jellyfin_service.logout()