Compare commits
5 Commits
a04becb616
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2982d40811 | |||
| d9194dcd76 | |||
| 6e9f348089 | |||
| c5fbad8ad6 | |||
| 196767001c |
45
main.py
45
main.py
@@ -38,9 +38,14 @@ def run_pls_command(playlist_id):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_for_filename(name):
|
||||||
|
"""Sanitizes a string to be safe as a filename component."""
|
||||||
|
return name.replace('/', '_').replace('\\', '_')
|
||||||
|
|
||||||
|
|
||||||
def save_playlist_file(playlist_dir, playlist_name, content):
|
def save_playlist_file(playlist_dir, playlist_name, content):
|
||||||
"""Saves the transformed playlist content to a file."""
|
"""Saves the transformed playlist content to a file."""
|
||||||
filename = f"{playlist_name}.m3u8"
|
filename = f"{sanitize_for_filename(playlist_name)}.m3u8"
|
||||||
filepath = os.path.join(playlist_dir, filename)
|
filepath = os.path.join(playlist_dir, filename)
|
||||||
try:
|
try:
|
||||||
with open(filepath, 'w', encoding='utf-8') as f:
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||||||
@@ -52,7 +57,7 @@ def save_playlist_file(playlist_dir, playlist_name, content):
|
|||||||
|
|
||||||
def delete_playlist_file(playlist_dir, playlist_name):
|
def delete_playlist_file(playlist_dir, playlist_name):
|
||||||
"""Deletes a playlist file."""
|
"""Deletes a playlist file."""
|
||||||
filename = f"{playlist_name}.m3u8"
|
filename = f"{sanitize_for_filename(playlist_name)}.m3u8"
|
||||||
filepath = os.path.join(playlist_dir, filename)
|
filepath = os.path.join(playlist_dir, filename)
|
||||||
if os.path.exists(filepath):
|
if os.path.exists(filepath):
|
||||||
try:
|
try:
|
||||||
@@ -62,6 +67,27 @@ def delete_playlist_file(playlist_dir, playlist_name):
|
|||||||
logging.error(f"Error deleting file {filepath}: {e}")
|
logging.error(f"Error deleting file {filepath}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def call_mopidy_rpc(method, params=None):
|
||||||
|
"""Calls a Mopidy RPC method."""
|
||||||
|
if not settings.MOPIDY_RPC_URL:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'jsonrpc': '2.0',
|
||||||
|
'id': 1,
|
||||||
|
'method': method,
|
||||||
|
}
|
||||||
|
if params:
|
||||||
|
data['params'] = params
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(settings.MOPIDY_RPC_URL, json=data, timeout=5)
|
||||||
|
response.raise_for_status()
|
||||||
|
logging.info(f"Successfully called Mopidy RPC method: {method}")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.error(f"Error calling Mopidy RPC: {e}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Get playlists from a Navidrome server using the Subsonic API."""
|
"""Get playlists from a Navidrome server using the Subsonic API."""
|
||||||
parser = argparse.ArgumentParser(description="Sync Navidrome playlists to Mopidy.")
|
parser = argparse.ArgumentParser(description="Sync Navidrome playlists to Mopidy.")
|
||||||
@@ -108,7 +134,7 @@ def main():
|
|||||||
logging.info("Starting one-time force sync of all playlists...")
|
logging.info("Starting one-time force sync of all playlists...")
|
||||||
api_url = f"{navidrome_url.rstrip('/')}/rest/getPlaylists.view"
|
api_url = f"{navidrome_url.rstrip('/')}/rest/getPlaylists.view"
|
||||||
try:
|
try:
|
||||||
response = requests.get(api_url, params=params)
|
response = requests.get(api_url, params=params, timeout=5)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logging.error(f"Error connecting to Navidrome: {e}")
|
logging.error(f"Error connecting to Navidrome: {e}")
|
||||||
@@ -127,6 +153,7 @@ def main():
|
|||||||
return
|
return
|
||||||
|
|
||||||
logging.info(f"Found {len(playlists)} playlists to sync.")
|
logging.info(f"Found {len(playlists)} playlists to sync.")
|
||||||
|
synced_a_playlist = False
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
playlist_id = playlist.get('id')
|
playlist_id = playlist.get('id')
|
||||||
playlist_name = playlist.get('name')
|
playlist_name = playlist.get('name')
|
||||||
@@ -136,17 +163,22 @@ def main():
|
|||||||
transformed_output = transform_m3u_to_m3u8(raw_output)
|
transformed_output = transform_m3u_to_m3u8(raw_output)
|
||||||
if transformed_output:
|
if transformed_output:
|
||||||
save_playlist_file(mopidy_playlist_dir, playlist_name, transformed_output)
|
save_playlist_file(mopidy_playlist_dir, playlist_name, transformed_output)
|
||||||
|
synced_a_playlist = True
|
||||||
|
|
||||||
|
if synced_a_playlist:
|
||||||
|
call_mopidy_rpc('core.playlists.refresh')
|
||||||
|
|
||||||
logging.info("Force sync complete.")
|
logging.info("Force sync complete.")
|
||||||
return
|
return
|
||||||
|
|
||||||
known_playlists = {}
|
known_playlists = {}
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
playlists_changed = False
|
||||||
api_url = f"{navidrome_url.rstrip('/')}/rest/getPlaylists.view"
|
api_url = f"{navidrome_url.rstrip('/')}/rest/getPlaylists.view"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(api_url, params=params)
|
response = requests.get(api_url, params=params, timeout=5)
|
||||||
response.raise_for_status() # Raise an exception for bad status codes
|
response.raise_for_status() # Raise an exception for bad status codes
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logging.error(f"Error connecting to Navidrome: {e}")
|
logging.error(f"Error connecting to Navidrome: {e}")
|
||||||
@@ -201,6 +233,7 @@ def main():
|
|||||||
playlist_name = playlist_data.get('name')
|
playlist_name = playlist_data.get('name')
|
||||||
logging.info(f"Playlist deleted: '{playlist_name}' ({playlist_id}). Removing file.")
|
logging.info(f"Playlist deleted: '{playlist_name}' ({playlist_id}). Removing file.")
|
||||||
delete_playlist_file(mopidy_playlist_dir, playlist_name)
|
delete_playlist_file(mopidy_playlist_dir, playlist_name)
|
||||||
|
playlists_changed = True
|
||||||
|
|
||||||
# Check for song count or name changes in existing playlists
|
# Check for song count or name changes in existing playlists
|
||||||
for playlist_id, playlist_data in current_playlists.items():
|
for playlist_id, playlist_data in current_playlists.items():
|
||||||
@@ -215,6 +248,7 @@ def main():
|
|||||||
name_changed = previous_name != current_name
|
name_changed = previous_name != current_name
|
||||||
|
|
||||||
if song_count_changed or name_changed:
|
if song_count_changed or name_changed:
|
||||||
|
playlists_changed = True
|
||||||
log_msg = f"Playlist '{previous_name}' ({playlist_id}) changed."
|
log_msg = f"Playlist '{previous_name}' ({playlist_id}) changed."
|
||||||
if name_changed:
|
if name_changed:
|
||||||
log_msg += f" Name: '{previous_name}' -> '{current_name}'."
|
log_msg += f" Name: '{previous_name}' -> '{current_name}'."
|
||||||
@@ -233,6 +267,9 @@ def main():
|
|||||||
# Update the state for the next iteration.
|
# Update the state for the next iteration.
|
||||||
known_playlists = current_playlists
|
known_playlists = current_playlists
|
||||||
|
|
||||||
|
if playlists_changed:
|
||||||
|
call_mopidy_rpc('core.playlists.refresh')
|
||||||
|
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
MOPIDY_PLAYLIST_DIR = '/var/lib/mopidy/m3u'
|
MOPIDY_PLAYLIST_DIR = '/var/lib/mopidy/m3u'
|
||||||
|
|
||||||
NAVIDROME_URL = ''
|
MOPIDY_RPC_URL = 'http://127.0.0.1:6680/mopidy/rpc'
|
||||||
|
NAVIDROME_URL = 'https://navidrome.example.com'
|
||||||
|
|
||||||
NAVIDROME_USER = ''
|
NAVIDROME_USER = ''
|
||||||
|
|
||||||
@@ -9,3 +10,4 @@ NAVIDROME_PASSWORD = ''
|
|||||||
# or these two:
|
# or these two:
|
||||||
SUBSONIC_SALT = ''
|
SUBSONIC_SALT = ''
|
||||||
SUBSONIC_TOKEN = ''
|
SUBSONIC_TOKEN = ''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user