mornye 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mornye-1.0.0/LICENSE +21 -0
- mornye-1.0.0/MANIFEST.in +3 -0
- mornye-1.0.0/PKG-INFO +199 -0
- mornye-1.0.0/README.md +140 -0
- mornye-1.0.0/mornye/__init__.py +85 -0
- mornye-1.0.0/mornye/__main__.py +13 -0
- mornye-1.0.0/mornye/api_mixins/__init__.py +20 -0
- mornye-1.0.0/mornye/api_mixins/covers_lyrics.py +417 -0
- mornye-1.0.0/mornye/api_mixins/csv_import.py +523 -0
- mornye-1.0.0/mornye/api_mixins/dedup.py +356 -0
- mornye-1.0.0/mornye/api_mixins/discovery.py +61 -0
- mornye-1.0.0/mornye/api_mixins/extension_health.py +143 -0
- mornye-1.0.0/mornye/api_mixins/failed_tracks.py +64 -0
- mornye-1.0.0/mornye/api_mixins/local_tagging.py +243 -0
- mornye-1.0.0/mornye/api_mixins/search.py +244 -0
- mornye-1.0.0/mornye/api_mixins/signed_sessions.py +55 -0
- mornye-1.0.0/mornye/api_mixins/stats.py +55 -0
- mornye-1.0.0/mornye/api_mixins/subscriptions.py +316 -0
- mornye-1.0.0/mornye/api_mixins/trust.py +37 -0
- mornye-1.0.0/mornye/app.py +2281 -0
- mornye-1.0.0/mornye/application/__init__.py +56 -0
- mornye-1.0.0/mornye/application/api_adapter.py +131 -0
- mornye-1.0.0/mornye/application/batch_finalizer.py +170 -0
- mornye-1.0.0/mornye/application/download_engine.py +2880 -0
- mornye-1.0.0/mornye/application/download_service.py +507 -0
- mornye-1.0.0/mornye/application/download_worker.py +100 -0
- mornye-1.0.0/mornye/application/event_bus.py +23 -0
- mornye-1.0.0/mornye/application/extension_service.py +50 -0
- mornye-1.0.0/mornye/application/job_service.py +258 -0
- mornye-1.0.0/mornye/application/legacy_download_adapter.py +197 -0
- mornye-1.0.0/mornye/application/metadata_service.py +170 -0
- mornye-1.0.0/mornye/application/output_service.py +83 -0
- mornye-1.0.0/mornye/application/pause.py +10 -0
- mornye-1.0.0/mornye/application/pipeline.py +259 -0
- mornye-1.0.0/mornye/application/post_processing.py +198 -0
- mornye-1.0.0/mornye/application/provider_executor.py +96 -0
- mornye-1.0.0/mornye/application/provider_factory.py +90 -0
- mornye-1.0.0/mornye/application/provider_resolver.py +5 -0
- mornye-1.0.0/mornye/application/queue_service.py +81 -0
- mornye-1.0.0/mornye/check_update.py +32 -0
- mornye-1.0.0/mornye/client.py +514 -0
- mornye-1.0.0/mornye/core/__init__.py +262 -0
- mornye-1.0.0/mornye/core/acoustid_lookup.py +207 -0
- mornye-1.0.0/mornye/core/apple_music_metadata.py +1244 -0
- mornye-1.0.0/mornye/core/apple_ttml.py +356 -0
- mornye-1.0.0/mornye/core/atomic_io.py +103 -0
- mornye-1.0.0/mornye/core/audio_fingerprint.py +259 -0
- mornye-1.0.0/mornye/core/base.py +253 -0
- mornye-1.0.0/mornye/core/cache_admin.py +255 -0
- mornye-1.0.0/mornye/core/canvas.py +501 -0
- mornye-1.0.0/mornye/core/cli_preview.py +184 -0
- mornye-1.0.0/mornye/core/code_search.py +102 -0
- mornye-1.0.0/mornye/core/config/__init__.py +183 -0
- mornye-1.0.0/mornye/core/console.py +287 -0
- mornye-1.0.0/mornye/core/cross_loop_lock.py +53 -0
- mornye-1.0.0/mornye/core/csv_picker.py +167 -0
- mornye-1.0.0/mornye/core/csv_source.py +845 -0
- mornye-1.0.0/mornye/core/db.py +444 -0
- mornye-1.0.0/mornye/core/dns_doh.py +172 -0
- mornye-1.0.0/mornye/core/download_log.py +336 -0
- mornye-1.0.0/mornye/core/download_validation.py +96 -0
- mornye-1.0.0/mornye/core/env.py +10 -0
- mornye-1.0.0/mornye/core/errors.py +129 -0
- mornye-1.0.0/mornye/core/extension_enrichment.py +341 -0
- mornye-1.0.0/mornye/core/extension_metadata.py +846 -0
- mornye-1.0.0/mornye/core/failed_tracks.py +230 -0
- mornye-1.0.0/mornye/core/ffmpeg_check.py +262 -0
- mornye-1.0.0/mornye/core/flac_validation.py +249 -0
- mornye-1.0.0/mornye/core/health_check.py +101 -0
- mornye-1.0.0/mornye/core/hires_check.py +1135 -0
- mornye-1.0.0/mornye/core/history.py +201 -0
- mornye-1.0.0/mornye/core/hooks.py +157 -0
- mornye-1.0.0/mornye/core/http.py +673 -0
- mornye-1.0.0/mornye/core/isrc_cache.py +144 -0
- mornye-1.0.0/mornye/core/isrc_finder.py +83 -0
- mornye-1.0.0/mornye/core/isrc_helper.py +61 -0
- mornye-1.0.0/mornye/core/isrc_utils.py +110 -0
- mornye-1.0.0/mornye/core/job_queue.py +402 -0
- mornye-1.0.0/mornye/core/library_dedup.py +1714 -0
- mornye-1.0.0/mornye/core/library_index_cache.py +149 -0
- mornye-1.0.0/mornye/core/library_notify.py +253 -0
- mornye-1.0.0/mornye/core/library_upgrade.py +451 -0
- mornye-1.0.0/mornye/core/link_resolver.py +482 -0
- mornye-1.0.0/mornye/core/local_matcher.py +341 -0
- mornye-1.0.0/mornye/core/local_processor.py +423 -0
- mornye-1.0.0/mornye/core/local_scanner.py +265 -0
- mornye-1.0.0/mornye/core/loop_runner.py +232 -0
- mornye-1.0.0/mornye/core/lyrics.py +1471 -0
- mornye-1.0.0/mornye/core/metadata_enrichment.py +1219 -0
- mornye-1.0.0/mornye/core/metadata_fallback.py +270 -0
- mornye-1.0.0/mornye/core/models.py +466 -0
- mornye-1.0.0/mornye/core/musicbrainz.py +1174 -0
- mornye-1.0.0/mornye/core/network_destinations.py +742 -0
- mornye-1.0.0/mornye/core/node_check.py +258 -0
- mornye-1.0.0/mornye/core/notifiers.py +359 -0
- mornye-1.0.0/mornye/core/output_lock.py +123 -0
- mornye-1.0.0/mornye/core/output_sink.py +242 -0
- mornye-1.0.0/mornye/core/paths.py +73 -0
- mornye-1.0.0/mornye/core/playlist_sync.py +434 -0
- mornye-1.0.0/mornye/core/profiles.py +219 -0
- mornye-1.0.0/mornye/core/progress.py +1046 -0
- mornye-1.0.0/mornye/core/provider_cooldown.py +260 -0
- mornye-1.0.0/mornye/core/provider_stats.py +373 -0
- mornye-1.0.0/mornye/core/providers/__init__.py +9 -0
- mornye-1.0.0/mornye/core/providers/manifest.py +62 -0
- mornye-1.0.0/mornye/core/providers/resolver.py +143 -0
- mornye-1.0.0/mornye/core/quality.py +142 -0
- mornye-1.0.0/mornye/core/recording_guard.py +194 -0
- mornye-1.0.0/mornye/core/replaygain.py +164 -0
- mornye-1.0.0/mornye/core/report.py +155 -0
- mornye-1.0.0/mornye/core/repositories/__init__.py +4 -0
- mornye-1.0.0/mornye/core/repositories/extension_repository.py +49 -0
- mornye-1.0.0/mornye/core/repositories/job_repository.py +300 -0
- mornye-1.0.0/mornye/core/response_cache.py +53 -0
- mornye-1.0.0/mornye/core/retry.py +24 -0
- mornye-1.0.0/mornye/core/session_memory.py +159 -0
- mornye-1.0.0/mornye/core/signed_session_desktop.py +630 -0
- mornye-1.0.0/mornye/core/signed_session_errors.py +188 -0
- mornye-1.0.0/mornye/core/signed_session_mobile.py +1603 -0
- mornye-1.0.0/mornye/core/signed_session_mono.py +523 -0
- mornye-1.0.0/mornye/core/signed_session_status.py +438 -0
- mornye-1.0.0/mornye/core/solver.py +1922 -0
- mornye-1.0.0/mornye/core/soundplate.py +34 -0
- mornye-1.0.0/mornye/core/spotfetch.py +906 -0
- mornye-1.0.0/mornye/core/spotify_metadata.py +1746 -0
- mornye-1.0.0/mornye/core/spotify_protobuf.py +369 -0
- mornye-1.0.0/mornye/core/spotify_totp.py +216 -0
- mornye-1.0.0/mornye/core/stats.py +580 -0
- mornye-1.0.0/mornye/core/subscription_scheduler.py +96 -0
- mornye-1.0.0/mornye/core/subscriptions.py +820 -0
- mornye-1.0.0/mornye/core/tagger.py +1750 -0
- mornye-1.0.0/mornye/core/text_match.py +442 -0
- mornye-1.0.0/mornye/core/tidal_metadata.py +537 -0
- mornye-1.0.0/mornye/core/tracklist.py +210 -0
- mornye-1.0.0/mornye/core/transcode.py +626 -0
- mornye-1.0.0/mornye/core/url_utils.py +93 -0
- mornye-1.0.0/mornye/core/web_users.py +440 -0
- mornye-1.0.0/mornye/downloader.py +34 -0
- mornye-1.0.0/mornye/extensions/__init__.py +50 -0
- mornye-1.0.0/mornye/extensions/_bridge.js +1238 -0
- mornye-1.0.0/mornye/extensions/_fsguard.js +171 -0
- mornye-1.0.0/mornye/extensions/_netguard.js +174 -0
- mornye-1.0.0/mornye/extensions/catalog.py +146 -0
- mornye-1.0.0/mornye/extensions/conformance.py +54 -0
- mornye-1.0.0/mornye/extensions/directories.py +307 -0
- mornye-1.0.0/mornye/extensions/manager.py +1120 -0
- mornye-1.0.0/mornye/extensions/provider.py +1366 -0
- mornye-1.0.0/mornye/extensions/python_provider.py +188 -0
- mornye-1.0.0/mornye/extensions/registry_config.py +283 -0
- mornye-1.0.0/mornye/extensions/runtime.py +562 -0
- mornye-1.0.0/mornye/extensions/runtime_features.py +48 -0
- mornye-1.0.0/mornye/extensions/sandbox.py +257 -0
- mornye-1.0.0/mornye/extensions/trust.py +260 -0
- mornye-1.0.0/mornye/frontend/app.js +7092 -0
- mornye-1.0.0/mornye/frontend/assets/icons/am.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/amzn.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/apple-touch-icon.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/download.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/dzr.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/flacdownloader.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/joox.svg +13 -0
- mornye-1.0.0/mornye/frontend/assets/icons/kuwo.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/logo-192.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/logo-512.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/logo-transparent.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/logo.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/lrclib.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/migu.jpeg +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/musicbrainz_d.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/musicbrainz_l.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/musixmatch.svg +20 -0
- mornye-1.0.0/mornye/frontend/assets/icons/netease.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/next.svg +27 -0
- mornye-1.0.0/mornye/frontend/assets/icons/pandora.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/qbz.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/songstats.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/soundcloud.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/spotify.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/spotubedl.svg +1 -0
- mornye-1.0.0/mornye/frontend/assets/icons/tidal_d.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/tidal_l.png +0 -0
- mornye-1.0.0/mornye/frontend/assets/icons/xbatchdl.svg +11 -0
- mornye-1.0.0/mornye/frontend/assets/icons/youtube.svg +28 -0
- mornye-1.0.0/mornye/frontend/index.html +1336 -0
- mornye-1.0.0/mornye/frontend/manifest.json +24 -0
- mornye-1.0.0/mornye/frontend/styles.css +4005 -0
- mornye-1.0.0/mornye/frontend/sw.js +85 -0
- mornye-1.0.0/mornye/frontend/toast-system.js +242 -0
- mornye-1.0.0/mornye/frontend/web-shim.js +395 -0
- mornye-1.0.0/mornye/launcher.py +3028 -0
- mornye-1.0.0/mornye/py.typed +0 -0
- mornye-1.0.0/mornye/tools/__init__.py +7 -0
- mornye-1.0.0/mornye/tools/bump_cache_bust.py +57 -0
- mornye-1.0.0/mornye/tools/cloud_config_cli.py +183 -0
- mornye-1.0.0/mornye/tools/dedup_check_cli.py +131 -0
- mornye-1.0.0/mornye/tools/ext_dryrun.py +236 -0
- mornye-1.0.0/mornye/tools/ext_scaffold.py +250 -0
- mornye-1.0.0/mornye/tools/hires_check_cli.py +147 -0
- mornye-1.0.0/mornye/tools/jellyfin_lyrics_gap.py +151 -0
- mornye-1.0.0/mornye/tools/library_dedup_cli.py +367 -0
- mornye-1.0.0/mornye/tools/library_upgrade_cli.py +144 -0
- mornye-1.0.0/mornye/tools/registry_signing_cli.py +104 -0
- mornye-1.0.0/mornye/tui/__init__.py +33 -0
- mornye-1.0.0/mornye/tui/app.py +816 -0
- mornye-1.0.0/mornye/tui/banner.py +114 -0
- mornye-1.0.0/mornye/tui/branding.py +356 -0
- mornye-1.0.0/mornye/tui/clipboard.py +82 -0
- mornye-1.0.0/mornye/tui/config_state.py +495 -0
- mornye-1.0.0/mornye/tui/config_view.py +881 -0
- mornye-1.0.0/mornye/tui/csv_picker_screen.py +168 -0
- mornye-1.0.0/mornye/tui/extensions_view.py +184 -0
- mornye-1.0.0/mornye/tui/health_view.py +92 -0
- mornye-1.0.0/mornye/tui/help_screen.py +69 -0
- mornye-1.0.0/mornye/tui/mornye.tcss +641 -0
- mornye-1.0.0/mornye/tui/queue_view.py +263 -0
- mornye-1.0.0/mornye/tui/runner.py +228 -0
- mornye-1.0.0/mornye/tui/search_view.py +154 -0
- mornye-1.0.0/mornye/tui/session_view.py +199 -0
- mornye-1.0.0/mornye/tui/signed_sessions_view.py +137 -0
- mornye-1.0.0/mornye/tui/tracklist_view.py +237 -0
- mornye-1.0.0/mornye/tui/window_size.py +151 -0
- mornye-1.0.0/mornye/webapi/__init__.py +35 -0
- mornye-1.0.0/mornye/webapi/routes.py +744 -0
- mornye-1.0.0/mornye/webapi/schemas.py +599 -0
- mornye-1.0.0/mornye/webapp.py +1374 -0
- mornye-1.0.0/mornye.egg-info/PKG-INFO +199 -0
- mornye-1.0.0/mornye.egg-info/SOURCES.txt +383 -0
- mornye-1.0.0/mornye.egg-info/dependency_links.txt +1 -0
- mornye-1.0.0/mornye.egg-info/entry_points.txt +2 -0
- mornye-1.0.0/mornye.egg-info/requires.txt +36 -0
- mornye-1.0.0/mornye.egg-info/top_level.txt +1 -0
- mornye-1.0.0/pyproject.toml +192 -0
- mornye-1.0.0/setup.cfg +4 -0
- mornye-1.0.0/tests/test_acoustid_lookup.py +324 -0
- mornye-1.0.0/tests/test_app_api_mixins.py +228 -0
- mornye-1.0.0/tests/test_apple_music_metadata.py +172 -0
- mornye-1.0.0/tests/test_architecture_foundation.py +2333 -0
- mornye-1.0.0/tests/test_artist_credit_split.py +113 -0
- mornye-1.0.0/tests/test_audio_fingerprint.py +136 -0
- mornye-1.0.0/tests/test_bridge_download_segments.py +205 -0
- mornye-1.0.0/tests/test_bridge_mobile_api.py +268 -0
- mornye-1.0.0/tests/test_bridge_retry_utils.py +151 -0
- mornye-1.0.0/tests/test_bridge_startup_errors.py +98 -0
- mornye-1.0.0/tests/test_cache_admin.py +178 -0
- mornye-1.0.0/tests/test_canvas.py +423 -0
- mornye-1.0.0/tests/test_canvas_sidecar.py +222 -0
- mornye-1.0.0/tests/test_cli_preview.py +72 -0
- mornye-1.0.0/tests/test_core_logic.py +898 -0
- mornye-1.0.0/tests/test_cover_fitting.py +131 -0
- mornye-1.0.0/tests/test_cover_url_plumbing.py +100 -0
- mornye-1.0.0/tests/test_credential_storage.py +154 -0
- mornye-1.0.0/tests/test_cross_loop_locks.py +110 -0
- mornye-1.0.0/tests/test_csv_download.py +321 -0
- mornye-1.0.0/tests/test_csv_picker.py +163 -0
- mornye-1.0.0/tests/test_csv_source.py +361 -0
- mornye-1.0.0/tests/test_db_persistence.py +366 -0
- mornye-1.0.0/tests/test_dedup_mixin.py +208 -0
- mornye-1.0.0/tests/test_directories.py +151 -0
- mornye-1.0.0/tests/test_disc_number_from_album.py +132 -0
- mornye-1.0.0/tests/test_discovery_mixin.py +66 -0
- mornye-1.0.0/tests/test_dns_doh.py +185 -0
- mornye-1.0.0/tests/test_docs_website.py +60 -0
- mornye-1.0.0/tests/test_download_queue_reporting.py +90 -0
- mornye-1.0.0/tests/test_download_recording_guard_wiring.py +176 -0
- mornye-1.0.0/tests/test_download_resume.py +228 -0
- mornye-1.0.0/tests/test_download_services.py +180 -0
- mornye-1.0.0/tests/test_ext_scaffold_and_dryrun.py +158 -0
- mornye-1.0.0/tests/test_extension_enrichment.py +560 -0
- mornye-1.0.0/tests/test_extension_fsguard.py +377 -0
- mornye-1.0.0/tests/test_extension_install_limits.py +135 -0
- mornye-1.0.0/tests/test_extension_metadata.py +824 -0
- mornye-1.0.0/tests/test_extension_netguard.py +119 -0
- mornye-1.0.0/tests/test_extension_output_containment.py +124 -0
- mornye-1.0.0/tests/test_extension_sandbox.py +232 -0
- mornye-1.0.0/tests/test_failed_tracks_and_resume.py +294 -0
- mornye-1.0.0/tests/test_fake_hires_redownload.py +286 -0
- mornye-1.0.0/tests/test_ffmpeg_check.py +312 -0
- mornye-1.0.0/tests/test_frontend_new_ui_wiring.py +231 -0
- mornye-1.0.0/tests/test_gui_batch_downloads.py +303 -0
- mornye-1.0.0/tests/test_gui_folder_options.py +55 -0
- mornye-1.0.0/tests/test_gui_hires_verification.py +115 -0
- mornye-1.0.0/tests/test_gui_prefetched_selection.py +224 -0
- mornye-1.0.0/tests/test_hires_check.py +470 -0
- mornye-1.0.0/tests/test_http_redirects.py +135 -0
- mornye-1.0.0/tests/test_job_queue.py +131 -0
- mornye-1.0.0/tests/test_json_report.py +144 -0
- mornye-1.0.0/tests/test_launcher_tui_flag.py +145 -0
- mornye-1.0.0/tests/test_launcher_web_flags.py +173 -0
- mornye-1.0.0/tests/test_library_dedup.py +753 -0
- mornye-1.0.0/tests/test_library_index_cache.py +193 -0
- mornye-1.0.0/tests/test_library_notify.py +284 -0
- mornye-1.0.0/tests/test_library_upgrade.py +337 -0
- mornye-1.0.0/tests/test_link_resolver_resolve_api.py +172 -0
- mornye-1.0.0/tests/test_link_resolver_songstats.py +166 -0
- mornye-1.0.0/tests/test_local_matcher.py +306 -0
- mornye-1.0.0/tests/test_loop_runner.py +174 -0
- mornye-1.0.0/tests/test_lrc_sidecar.py +131 -0
- mornye-1.0.0/tests/test_lyrics_apple_match.py +148 -0
- mornye-1.0.0/tests/test_lyrics_jellyfin_gap.py +81 -0
- mornye-1.0.0/tests/test_lyrics_jiosaavn.py +32 -0
- mornye-1.0.0/tests/test_lyrics_provider_order.py +289 -0
- mornye-1.0.0/tests/test_lyrics_ttml_catalogues.py +274 -0
- mornye-1.0.0/tests/test_m4a_no_roundtrip.py +133 -0
- mornye-1.0.0/tests/test_metadata_fallback.py +300 -0
- mornye-1.0.0/tests/test_mono_browser_cleanup.py +100 -0
- mornye-1.0.0/tests/test_mono_page_fallback.py +140 -0
- mornye-1.0.0/tests/test_mornye_configuration.py +65 -0
- mornye-1.0.0/tests/test_musicbrainz_fallback.py +311 -0
- mornye-1.0.0/tests/test_musicbrainz_release_pick.py +219 -0
- mornye-1.0.0/tests/test_musicbrainz_tag_names.py +187 -0
- mornye-1.0.0/tests/test_network_destinations.py +390 -0
- mornye-1.0.0/tests/test_node_check.py +351 -0
- mornye-1.0.0/tests/test_node_options_quoting.py +95 -0
- mornye-1.0.0/tests/test_notifiers.py +204 -0
- mornye-1.0.0/tests/test_output_lock.py +181 -0
- mornye-1.0.0/tests/test_output_sink.py +391 -0
- mornye-1.0.0/tests/test_parse_spotify_url_id_shape.py +55 -0
- mornye-1.0.0/tests/test_paths_overrides.py +48 -0
- mornye-1.0.0/tests/test_playcount_throttle.py +131 -0
- mornye-1.0.0/tests/test_post_download_command_gate.py +93 -0
- mornye-1.0.0/tests/test_post_download_hooks.py +159 -0
- mornye-1.0.0/tests/test_profiles.py +109 -0
- mornye-1.0.0/tests/test_progress_and_csv_counters.py +573 -0
- mornye-1.0.0/tests/test_progress_cross_loop.py +120 -0
- mornye-1.0.0/tests/test_progress_manager_loops.py +86 -0
- mornye-1.0.0/tests/test_provider_cooldown.py +296 -0
- mornye-1.0.0/tests/test_provider_link_hints.py +113 -0
- mornye-1.0.0/tests/test_python_provider_link_hints.py +60 -0
- mornye-1.0.0/tests/test_quotas.py +230 -0
- mornye-1.0.0/tests/test_rate_limit_logging.py +301 -0
- mornye-1.0.0/tests/test_recording_guard.py +216 -0
- mornye-1.0.0/tests/test_registry_signing_cli.py +93 -0
- mornye-1.0.0/tests/test_release_date_hydration.py +140 -0
- mornye-1.0.0/tests/test_replaygain.py +184 -0
- mornye-1.0.0/tests/test_runtime_node_check.py +63 -0
- mornye-1.0.0/tests/test_search_mixin.py +245 -0
- mornye-1.0.0/tests/test_signed_session_backoff.py +538 -0
- mornye-1.0.0/tests/test_signed_session_errors.py +139 -0
- mornye-1.0.0/tests/test_signed_session_status.py +305 -0
- mornye-1.0.0/tests/test_smoke.py +212 -0
- mornye-1.0.0/tests/test_solver_grant_capture.py +19 -0
- mornye-1.0.0/tests/test_solver_profile_cleanup.py +227 -0
- mornye-1.0.0/tests/test_solver_turnstile_locate.py +122 -0
- mornye-1.0.0/tests/test_solver_virtual_display.py +167 -0
- mornye-1.0.0/tests/test_spotify_metadata_nulls.py +185 -0
- mornye-1.0.0/tests/test_stats.py +236 -0
- mornye-1.0.0/tests/test_stream_url_validation.py +64 -0
- mornye-1.0.0/tests/test_subscription_scheduler.py +97 -0
- mornye-1.0.0/tests/test_subscriptions.py +375 -0
- mornye-1.0.0/tests/test_subscriptions_mixin.py +223 -0
- mornye-1.0.0/tests/test_tag_number_parsing.py +141 -0
- mornye-1.0.0/tests/test_title_floor.py +122 -0
- mornye-1.0.0/tests/test_tqdm_lock_under_capture.py +60 -0
- mornye-1.0.0/tests/test_track_identity.py +185 -0
- mornye-1.0.0/tests/test_track_isrc_wiring.py +135 -0
- mornye-1.0.0/tests/test_tracklist.py +266 -0
- mornye-1.0.0/tests/test_transcode_ffmpeg_install.py +51 -0
- mornye-1.0.0/tests/test_transcode_lossless.py +969 -0
- mornye-1.0.0/tests/test_trust.py +164 -0
- mornye-1.0.0/tests/test_trust_enforcement.py +178 -0
- mornye-1.0.0/tests/test_trust_mixin.py +65 -0
- mornye-1.0.0/tests/test_tui_app.py +416 -0
- mornye-1.0.0/tests/test_tui_config_profile.py +238 -0
- mornye-1.0.0/tests/test_tui_config_state.py +390 -0
- mornye-1.0.0/tests/test_tui_csv_picker.py +163 -0
- mornye-1.0.0/tests/test_tui_extensions.py +218 -0
- mornye-1.0.0/tests/test_tui_fixes.py +246 -0
- mornye-1.0.0/tests/test_tui_health.py +132 -0
- mornye-1.0.0/tests/test_tui_look.py +447 -0
- mornye-1.0.0/tests/test_tui_runner.py +435 -0
- mornye-1.0.0/tests/test_tui_search.py +187 -0
- mornye-1.0.0/tests/test_tui_session.py +266 -0
- mornye-1.0.0/tests/test_tui_signed_sessions.py +116 -0
- mornye-1.0.0/tests/test_tui_tracklist.py +368 -0
- mornye-1.0.0/tests/test_tui_window_size.py +151 -0
- mornye-1.0.0/tests/test_watch.py +49 -0
- mornye-1.0.0/tests/test_web_shim_methods_in_sync.py +82 -0
- mornye-1.0.0/tests/test_web_users.py +107 -0
- mornye-1.0.0/tests/test_webapi_integration.py +228 -0
- mornye-1.0.0/tests/test_webapi_v1.py +662 -0
- mornye-1.0.0/tests/test_webapp_auth.py +87 -0
- mornye-1.0.0/tests/test_webapp_isolation.py +358 -0
- mornye-1.0.0/tests/test_webapp_multiuser.py +251 -0
- mornye-1.0.0/tests/test_webapp_ops.py +136 -0
- mornye-1.0.0/tests/test_webapp_pwa.py +81 -0
mornye-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BartolomeoRusso9
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
mornye-1.0.0/MANIFEST.in
ADDED
mornye-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mornye
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Python module for metadata and user-configured music-provider extensions.
|
|
5
|
+
Author: BartolomeoRusso9
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mornyeapp/Mornye-Python
|
|
8
|
+
Project-URL: Repository, https://github.com/mornyeapp/Mornye-Python
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/mornyeapp/Mornye-Python/issues
|
|
10
|
+
Keywords: spotify,flac,downloader,music,lossless,lossless-music,tidal,qobuz,amazon-music,audio,mornye,spotify-metadata,no-account,hi-res
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: httpx[http2]>=0.27.0
|
|
25
|
+
Requires-Dist: mutagen>=1.47.0
|
|
26
|
+
Requires-Dist: Pillow>=12.3.0
|
|
27
|
+
Requires-Dist: pydantic>=2.7.0
|
|
28
|
+
Requires-Dist: cryptography>=50.0.0
|
|
29
|
+
Requires-Dist: pycryptodome>=3.20.0
|
|
30
|
+
Requires-Dist: packaging>=23.0
|
|
31
|
+
Requires-Dist: yt-dlp>=2026.7.4
|
|
32
|
+
Requires-Dist: tqdm>=4.66.3
|
|
33
|
+
Requires-Dist: pywebview>=5.0.0
|
|
34
|
+
Requires-Dist: textual>=1.0
|
|
35
|
+
Requires-Dist: aiofiles>=23.2.1
|
|
36
|
+
Requires-Dist: tenacity>=8.3.0
|
|
37
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
38
|
+
Requires-Dist: requests>=2.33.0
|
|
39
|
+
Requires-Dist: pydoll-python>=2.19.0
|
|
40
|
+
Requires-Dist: multidict>=6.9.1
|
|
41
|
+
Requires-Dist: numpy>=1.24.0
|
|
42
|
+
Requires-Dist: soundfile>=0.12.1
|
|
43
|
+
Requires-Dist: fastapi>=0.110.0
|
|
44
|
+
Requires-Dist: uvicorn[standard]>=0.29.0
|
|
45
|
+
Requires-Dist: pyacoustid>=1.3.0
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
48
|
+
Requires-Dist: pytest-httpx>=0.21; extra == "dev"
|
|
49
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
50
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
51
|
+
Requires-Dist: ruff>=0.12.0; extra == "dev"
|
|
52
|
+
Requires-Dist: black>=26.3.1; extra == "dev"
|
|
53
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
54
|
+
Requires-Dist: pre-commit>=3.0; extra == "dev"
|
|
55
|
+
Provides-Extra: smb
|
|
56
|
+
Requires-Dist: smbprotocol>=1.15.0; extra == "smb"
|
|
57
|
+
Requires-Dist: keyring>=25.0.0; extra == "smb"
|
|
58
|
+
Dynamic: license-file
|
|
59
|
+
|
|
60
|
+
# Mornye Python
|
|
61
|
+
|
|
62
|
+
Fetch track metadata and retrieve matching lossless audio through Tidal, Qobuz, Amazon Music and other provider backends — supplied entirely via extensions you choose and configure yourself. Integrate directly into your Python projects, build custom Telegram bots, automation tools, or bulk downloaders.
|
|
63
|
+
|
|
64
|
+
[](https://github.com/mornyeapp/Mornye-Python/stargazers)
|
|
65
|
+
[](https://github.com/mornyeapp/Mornye-Python/releases/latest)
|
|
66
|
+
[](https://pypi.org/project/mornye/)
|
|
67
|
+
[](https://pypi.org/project/mornye/)
|
|
68
|
+
[](https://github.com/mornyeapp/Mornye-Python/releases)
|
|
69
|
+
[](https://pypi.org/project/mornye/)
|
|
70
|
+
[](https://t.me/mornyecommunity)
|
|
71
|
+
|
|
72
|
+
## Disclaimer
|
|
73
|
+
|
|
74
|
+
This project is intended for **educational and personal use only**. The developer does not condone or encourage copyright infringement. The software is licensed under the [MIT License](https://github.com/mornyeapp/Mornye-Python//blob/main/LICENSE).
|
|
75
|
+
|
|
76
|
+
**Mornye-Python** is an independent, third-party tool and is not affiliated with, endorsed by, or connected to Spotify, Tidal, Qobuz, Amazon Music, Deezer, or any other streaming service. It is also not affiliated with, and has no control over or responsibility for, any other project sharing a similar name on other platforms.
|
|
77
|
+
|
|
78
|
+
No copyrighted content is hosted, stored, mirrored, or distributed by this repository. The core application does not bundle, ship, or default to any third-party extension, registry, or provider. Extensions are installed only if a user explicitly configures and requests them from a source of their own choosing; the maintainer has no control over, does not review, and assumes no responsibility for the content or behavior of third-party extensions or registries a user may choose to install.
|
|
79
|
+
|
|
80
|
+
You are solely responsible for:
|
|
81
|
+
|
|
82
|
+
1. Ensuring your use of this software, and any extension or registry you choose to install, complies with your local laws.
|
|
83
|
+
2. Reading and adhering to the Terms of Service of any platform or provider you access, directly or through an extension.
|
|
84
|
+
3. Any legal consequences resulting from the use or misuse of this tool.
|
|
85
|
+
|
|
86
|
+
This software is provided free of charge by the maintainer. If you paid a third party for access to it, you may have been misled or scammed.
|
|
87
|
+
|
|
88
|
+
The software is provided "as is", without warranty of any kind, express or implied. The author assumes no liability for any bans, damages, or legal issues arising from its use or misuse. Users assume all risk associated with its use.
|
|
89
|
+
|
|
90
|
+
If you are a copyright holder or an authorized representative and believe this repository infringes upon your rights, please contact the maintainer with sufficient detail (including relevant URLs and proof of ownership); the matter will be promptly investigated.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
> **Looking for a standalone app?**
|
|
95
|
+
>
|
|
96
|
+
> - [Mornye (Mobile)](https://github.com/mornyeapp/Mornye) — Mornye for Android & iOS, maintained by [@zarzet](https://github.com/zarzet)
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Why Python (instead of the standalone app)
|
|
100
|
+
|
|
101
|
+
The Mobile app is built for direct, immediate use: open it, paste a link, download. Python exists for a different case — **integrating** this logic into something else.
|
|
102
|
+
|
|
103
|
+
It makes sense to start here if:
|
|
104
|
+
|
|
105
|
+
- **You're building a bot** (Telegram, Discord) or a service that needs to handle requests from many users automatically, not a single manual download.
|
|
106
|
+
- **You need the async API** (`AsyncMornye`) inside an existing FastAPI/Quart/Sanic app, with shared connection pooling.
|
|
107
|
+
- **You want to orchestrate bulk downloads** via scripts — multiple playlists, full discographies, local library retagging — with custom logic (filenames via a Python function, post-download actions, saved profiles).
|
|
108
|
+
- **You need to run headless**, on a server, a NAS, or inside Docker, possibly as part of a larger pipeline.
|
|
109
|
+
- **You want full control over which extensions get loaded** and from which registry, instead of relying on a fixed set baked into an app.
|
|
110
|
+
|
|
111
|
+
If you just want a GUI for personal use, with no code involved, the [Mobile](https://github.com/mornyeapp/Mornye) app remain the simpler choice — this module is the building block those (and similar projects) can be built on top of.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Features
|
|
116
|
+
|
|
117
|
+
- Native synchronous and asynchronous Python APIs
|
|
118
|
+
- Modular JavaScript and Python Extension system (bring-your-own registry — nothing bundled)
|
|
119
|
+
- Automatic fallback among the extensions *you* have installed
|
|
120
|
+
- Built-in GUI, as a native window or served locally in a browser (`--gui` / `--web`)
|
|
121
|
+
- Guided terminal UI (`--tui`), with the download queue live on screen
|
|
122
|
+
- Docker support
|
|
123
|
+
- Configuration Profiles
|
|
124
|
+
- MusicBrainz metadata enrichment
|
|
125
|
+
- Synchronized lyrics — embedded in the tag and, optionally, written out as `.lrc` files; word-by-word where the source has it, or line-synced if you prefer
|
|
126
|
+
- Optional Canvas sidecars — the short looping visual a track carries, saved next to it as a video file (off by default; no media server reads these on its own)
|
|
127
|
+
- Optional MP3 320 kbps transcoding
|
|
128
|
+
- Watch mode: re-sync a playlist/album/artist/URL on an interval, indefinitely
|
|
129
|
+
- Optional shared-secret or per-account authentication for `--web`, plus a queued, per-user download history in multi-user mode
|
|
130
|
+
- Extension discovery directories, and optional Ed25519 signature verification on top of registry checksums
|
|
131
|
+
- Local-library duplicate detection by acoustic fingerprint, independent of tags/ISRC
|
|
132
|
+
- Extension scaffolding + dry-run validation for developing your own
|
|
133
|
+
- Installable as a PWA in `--web` mode (Add to Home Screen / standalone window)
|
|
134
|
+
- **Follow artists** — check for new releases and fetch only what came out since
|
|
135
|
+
- **Library upgrade** — find files below a target quality (including fake Hi-Res) and re-fetch them
|
|
136
|
+
- **Outbound notifications** — webhook, Discord, Telegram or ntfy, per track or one summary per run
|
|
137
|
+
- **Versioned REST API** at `/api/v1`, with an OpenAPI document at `/docs`
|
|
138
|
+
- **CSV input** — hand it a playlist export (Exportify, Soundiiz, a bare list of links) and it downloads what the file lists, matching rows that carry no link and reporting the ones it cannot
|
|
139
|
+
- **Dashboard** — what you have actually downloaded, as numbers: top artists, genres, decades, providers, listening time, activity by weekday and streaks (`--stats`, or the Stats screen)
|
|
140
|
+
- **Durable queue and download log** (SQLite): a restart no longer loses queued work
|
|
141
|
+
- **SMB destinations** — use an OS-mounted share or the optional SMB2/SMB3 backend, with staged and verified remote delivery
|
|
142
|
+
- **Per-account quotas and an admin role** in `--web-multiuser`
|
|
143
|
+
- **Extension health panel** — success rate, latency and last error per provider
|
|
144
|
+
- **Wrong-recording guard** — a provider that answers with a karaoke take, a clean edit of an explicit track, or another artist's cover is rejected and the download falls through to the next one, instead of producing a file that carries the right tags and the wrong audio
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Installation
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pip install mornye
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
> **Important:** out of the box, Mornye does nothing but resolve metadata — it ships with **no built-in provider and no default extension source**. Before you can download anything, you need to point it at an extension registry of your own choosing and install at least one extension. See [Extensions](docs/guide/extensions.md#extensions) below.
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Documentation
|
|
158
|
+
|
|
159
|
+
The full guide lives in [`docs/guide/`](docs/guide/) — the README had grown
|
|
160
|
+
to 76 KB and 87 headings, which is past the point where GitHub or PyPI
|
|
161
|
+
renders it usefully. Every page below is the same text that used to be here.
|
|
162
|
+
|
|
163
|
+
| Page | What's in it |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| [Quick Start](docs/guide/quick-start.md) | Every way to run it — GUI, web, CLI, terminal UI, Python — and the URL types it accepts |
|
|
166
|
+
| [Terminal UI](docs/guide/tui.md) | `--tui`, the guided mode: every setting on one screen, and the download queue live |
|
|
167
|
+
| [Extensions](docs/guide/extensions.md) | Registries, installing providers, signature verification and trust tiers |
|
|
168
|
+
| [Downloading from a CSV](docs/guide/csv.md) | Feeding it a file of tracks instead of a link: supported exports, how rows without a link are matched, and the unmatched-row report |
|
|
169
|
+
| [Your library in numbers](docs/guide/dashboard.md) | The dashboard built from what you have actually downloaded — top artists, genres, decades, activity |
|
|
170
|
+
| [Configuration](docs/guide/configuration.md) | Every option: naming, quality, lyrics, enrichment, transcoding, watch mode, profiles |
|
|
171
|
+
| [Network Destinations](docs/guide/network-destinations.md) | Mounted paths, native SMB profiles, safe transfers, CLI and Docker |
|
|
172
|
+
| [API Reference](docs/guide/api-reference.md) | `Mornye` and `AsyncMornye`, and the objects they return |
|
|
173
|
+
| [Migration Guide](docs/guide/migration.md) | Moving from the old distribution, CLI, environment, and local state |
|
|
174
|
+
| [Local Tagging](docs/guide/local-tagging.md) | Retagging an existing library, MusicBrainz enrichment, download validation |
|
|
175
|
+
| [Automation & Operations](docs/guide/automation.md) | JSON output, post-download hooks, notifications, M3U, library rescan, cache maintenance, running the web server |
|
|
176
|
+
| [Following Artists](docs/guide/subscriptions.md) | Subscriptions, new-release checks, scheduled playlist monitoring, and the library-upgrade pass |
|
|
177
|
+
| [REST API](docs/guide/rest-api.md) | The versioned `/api/v1` surface, quotas and the admin endpoints |
|
|
178
|
+
| [Docker](docs/guide/docker.md) | Headless and NAS setups |
|
|
179
|
+
| [CLI](docs/guide/cli.md) | The standalone executables |
|
|
180
|
+
|
|
181
|
+
Security policy and the reporting process: [SECURITY.md](SECURITY.md).
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Want to support the project?
|
|
186
|
+
|
|
187
|
+
If this software is useful and brings you value, consider supporting the project by buying us a coffee. Your support helps keep development going.
|
|
188
|
+
|
|
189
|
+
[](https://ko-fi.com/bartolomeorusso9)
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## API Credits
|
|
194
|
+
|
|
195
|
+
[MusicBrainz](https://musicbrainz.org) · [LRCLIB](https://lrclib.net) · [Musixmatch](https://www.musixmatch.com) · [iTunes Search API](https://itunes.apple.com)
|
|
196
|
+
|
|
197
|
+
> Provider-specific credits (Tidal, Qobuz, Amazon Music, Deezer, SoundCloud, Apple Music, Pandora, and any third-party API used to reach them) now belong to whichever extension you install — see that extension's own documentation for its credits and terms.
|
|
198
|
+
>
|
|
199
|
+
> **[!TIP]** Star the repo to show support, and click **Watch → Custom → Releases** on GitHub if you want to be notified as soon as a new release goes out.
|
mornye-1.0.0/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Mornye Python
|
|
2
|
+
|
|
3
|
+
Fetch track metadata and retrieve matching lossless audio through Tidal, Qobuz, Amazon Music and other provider backends — supplied entirely via extensions you choose and configure yourself. Integrate directly into your Python projects, build custom Telegram bots, automation tools, or bulk downloaders.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/mornyeapp/Mornye-Python/stargazers)
|
|
6
|
+
[](https://github.com/mornyeapp/Mornye-Python/releases/latest)
|
|
7
|
+
[](https://pypi.org/project/mornye/)
|
|
8
|
+
[](https://pypi.org/project/mornye/)
|
|
9
|
+
[](https://github.com/mornyeapp/Mornye-Python/releases)
|
|
10
|
+
[](https://pypi.org/project/mornye/)
|
|
11
|
+
[](https://t.me/mornyecommunity)
|
|
12
|
+
|
|
13
|
+
## Disclaimer
|
|
14
|
+
|
|
15
|
+
This project is intended for **educational and personal use only**. The developer does not condone or encourage copyright infringement. The software is licensed under the [MIT License](https://github.com/mornyeapp/Mornye-Python//blob/main/LICENSE).
|
|
16
|
+
|
|
17
|
+
**Mornye-Python** is an independent, third-party tool and is not affiliated with, endorsed by, or connected to Spotify, Tidal, Qobuz, Amazon Music, Deezer, or any other streaming service. It is also not affiliated with, and has no control over or responsibility for, any other project sharing a similar name on other platforms.
|
|
18
|
+
|
|
19
|
+
No copyrighted content is hosted, stored, mirrored, or distributed by this repository. The core application does not bundle, ship, or default to any third-party extension, registry, or provider. Extensions are installed only if a user explicitly configures and requests them from a source of their own choosing; the maintainer has no control over, does not review, and assumes no responsibility for the content or behavior of third-party extensions or registries a user may choose to install.
|
|
20
|
+
|
|
21
|
+
You are solely responsible for:
|
|
22
|
+
|
|
23
|
+
1. Ensuring your use of this software, and any extension or registry you choose to install, complies with your local laws.
|
|
24
|
+
2. Reading and adhering to the Terms of Service of any platform or provider you access, directly or through an extension.
|
|
25
|
+
3. Any legal consequences resulting from the use or misuse of this tool.
|
|
26
|
+
|
|
27
|
+
This software is provided free of charge by the maintainer. If you paid a third party for access to it, you may have been misled or scammed.
|
|
28
|
+
|
|
29
|
+
The software is provided "as is", without warranty of any kind, express or implied. The author assumes no liability for any bans, damages, or legal issues arising from its use or misuse. Users assume all risk associated with its use.
|
|
30
|
+
|
|
31
|
+
If you are a copyright holder or an authorized representative and believe this repository infringes upon your rights, please contact the maintainer with sufficient detail (including relevant URLs and proof of ownership); the matter will be promptly investigated.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
> **Looking for a standalone app?**
|
|
36
|
+
>
|
|
37
|
+
> - [Mornye (Mobile)](https://github.com/mornyeapp/Mornye) — Mornye for Android & iOS, maintained by [@zarzet](https://github.com/zarzet)
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Why Python (instead of the standalone app)
|
|
41
|
+
|
|
42
|
+
The Mobile app is built for direct, immediate use: open it, paste a link, download. Python exists for a different case — **integrating** this logic into something else.
|
|
43
|
+
|
|
44
|
+
It makes sense to start here if:
|
|
45
|
+
|
|
46
|
+
- **You're building a bot** (Telegram, Discord) or a service that needs to handle requests from many users automatically, not a single manual download.
|
|
47
|
+
- **You need the async API** (`AsyncMornye`) inside an existing FastAPI/Quart/Sanic app, with shared connection pooling.
|
|
48
|
+
- **You want to orchestrate bulk downloads** via scripts — multiple playlists, full discographies, local library retagging — with custom logic (filenames via a Python function, post-download actions, saved profiles).
|
|
49
|
+
- **You need to run headless**, on a server, a NAS, or inside Docker, possibly as part of a larger pipeline.
|
|
50
|
+
- **You want full control over which extensions get loaded** and from which registry, instead of relying on a fixed set baked into an app.
|
|
51
|
+
|
|
52
|
+
If you just want a GUI for personal use, with no code involved, the [Mobile](https://github.com/mornyeapp/Mornye) app remain the simpler choice — this module is the building block those (and similar projects) can be built on top of.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Features
|
|
57
|
+
|
|
58
|
+
- Native synchronous and asynchronous Python APIs
|
|
59
|
+
- Modular JavaScript and Python Extension system (bring-your-own registry — nothing bundled)
|
|
60
|
+
- Automatic fallback among the extensions *you* have installed
|
|
61
|
+
- Built-in GUI, as a native window or served locally in a browser (`--gui` / `--web`)
|
|
62
|
+
- Guided terminal UI (`--tui`), with the download queue live on screen
|
|
63
|
+
- Docker support
|
|
64
|
+
- Configuration Profiles
|
|
65
|
+
- MusicBrainz metadata enrichment
|
|
66
|
+
- Synchronized lyrics — embedded in the tag and, optionally, written out as `.lrc` files; word-by-word where the source has it, or line-synced if you prefer
|
|
67
|
+
- Optional Canvas sidecars — the short looping visual a track carries, saved next to it as a video file (off by default; no media server reads these on its own)
|
|
68
|
+
- Optional MP3 320 kbps transcoding
|
|
69
|
+
- Watch mode: re-sync a playlist/album/artist/URL on an interval, indefinitely
|
|
70
|
+
- Optional shared-secret or per-account authentication for `--web`, plus a queued, per-user download history in multi-user mode
|
|
71
|
+
- Extension discovery directories, and optional Ed25519 signature verification on top of registry checksums
|
|
72
|
+
- Local-library duplicate detection by acoustic fingerprint, independent of tags/ISRC
|
|
73
|
+
- Extension scaffolding + dry-run validation for developing your own
|
|
74
|
+
- Installable as a PWA in `--web` mode (Add to Home Screen / standalone window)
|
|
75
|
+
- **Follow artists** — check for new releases and fetch only what came out since
|
|
76
|
+
- **Library upgrade** — find files below a target quality (including fake Hi-Res) and re-fetch them
|
|
77
|
+
- **Outbound notifications** — webhook, Discord, Telegram or ntfy, per track or one summary per run
|
|
78
|
+
- **Versioned REST API** at `/api/v1`, with an OpenAPI document at `/docs`
|
|
79
|
+
- **CSV input** — hand it a playlist export (Exportify, Soundiiz, a bare list of links) and it downloads what the file lists, matching rows that carry no link and reporting the ones it cannot
|
|
80
|
+
- **Dashboard** — what you have actually downloaded, as numbers: top artists, genres, decades, providers, listening time, activity by weekday and streaks (`--stats`, or the Stats screen)
|
|
81
|
+
- **Durable queue and download log** (SQLite): a restart no longer loses queued work
|
|
82
|
+
- **SMB destinations** — use an OS-mounted share or the optional SMB2/SMB3 backend, with staged and verified remote delivery
|
|
83
|
+
- **Per-account quotas and an admin role** in `--web-multiuser`
|
|
84
|
+
- **Extension health panel** — success rate, latency and last error per provider
|
|
85
|
+
- **Wrong-recording guard** — a provider that answers with a karaoke take, a clean edit of an explicit track, or another artist's cover is rejected and the download falls through to the next one, instead of producing a file that carries the right tags and the wrong audio
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install mornye
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> **Important:** out of the box, Mornye does nothing but resolve metadata — it ships with **no built-in provider and no default extension source**. Before you can download anything, you need to point it at an extension registry of your own choosing and install at least one extension. See [Extensions](docs/guide/extensions.md#extensions) below.
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
The full guide lives in [`docs/guide/`](docs/guide/) — the README had grown
|
|
101
|
+
to 76 KB and 87 headings, which is past the point where GitHub or PyPI
|
|
102
|
+
renders it usefully. Every page below is the same text that used to be here.
|
|
103
|
+
|
|
104
|
+
| Page | What's in it |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| [Quick Start](docs/guide/quick-start.md) | Every way to run it — GUI, web, CLI, terminal UI, Python — and the URL types it accepts |
|
|
107
|
+
| [Terminal UI](docs/guide/tui.md) | `--tui`, the guided mode: every setting on one screen, and the download queue live |
|
|
108
|
+
| [Extensions](docs/guide/extensions.md) | Registries, installing providers, signature verification and trust tiers |
|
|
109
|
+
| [Downloading from a CSV](docs/guide/csv.md) | Feeding it a file of tracks instead of a link: supported exports, how rows without a link are matched, and the unmatched-row report |
|
|
110
|
+
| [Your library in numbers](docs/guide/dashboard.md) | The dashboard built from what you have actually downloaded — top artists, genres, decades, activity |
|
|
111
|
+
| [Configuration](docs/guide/configuration.md) | Every option: naming, quality, lyrics, enrichment, transcoding, watch mode, profiles |
|
|
112
|
+
| [Network Destinations](docs/guide/network-destinations.md) | Mounted paths, native SMB profiles, safe transfers, CLI and Docker |
|
|
113
|
+
| [API Reference](docs/guide/api-reference.md) | `Mornye` and `AsyncMornye`, and the objects they return |
|
|
114
|
+
| [Migration Guide](docs/guide/migration.md) | Moving from the old distribution, CLI, environment, and local state |
|
|
115
|
+
| [Local Tagging](docs/guide/local-tagging.md) | Retagging an existing library, MusicBrainz enrichment, download validation |
|
|
116
|
+
| [Automation & Operations](docs/guide/automation.md) | JSON output, post-download hooks, notifications, M3U, library rescan, cache maintenance, running the web server |
|
|
117
|
+
| [Following Artists](docs/guide/subscriptions.md) | Subscriptions, new-release checks, scheduled playlist monitoring, and the library-upgrade pass |
|
|
118
|
+
| [REST API](docs/guide/rest-api.md) | The versioned `/api/v1` surface, quotas and the admin endpoints |
|
|
119
|
+
| [Docker](docs/guide/docker.md) | Headless and NAS setups |
|
|
120
|
+
| [CLI](docs/guide/cli.md) | The standalone executables |
|
|
121
|
+
|
|
122
|
+
Security policy and the reporting process: [SECURITY.md](SECURITY.md).
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Want to support the project?
|
|
127
|
+
|
|
128
|
+
If this software is useful and brings you value, consider supporting the project by buying us a coffee. Your support helps keep development going.
|
|
129
|
+
|
|
130
|
+
[](https://ko-fi.com/bartolomeorusso9)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## API Credits
|
|
135
|
+
|
|
136
|
+
[MusicBrainz](https://musicbrainz.org) · [LRCLIB](https://lrclib.net) · [Musixmatch](https://www.musixmatch.com) · [iTunes Search API](https://itunes.apple.com)
|
|
137
|
+
|
|
138
|
+
> Provider-specific credits (Tidal, Qobuz, Amazon Music, Deezer, SoundCloud, Apple Music, Pandora, and any third-party API used to reach them) now belong to whichever extension you install — see that extension's own documentation for its credits and terms.
|
|
139
|
+
>
|
|
140
|
+
> **[!TIP]** Star the repo to show support, and click **Watch → Custom → Releases** on GitHub if you want to be notified as soon as a new release goes out.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Mornye Python — a Python module with user-configured provider extensions.
|
|
2
|
+
|
|
3
|
+
Minimum use:
|
|
4
|
+
from mornye import Mornye
|
|
5
|
+
Mornye("URL_SPOTIFY", "./downloads")
|
|
6
|
+
|
|
7
|
+
Advanced use:
|
|
8
|
+
from mornye import AsyncMornye
|
|
9
|
+
# See the documentation for advanced async usage
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import importlib.metadata
|
|
15
|
+
import warnings
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
# Single canonical client implementation (both sync and async)
|
|
19
|
+
from .client import AsyncMornye, Mornye
|
|
20
|
+
from .core import DownloadResult, TrackMetadata
|
|
21
|
+
from .core.config import (
|
|
22
|
+
DownloadFailure,
|
|
23
|
+
DownloadReport,
|
|
24
|
+
DownloadRequest,
|
|
25
|
+
DownloadSkip,
|
|
26
|
+
MornyeConfig,
|
|
27
|
+
)
|
|
28
|
+
from .downloader import DownloadOptions, MornyeDownloader
|
|
29
|
+
|
|
30
|
+
# Legacy provider classes stay importable for one release, but are lazy so an
|
|
31
|
+
# extension-first application start never initialises their browser/session
|
|
32
|
+
# machinery. New integrations should use ``mornye.extensions``.
|
|
33
|
+
_LEGACY_PROVIDER_EXPORTS = {
|
|
34
|
+
"AmazonProvider",
|
|
35
|
+
"AppleMusicProvider",
|
|
36
|
+
"DeezerProvider",
|
|
37
|
+
"JooxProvider",
|
|
38
|
+
"KuwoProvider",
|
|
39
|
+
"MiguProvider",
|
|
40
|
+
"NeteaseProvider",
|
|
41
|
+
"QobuzProvider",
|
|
42
|
+
"TidalProvider",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def __getattr__(name: str) -> Any:
|
|
47
|
+
if name == "SpotifyMetadataClient":
|
|
48
|
+
from .core.spotify_metadata import SpotifyMetadataClient
|
|
49
|
+
|
|
50
|
+
return SpotifyMetadataClient
|
|
51
|
+
if name in _LEGACY_PROVIDER_EXPORTS:
|
|
52
|
+
warnings.warn(
|
|
53
|
+
f"mornye.{name} is deprecated; install and select an extension instead.",
|
|
54
|
+
DeprecationWarning,
|
|
55
|
+
stacklevel=2,
|
|
56
|
+
)
|
|
57
|
+
# The legacy `mornye.providers` package was removed. Instead of
|
|
58
|
+
# attempting a hard import that would raise ModuleNotFoundError,
|
|
59
|
+
# provide a clear error explaining the migration path.
|
|
60
|
+
raise AttributeError(
|
|
61
|
+
f"Legacy provider '{name}' is not available. Install and select an "
|
|
62
|
+
"extension (ext:) or use the extensions API instead."
|
|
63
|
+
)
|
|
64
|
+
raise AttributeError(name)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
__version__ = importlib.metadata.version("mornye-python")
|
|
69
|
+
except importlib.metadata.PackageNotFoundError:
|
|
70
|
+
__version__ = "unknown"
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
"AsyncMornye",
|
|
74
|
+
"DownloadFailure",
|
|
75
|
+
"DownloadOptions",
|
|
76
|
+
"DownloadReport",
|
|
77
|
+
"DownloadRequest",
|
|
78
|
+
"DownloadResult",
|
|
79
|
+
"DownloadSkip",
|
|
80
|
+
"Mornye",
|
|
81
|
+
"MornyeConfig",
|
|
82
|
+
"MornyeDownloader",
|
|
83
|
+
"SpotifyMetadataClient",
|
|
84
|
+
"TrackMetadata",
|
|
85
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""mornye/__main__.py.
|
|
2
|
+
|
|
3
|
+
Alias for `python -m mornye`: delegates entirely to launcher.py so that
|
|
4
|
+
`python -m mornye ...` and the `mornye ...` command (console script)
|
|
5
|
+
run exactly the same code, with no duplicated logic.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .launcher import main
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
main()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Mixins that make up MornyeAPI (see app.py).
|
|
2
|
+
|
|
3
|
+
`MornyeAPI` is the single object both GUI transports bind every exposed
|
|
4
|
+
method to — pywebview's `js_api` for the desktop window, and webapp.py's
|
|
5
|
+
`ALLOWED_METHODS` dispatcher for `--web` mode — so splitting it into several
|
|
6
|
+
top-level objects isn't an option without changing both entry points and the
|
|
7
|
+
frontend's calling convention (`pywebview.api.<method>`).
|
|
8
|
+
|
|
9
|
+
What *can* move without touching either transport is *where a method's body
|
|
10
|
+
lives*: Python composes `class MornyeAPI(SomeMixin, ...):` exactly the
|
|
11
|
+
same as if the methods were written directly in the class, and `self.<attr>`
|
|
12
|
+
inside a mixin's methods resolves against the final composed instance
|
|
13
|
+
regardless of which mixin (or the main class itself) actually defines that
|
|
14
|
+
attribute. That's what these mixins are: the same methods, same names, same
|
|
15
|
+
behavior, moved out of app.py's 2000+-line single class body into smaller,
|
|
16
|
+
independently-readable files grouped by what they do.
|
|
17
|
+
|
|
18
|
+
This is a first, intentionally partial pass — see the "God object" note in
|
|
19
|
+
app.py's own module docstring for what's still there and why.
|
|
20
|
+
"""
|