├── .blueprint ├── .dockerignore ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app ├── Console │ └── Commands │ │ ├── ClearEpgFileCache.php │ │ ├── CompareVersion.php │ │ ├── DisableMfa.php │ │ ├── EpgCacheHealthCheck.php │ │ ├── FlushFfmpegProcessCache.php │ │ ├── FlushJobsTable.php │ │ ├── GenerateAppKey.php │ │ ├── GenerateEpgCache.php │ │ ├── GitInfo.php │ │ ├── LogoCacheCleanup.php │ │ ├── ManageSharedStreams.php │ │ ├── PruneStaleHlsProcesses.php │ │ ├── RefreshEpg.php │ │ ├── RefreshEpgSchedulesDirectData.php │ │ ├── RefreshPlaylist.php │ │ ├── ResetPassword.php │ │ ├── RestartQueue.php │ │ ├── RunScheduledBackups.php │ │ ├── ShowConfig.php │ │ ├── SqliteWalEnable.php │ │ ├── TestBroadcasting.php │ │ ├── TestSchedulesDirectCommand.php │ │ ├── TestSystemStats.php │ │ ├── TestXtream.php │ │ └── Xtream2Strm.php ├── Enums │ ├── ChannelLogoType.php │ ├── EpgSourceType.php │ ├── PlaylistChannelId.php │ └── Status.php ├── Events │ ├── CustomPlaylistCreated.php │ ├── EpgCreated.php │ ├── EpgDeleted.php │ ├── EpgUpdated.php │ ├── MergedPlaylistCreated.php │ ├── PlaylistCreated.php │ ├── PlaylistDeleted.php │ ├── PlaylistUpdated.php │ ├── StreamingStarted.php │ ├── StreamingStopped.php │ └── SyncCompleted.php ├── Exceptions │ ├── AllPlaylistsExhaustedException.php │ ├── FatalStreamContentException.php │ ├── MaxRetriesReachedException.php │ └── SourceNotResponding.php ├── Facades │ ├── GitInfo.php │ ├── LogoFacade.php │ ├── PlaylistFacade.php │ └── ProxyFacade.php ├── Filament │ ├── Auth │ │ ├── EditProfile.php │ │ └── Login.php │ ├── Exports │ │ └── ChannelExporter.php │ ├── GuestPanel │ │ ├── Pages │ │ │ ├── Concerns │ │ │ │ ├── HasGuestAuth.php │ │ │ │ └── HasPlaylist.php │ │ │ └── GuestDashboard.php │ │ └── Resources │ │ │ ├── Series │ │ │ ├── Pages │ │ │ │ ├── ListSeries.php │ │ │ │ └── ViewSeries.php │ │ │ ├── RelationManagers │ │ │ │ └── EpisodesRelationManager.php │ │ │ └── SeriesResource.php │ │ │ └── Vods │ │ │ ├── Pages │ │ │ ├── ListVod.php │ │ │ └── ViewVod.php │ │ │ └── VodResource.php │ ├── Imports │ │ └── ChannelImporter.php │ ├── Pages │ │ ├── Backups.php │ │ ├── CustomDashboard.php │ │ ├── M3uProxyStreamMonitor.php │ │ ├── Preferences.php │ │ └── SharedStreamMonitor.php │ ├── Resources │ │ ├── Categories │ │ │ ├── CategoryResource.php │ │ │ ├── Pages │ │ │ │ ├── CreateCategory.php │ │ │ │ ├── EditCategory.php │ │ │ │ ├── ListCategories.php │ │ │ │ └── ViewCategory.php │ │ │ └── RelationManagers │ │ │ │ └── SeriesRelationManager.php │ │ ├── Channels │ │ │ ├── ChannelResource.php │ │ │ └── Pages │ │ │ │ ├── CreateChannel.php │ │ │ │ ├── EditChannel.php │ │ │ │ ├── ListChannels.php │ │ │ │ └── ViewChannel.php │ │ ├── CustomPlaylists │ │ │ ├── CustomPlaylistResource.php │ │ │ ├── Pages │ │ │ │ ├── CreateCustomPlaylist.php │ │ │ │ ├── EditCustomPlaylist.php │ │ │ │ ├── ListCustomPlaylists.php │ │ │ │ └── ViewCustomPlaylist.php │ │ │ └── RelationManagers │ │ │ │ ├── CategoriesRelationManager.php │ │ │ │ ├── ChannelsRelationManager.php │ │ │ │ ├── GroupsRelationManager.php │ │ │ │ ├── SeriesRelationManager.php │ │ │ │ └── VodRelationManager.php │ │ ├── EpgChannels │ │ │ ├── EpgChannelResource.php │ │ │ └── Pages │ │ │ │ ├── CreateEpgChannel.php │ │ │ │ ├── EditEpgChannel.php │ │ │ │ └── ListEpgChannels.php │ │ ├── EpgMaps │ │ │ ├── EpgMapResource.php │ │ │ └── Pages │ │ │ │ ├── CreateEpgMap.php │ │ │ │ ├── EditEpgMap.php │ │ │ │ └── ListEpgMaps.php │ │ ├── Epgs │ │ │ ├── EpgResource.php │ │ │ ├── Pages │ │ │ │ ├── CreateEpg.php │ │ │ │ ├── EditEpg.php │ │ │ │ ├── ListEpgs.php │ │ │ │ └── ViewEpg.php │ │ │ └── Widgets │ │ │ │ └── ImportProgress.php │ │ ├── Groups │ │ │ ├── GroupResource.php │ │ │ ├── Pages │ │ │ │ ├── CreateGroup.php │ │ │ │ ├── EditGroup.php │ │ │ │ ├── ListGroups.php │ │ │ │ └── ViewGroup.php │ │ │ └── RelationManagers │ │ │ │ ├── ChannelsRelationManager.php │ │ │ │ └── VodRelationManager.php │ │ ├── MergedPlaylists │ │ │ ├── MergedPlaylistResource.php │ │ │ ├── Pages │ │ │ │ ├── CreateMergedPlaylist.php │ │ │ │ ├── EditMergedPlaylist.php │ │ │ │ └── ListMergedPlaylists.php │ │ │ └── RelationManagers │ │ │ │ └── PlaylistsRelationManager.php │ │ ├── PersonalAccessTokens │ │ │ ├── Pages │ │ │ │ ├── CreatePersonalAccessToken.php │ │ │ │ ├── EditPersonalAccessToken.php │ │ │ │ └── ListPersonalAccessTokens.php │ │ │ └── PersonalAccessTokenResource.php │ │ ├── PlaylistAliases │ │ │ ├── Pages │ │ │ │ ├── CreatePlaylistAlias.php │ │ │ │ ├── EditPlaylistAlias.php │ │ │ │ └── ListPlaylistAliases.php │ │ │ └── PlaylistAliasResource.php │ │ ├── PlaylistAuths │ │ │ ├── Pages │ │ │ │ ├── CreatePlaylistAuth.php │ │ │ │ ├── EditPlaylistAuth.php │ │ │ │ └── ListPlaylistAuths.php │ │ │ ├── PlaylistAuthResource.php │ │ │ └── RelationManagers │ │ │ │ └── PlaylistsRelationManager.php │ │ ├── Playlists │ │ │ ├── Pages │ │ │ │ ├── CreatePlaylist.php │ │ │ │ ├── EditPlaylist.php │ │ │ │ ├── ListPlaylists.php │ │ │ │ └── ViewPlaylist.php │ │ │ ├── PlaylistResource.php │ │ │ ├── RelationManagers │ │ │ │ └── SyncStatusesRelationManager.php │ │ │ ├── Resources │ │ │ │ └── PlaylistSyncStatuses │ │ │ │ │ ├── Pages │ │ │ │ │ ├── CreatePlaylistSyncStatus.php │ │ │ │ │ ├── EditPlaylistSyncStatus.php │ │ │ │ │ ├── ListPlaylistSyncStatuses.php │ │ │ │ │ └── ViewPlaylistSyncStatus.php │ │ │ │ │ ├── PlaylistSyncStatusResource.php │ │ │ │ │ └── RelationManagers │ │ │ │ │ └── LogsRelationManager.php │ │ │ └── Widgets │ │ │ │ └── ImportProgress.php │ │ ├── PostProcesses │ │ │ ├── Pages │ │ │ │ ├── CreatePostProcess.php │ │ │ │ ├── EditPostProcess.php │ │ │ │ └── ListPostProcesses.php │ │ │ ├── PostProcessResource.php │ │ │ └── RelationManagers │ │ │ │ ├── LogsRelationManager.php │ │ │ │ └── ProcessesRelationManager.php │ │ ├── Series │ │ │ ├── Pages │ │ │ │ ├── CreateSeries.php │ │ │ │ ├── EditSeries.php │ │ │ │ └── ListSeries.php │ │ │ ├── RelationManagers │ │ │ │ └── EpisodesRelationManager.php │ │ │ └── SeriesResource.php │ │ └── Vods │ │ │ ├── Pages │ │ │ └── ListVod.php │ │ │ └── VodResource.php │ └── Widgets │ │ ├── DiscordWidget.php │ │ ├── DocumentsWidget.php │ │ ├── DonateCrypto.php │ │ ├── KoFiWidget.php │ │ ├── PayPalDonateWidget.php │ │ ├── QuickActionsWidget.php │ │ ├── SharedStreamPerformanceChart.php │ │ ├── SharedStreamStatsWidget.php │ │ ├── StatsOverview.php │ │ ├── SystemHealthWidget.php │ │ └── UpdateNoticeWidget.php ├── Forms │ └── Components │ │ ├── MediaFlowProxyUrl.php │ │ ├── PlaylistEpgUrl.php │ │ ├── PlaylistInfo.php │ │ ├── PlaylistM3uUrl.php │ │ └── XtreamApiInfo.php ├── Http │ ├── Controllers │ │ ├── Api │ │ │ ├── EpgApiController.php │ │ │ ├── M3uProxyApiController.php │ │ │ ├── SharedStreamApiController.php │ │ │ └── SystemInfoController.php │ │ ├── ChannelController.php │ │ ├── Controller.php │ │ ├── EpgController.php │ │ ├── EpgFileController.php │ │ ├── EpgGenerateController.php │ │ ├── LogoProxyController.php │ │ ├── PlaylistController.php │ │ ├── PlaylistGenerateController.php │ │ ├── SchedulesDirectImageProxyController.php │ │ ├── SharedStreamController.php │ │ ├── StreamTestController.php │ │ ├── UserController.php │ │ ├── WebhookTestController.php │ │ ├── XtreamApiController.php │ │ └── XtreamStreamController.php │ └── Middleware │ │ ├── AutoLoginMiddleware.php │ │ ├── DashboardMiddleware.php │ │ └── GuestPlaylistAuth.php ├── Infolists │ └── Components │ │ ├── SeriesPreview.php │ │ └── VideoPreview.php ├── Jobs │ ├── ChannelFindAndReplace.php │ ├── ChannelFindAndReplaceReset.php │ ├── CopyAttributesToPlaylist.php │ ├── CopyPlaylist.php │ ├── CreateBackup.php │ ├── DuplicatePlaylist.php │ ├── EpgChannelFindAndReplace.php │ ├── EpgChannelFindAndReplaceReset.php │ ├── EpisodeFindAndReplace.php │ ├── GenerateEpgCache.php │ ├── MapEpgToChannels.php │ ├── MapEpgToChannelsComplete.php │ ├── MapPlaylistChannelsToEpg.php │ ├── MergeChannels.php │ ├── ProcessEpgImport.php │ ├── ProcessEpgImportChunk.php │ ├── ProcessEpgImportComplete.php │ ├── ProcessEpgSDImport.php │ ├── ProcessM3uImport.php │ ├── ProcessM3uImportChunk.php │ ├── ProcessM3uImportComplete.php │ ├── ProcessM3uImportSeries.php │ ├── ProcessM3uImportSeriesChunk.php │ ├── ProcessM3uImportSeriesComplete.php │ ├── ProcessM3uImportSeriesEpisodes.php │ ├── ProcessM3uImportVod.php │ ├── ProcessVodChannels.php │ ├── RestartQueue.php │ ├── RestoreBackup.php │ ├── RunPostProcess.php │ ├── SeriesFindAndReplace.php │ ├── SharedStreamCleanup.php │ ├── StreamBufferManager.php │ ├── StreamMonitorUpdate.php │ ├── SyncSeriesStrmFiles.php │ ├── SyncVodStrmFiles.php │ ├── SyncXtreamSeries.php │ └── UnmergeChannels.php ├── Listeners │ ├── BackupFailed.php │ ├── CustomPlaylistListener.php │ ├── EpgListener.php │ ├── GroupListener.php │ ├── MergedPlaylistListener.php │ ├── PlaylistListener.php │ ├── StreamingStartListener.php │ ├── StreamingStopListener.php │ └── SyncListener.php ├── Livewire │ ├── BackupDestinationListRecords.php │ ├── ChannelStreamStats.php │ ├── EpgViewer.php │ ├── MediaFlowProxyUrl.php │ ├── PlaylistEpgUrl.php │ ├── PlaylistInfo.php │ ├── PlaylistM3uUrl.php │ ├── StreamPlayer.php │ └── XtreamApiInfo.php ├── Mail │ └── PostProcessMail.php ├── Models │ ├── Category.php │ ├── Channel.php │ ├── ChannelFailover.php │ ├── CustomPlaylist.php │ ├── Epg.php │ ├── EpgChannel.php │ ├── EpgMap.php │ ├── Episode.php │ ├── Group.php │ ├── Job.php │ ├── MergedPlaylist.php │ ├── PersonalAccessToken.php │ ├── Playlist.php │ ├── PlaylistAlias.php │ ├── PlaylistAuth.php │ ├── PlaylistSyncStatus.php │ ├── PlaylistSyncStatusLog.php │ ├── PostProcess.php │ ├── PostProcessLog.php │ ├── Season.php │ ├── Series.php │ ├── SharedStream.php │ ├── SharedStreamClient.php │ ├── SharedStreamStat.php │ ├── SourceGroup.php │ └── User.php ├── Pivots │ ├── CustomPlaylistPivot.php │ ├── MergedPlaylistPivot.php │ ├── PlaylistAuthPivot.php │ └── PostProcessPivot.php ├── Policies │ ├── CustomPlaylistPolicy.php │ ├── EpgPolicy.php │ ├── MergedPlaylistPolicy.php │ └── PlaylistPolicy.php ├── Providers │ ├── AppServiceProvider.php │ ├── Filament │ │ ├── AdminPanelProvider.php │ │ └── GuestPanelPanelProvider.php │ ├── HorizonServiceProvider.php │ ├── LogoServiceProvider.php │ └── VersionServiceProvider.php ├── Rules │ ├── CheckIfUrlOrLocalPath.php │ └── Cron.php ├── Services │ ├── EpgCacheService.php │ ├── ExternalIpService.php │ ├── FfmpegCodecService.php │ ├── GitInfoService.php │ ├── LogoService.php │ ├── M3uProxyService.php │ ├── PlayerTranscodingService.php │ ├── PlaylistService.php │ ├── PlaylistUrlService.php │ ├── ProxyService.php │ ├── SchedulesDirectService.php │ ├── SharedStreamService.php │ ├── SimilaritySearchService.php │ ├── StreamMonitorService.php │ └── XtreamService.php ├── Settings │ └── GeneralSettings.php ├── Tables │ └── Columns │ │ ├── PivotNameColumn.php │ │ ├── PlaylistUrlColumn.php │ │ └── SyncStats.php └── Traits │ ├── HasParentResource.php │ ├── Schedulable.php │ ├── ShortUrlTrait.php │ └── TracksActiveStreams.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── backup-restore.php ├── backup.php ├── blueprint.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── dev.php ├── filament-log-viewer.php ├── filament.php ├── filesystems.php ├── horizon.php ├── livewire.php ├── logging.php ├── mail.php ├── proxy.php ├── queue.php ├── reverb.php ├── sanctum.php ├── scramble.php ├── services.php ├── session.php ├── settings.php ├── short-url.php └── xtream.php ├── database ├── .gitignore ├── factories │ ├── CategoryFactory.php │ ├── ChannelFactory.php │ ├── ChannelFailoverFactory.php │ ├── CustomPlaylistFactory.php │ ├── EpgChannelFactory.php │ ├── EpgFactory.php │ ├── EpgMapFactory.php │ ├── EpisodeFactory.php │ ├── GroupFactory.php │ ├── MergedPlaylistFactory.php │ ├── PlaylistAuthFactory.php │ ├── PlaylistFactory.php │ ├── PlaylistSyncStatusFactory.php │ ├── PlaylistSyncStatusLogFactory.php │ ├── PostProcessFactory.php │ ├── SeasonFactory.php │ ├── SeriesFactory.php │ └── UserFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000002_create_jobs_table.php │ ├── 2019_12_22_015115_create_short_urls_table.php │ ├── 2019_12_22_015214_create_short_url_visits_table.php │ ├── 2020_02_11_224848_update_short_url_table_for_version_two_zero_zero.php │ ├── 2020_02_12_008432_update_short_url_visits_table_for_version_two_zero_zero.php │ ├── 2020_04_10_224546_update_short_url_table_for_version_three_zero_zero.php │ ├── 2020_04_20_009283_update_short_url_table_add_option_to_forward_query_params.php │ ├── 2022_12_14_083707_create_settings_table.php │ ├── 2024_12_17_203030_create_admin_user.php │ ├── 2024_12_18_233316_create_notifications_table.php │ ├── 2024_12_19_034846_create_imports_table.php │ ├── 2024_12_19_034847_create_exports_table.php │ ├── 2024_12_19_034848_create_failed_import_rows_table.php │ ├── 2024_12_19_154247_create_playlists_table.php │ ├── 2024_12_19_154248_create_groups_table.php │ ├── 2024_12_19_154249_create_channels_table.php │ ├── 2025_01_06_161153_add_import_batch_no_to_channels.php │ ├── 2025_01_19_220431_add_import_batch_no_to_groups.php │ ├── 2025_01_21_164226_create_epgs_table.php │ ├── 2025_01_21_164227_create_epg_channels_table.php │ ├── 2025_01_21_170519_add_synced_column_to_epgs.php │ ├── 2025_01_21_170900_add_import_batch_to_epg_channels.php │ ├── 2025_01_21_171508_add_status_to_epg.php │ ├── 2025_01_21_171725_add_errors_columnd_to_epg.php │ ├── 2025_01_21_173519_add_uploads_column_to_epg.php │ ├── 2025_01_21_174319_add_user_id_to_epg_channels.php │ ├── 2025_01_21_224113_add_icon_to_epg_channels.php │ ├── 2025_01_22_200353_create_epg_programmes_table.php │ ├── 2025_01_22_200738_remove_programme_column_from_epg_channels.php │ ├── 2025_01_23_204711_add_sync_time_to_playlists.php │ ├── 2025_01_28_152132_create_merged_playlists_table.php │ ├── 2025_01_28_152133_create_merged_playlist_playlist_table.php │ ├── 2025_01_28_152137_create_custom_playlists_table.php │ ├── 2025_01_28_152138_create_channel_custom_playlist_table.php │ ├── 2025_01_28_225053_add_sync_time_to_epgs.php │ ├── 2025_01_28_225829_add_index_to_epg_channels.php │ ├── 2025_01_29_190403_add_epg_channel_id_column_to_channels.php │ ├── 2025_02_01_151321_drop_epg_programmes_table.php │ ├── 2025_02_01_153803_add_uuid_column_to_epgs.php │ ├── 2025_02_06_161829_add_auto_sync_toggle_to_playlists.php │ ├── 2025_02_06_161839_add_auto_sync_toggle_to_epgs.php │ ├── 2025_02_09_165446_add_avatar_url_to_users.php │ ├── 2025_02_09_195223_create_filament-jobs-monitor_table.php │ ├── 2025_02_09_215228_add_progress_column_to_playlists.php │ ├── 2025_02_09_215233_add_progress_column_to_playlists.php │ ├── 2025_02_13_171533_add_processing_flag_to_playlists.php │ ├── 2025_02_13_171537_add_processing_flag_to_epgs.php │ ├── 2025_02_13_215803_create_jobs_table.php │ ├── 2025_02_13_224233_add_sync_interval_to_playlists.php │ ├── 2025_02_13_224236_add_sync_interval_to_epgs.php │ ├── 2025_02_14_214426_add_playlist_import_prefs_column_to_playlists.php │ ├── 2025_02_14_222330_add_groups_column_to_playlists.php │ ├── 2025_02_15_150422_reset_playlist_interval_on_playlists.php │ ├── 2025_02_15_150833_reset_playlist_interval_on_epgs.php │ ├── 2025_02_16_181719_create_epg_maps_table.php │ ├── 2025_02_16_184838_add_counts_to_epg_maps.php │ ├── 2025_02_16_193535_add_override_column_to_epg_maps.php │ ├── 2025_02_18_173424_add_enable_toggle_to_playlists.php │ ├── 2025_02_18_185618_add_internal_group_name_to_groups.php │ ├── 2025_02_19_152211_add_is_custom_column_to_groups.php │ ├── 2025_02_21_155914_drop_queue_monitors_table.php │ ├── 2025_02_21_205916_add_uploads_column_to_playlists.php │ ├── 2025_02_26_200928_add_xtream_columns_to_playlists.php │ ├── 2025_02_27_221823_add_user_agent_ssl_prefs_to_playlists.php │ ├── 2025_02_27_221833_add_user_agent_ssl_prefs_to_epgs.php │ ├── 2025_02_27_224518_add_default_user_agent_to_playlists_and_epgs.php │ ├── 2025_03_04_140444_add_editable_attributes_to_channels.php │ ├── 2025_03_04_145644_add_icon_preference_toggle_to_channels.php │ ├── 2025_03_05_142409_add_recurring_toggle_to_epg_maps.php │ ├── 2025_03_05_145318_add_playlist_id_column_to_epg_maps.php │ ├── 2025_03_05_165633_add_mapped_at_column_to_epg_maps.php │ ├── 2025_03_06_155450_add_custom_id_and_url_to_channels.php │ ├── 2025_03_07_153936_add_sort_column_to_channels.php │ ├── 2025_03_07_190744_add_auto_channel_output_option_to_playlists.php │ ├── 2025_03_09_161832_add_xtream_status_to_playlists.php │ ├── 2025_03_10_205435_add_extvlcopt_column_to_channels.php │ ├── 2025_03_12_215809_create_personal_access_tokens_table.php │ ├── 2025_03_12_220659_create_breezy_sessions_table.php │ ├── 2025_03_18_132548_add_auto_sort_order_to_playlists.php │ ├── 2025_03_19_161419_add_enable_proxy_to_playlists.php │ ├── 2025_03_19_161433_add_enable_proxy_to_custom_playlists.php │ ├── 2025_03_19_161447_add_enable_proxy_to_merged_playlists.php │ ├── 2025_03_20_131924_add_hdhr_tuners_to_playlists.php │ ├── 2025_03_20_174644_add_kodi_drop_column_to_channels.php │ ├── 2025_03_20_230059_add_channel_id_selection_to_playlists.php │ ├── 2025_03_25_204141_add_preferred_local_to_epg.php │ ├── 2025_03_31_212827_create_playlist_auths_table.php │ ├── 2025_03_31_221549_add_authable_pivot_table.php │ ├── 2025_04_03_140401_add_dummy_epg_config_to_playlists.php │ ├── 2025_04_15_212653_add_short_url_columns_to_playlists.php │ ├── 2025_04_19_175837_create_tag_tables.php │ ├── 2025_04_21_203422_create_post_processes_table.php │ ├── 2025_04_21_203508_create_processables_table.php │ ├── 2025_04_21_213721_crate_post_processing_logs_table.php │ ├── 2025_04_24_205934_add_new_column_to_channels.php │ ├── 2025_04_24_205937_add_new_column_to_groups.php │ ├── 2025_04_24_212113_create_playlist_sync_statuses_table.php │ ├── 2025_04_24_214454_add_stats_meta_to_playlist_sync_status.php │ ├── 2025_04_26_124521_add_dummy_epg_category_to_playlists.php │ ├── 2025_04_28_170814_create_playlist_sync_status_logs_table.php │ ├── 2025_04_28_170938_remove_and_move_sync_status_to_log_entries.php │ ├── 2025_05_03_172748_add_user_agent_to_custom_merged_playlists.php │ ├── 2025_05_05_180136_add_catchup_to_channels.php │ ├── 2025_05_07_202251_remove_batch_job_tables.php │ ├── 2025_05_09_145446_alter_notifications_data_to_jsonb.php │ ├── 2025_05_09_220958_update_epg_channel_title_column.php │ ├── 2025_05_20_135723_convert_json_to_jsonb.php │ ├── 2025_05_21_185308_create_categories_table.php │ ├── 2025_05_21_185309_create_series_table.php │ ├── 2025_05_21_185310_create_seasons_table.php │ ├── 2025_05_21_185311_create_episodes_table.php │ ├── 2025_05_21_185917_add_series_progress_to_playlists.php │ ├── 2025_05_21_192755_add_source_series_id_to_series.php │ ├── 2025_05_21_213847_update_date_time_column.php │ ├── 2025_05_22_114818_add_sort_order_to_series.php │ ├── 2025_05_22_201214_add_sync_location_to_series.php │ ├── 2025_05_24_124551_add_playlist_source_groups_table.php │ ├── 2025_05_25_135318_live_enabled_by_default.php │ ├── 2025_05_27_205110_add_proxy_options_to_playlists.php │ ├── 2025_05_30_131848_create_channel_failovers_table.php │ ├── 2025_05_30_152649_add_available_streams_to_playlists.php │ ├── 2025_06_01_020531_add_sort_order_to_groups_table.php │ ├── 2025_06_09_133035_add_addtl_display_names_to_epg_channels.php │ ├── 2025_06_10_212659_add_additional_attributes_to_channels.php │ ├── 2025_06_10_215740_update_playlist_id_column_allow_null_on_channels.php │ ├── 2025_06_10_220321_add_custom_playlist_id_column_to_channels.php │ ├── 2025_06_11_150943_add_available_streams_to_custom_playlists.php │ ├── 2025_06_11_190844_add_prefix_suffix_ignore_tags_to_epg_maps.php │ ├── 2025_06_11_205559_update_channel_name_to_allow_null_on_channels.php │ ├── 2025_06_12_165152_add_tvg_shift_to_channels.php │ ├── 2025_06_12_200107_update_shift_column_on_channels.php │ ├── 2025_06_13_135855_add_vod_column_on_channels.php │ ├── 2025_06_17_135730_create_shared_streams_table.php │ ├── 2025_06_17_222347_add_error_message_to_shared_streams_table.php │ ├── 2025_06_23_164102_add_additional_count_columns_to_epg_maps.php │ ├── 2025_06_23_205558_add_container_extension_column_to_channels.php │ ├── 2025_06_24_145609_add_info_meta_column_to_episodes.php │ ├── 2025_06_24_223917_add_source_id_column_to_channels.php │ ├── 2025_06_27_191925_add_unique_constraint_to_authenticatables_table.php │ ├── 2025_06_30_161208_update_source_id_column_on_channels.php │ ├── 2025_07_14_153343_change_playlist_uuid_column_type.php │ ├── 2025_07_15_130656_change_custom_merged_playlist_uuid_column_type.php │ ├── 2025_07_16_070133_update_playlist_column_types.php │ ├── 2025_07_16_161107_create_series_custom_playlist_table.php │ ├── 2025_07_21_082745_add_backup_before_sync_column_to_playlists.php │ ├── 2025_07_22_074848_add_indexes_to_channels.php │ ├── 2025_07_25_082930_add_is_cached_column_to_epgs.php │ ├── 2025_07_31_175630_add_schedules_direct_fields_to_epgs_table.php │ ├── 2025_08_02_062327_add_sd_config_column_to_epgs.php │ ├── 2025_08_03_172048_add_conditions_to_post_processes.php │ ├── 2025_08_06_072847_add_cache_meta_column_to_epgs.php │ ├── 2025_08_08_070415_add_sync_toggle_to_playlists.php │ ├── 2025_08_12_154638_add_cache_progress_to_epg_table.php │ ├── 2025_08_16_150208_add_server_timezone_to_playlists.php │ ├── 2025_08_20_134822_add_new_sync_index_columns_to_channels.php │ ├── 2025_08_21_100430_add_programme_count_to_epgs.php │ ├── 2025_08_21_142446_add_logo_internal_to_channels.php │ ├── 2025_08_22_210003_add_mfa_column_to_users.php │ ├── 2025_09_01_150234_update_shared_stream_type.php │ ├── 2025_09_02_200144_add_enable_toggle_column_to_episodes.php │ ├── 2025_09_03_072145_add_include_series_toggle_for_m3u.php │ ├── 2025_09_03_080022_add_series_metadata_autofetch_toggle.php │ ├── 2025_09_04_064050_add_stream_file_fields_to_channels.php │ ├── 2025_09_04_081310_add_last_metadata_fetch_column_to_channels.php │ ├── 2025_09_13_093721_create_playlist_aliases_table.php │ ├── 2025_09_14_101901_add_short_urls_to_playlist_aliases.php │ ├── 2025_09_17_070541_add_proxy_field_to_playlist_aliases.php │ ├── 2025_09_17_102508_update_schedule_intervals_to_cron_values.php │ ├── 2025_09_18_151019_add_sd_metadata_column_to_epgs.php │ ├── 2025_09_22_081912_change_series_name_column.php │ ├── 2025_09_22_110254_add_logo_proxy_toggle_to_playlists.php │ ├── 2025_09_22_111113_add_logo_proxy_toggle_to_playlisy_aliases.php │ ├── 2025_09_26_115419_add_custom_icon_to_epg_channels.php │ ├── 2025_09_30_094734_add_auto_merge_settings_to_playlists.php │ ├── 2025_10_01_074107_add_sync_vod_files_to_playlists.php │ ├── 2025_10_01_081952_add_enable_toggles_to_groups_and_categories.php │ ├── 2025_10_01_150410_update_m3u_channel_source_id.php │ ├── 2025_10_02_083138_add_channels_column_to_epg_maps.php │ └── 2025_10_05_085522_add_metadata_to_series_seasons.php ├── seeders │ └── DatabaseSeeder.php └── settings │ ├── 2025_04_21_140953_create_general_settings.php │ ├── 2025_05_01_160604_add_mfp_user_agent_fields.php │ ├── 2025_05_16_070340_add_ffmpeg_codec_fields.php │ ├── 2025_05_19_130544_add_ffmpeg_path_select_option.php │ ├── 2025_05_29_152250_add_hw_accell_ffmpeg_settings.php │ ├── 2025_05_29_180906_add_ffmpeg_custom_command_template_settings.php │ ├── 2025_06_03_190045_add_ffmpeg_hls_settings.php │ ├── 2025_07_22_000000_add_ffprobe_path_select_option.php │ ├── 2025_08_03_181426_add_smtp_settings_fields.php │ ├── 2025_08_07_140326_add_stream_file_sync_options.php │ ├── 2025_08_16_110411_add_video_player_proxy_options.php │ ├── 2025_08_20_100536_add_sync_abort_options.php │ ├── 2025_09_04_064323_add_vod_sync_location_settings.php │ ├── 2025_09_05_082037_add_backup_schedule_to_settings.php │ ├── 2025_09_17_093017_add_stream_file_sync_include_category.php │ └── 2025_09_30_160741_add_wan_output_toggle.php ├── docker ├── 8.4 │ ├── my.cnf │ ├── nginx │ │ ├── laravel.conf │ │ └── nginx.conf │ ├── php.ini │ ├── redis.conf │ ├── supervisord.conf │ └── www.conf ├── mariadb │ └── create-testing-database.sh ├── mysql │ └── create-testing-database.sh └── pgsql │ └── create-testing-database.sql ├── docs ├── auto-merge-channels.md ├── buffered-streaming.md ├── epg-optimization.md ├── stream-player.md └── test-stream.md ├── draft.yml ├── package-lock.json ├── package.json ├── phpunit.xml ├── postcss.config.js ├── public ├── .htaccess ├── css │ ├── aymanalhattami │ │ └── filament-slim-scrollbar │ │ │ └── filament-slim-scrollbar.css │ ├── devonab │ │ └── filament-easy-footer │ │ │ └── filament-easy-footer-styles.css │ ├── filament-spatie-backup │ │ └── filament-spatie-backup-styles.css │ ├── filament │ │ ├── filament │ │ │ └── app.css │ │ ├── forms │ │ │ └── forms.css │ │ └── support │ │ │ └── support.css │ ├── ryanchandler │ │ └── filament-progress-column │ │ │ └── filament-progress-column.css │ └── saade │ │ └── filament-laravel-log │ │ └── filament-laravel-log-styles.css ├── episode-placeholder.png ├── favicon.ico ├── favicon.png ├── favicon.svg ├── fonts │ └── filament │ │ └── filament │ │ └── inter │ │ ├── index.css │ │ ├── inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2 │ │ ├── inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2 │ │ ├── inter-cyrillic-wght-normal-EWLSKVKN.woff2 │ │ ├── inter-cyrillic-wght-normal-R5CMSONN.woff2 │ │ ├── inter-greek-ext-wght-normal-7GGTF7EK.woff2 │ │ ├── inter-greek-ext-wght-normal-ZEVLMORV.woff2 │ │ ├── inter-greek-wght-normal-AXVTPQD5.woff2 │ │ ├── inter-greek-wght-normal-N43DBLU2.woff2 │ │ ├── inter-latin-ext-wght-normal-5SRY4DMZ.woff2 │ │ ├── inter-latin-ext-wght-normal-GZCIV3NH.woff2 │ │ ├── inter-latin-ext-wght-normal-HA22NDSG.woff2 │ │ ├── inter-latin-wght-normal-NRMW37G5.woff2 │ │ ├── inter-latin-wght-normal-O25CN4JL.woff2 │ │ ├── inter-latin-wght-normal-OPIJAQLS.woff2 │ │ ├── inter-vietnamese-wght-normal-CE5GGD3W.woff2 │ │ └── inter-vietnamese-wght-normal-TWG5UU7E.woff2 ├── images │ ├── 1080.svg │ ├── 4k.svg │ ├── bmc-button.svg │ ├── bmm-logo.svg │ ├── crypto-icons │ │ ├── bitcoin.svg │ │ ├── crypto-coins.svg │ │ ├── ethereum.svg │ │ ├── litecoin.svg │ │ ├── ripple.svg │ │ ├── solana.svg │ │ └── tether.svg │ ├── discord.svg │ ├── github.svg │ ├── plex-logo.svg │ ├── sd.svg │ └── xtream-codes.png ├── index.php ├── js │ ├── devonab │ │ └── filament-easy-footer │ │ │ └── filament-easy-footer-scripts.js │ ├── filament │ │ ├── actions │ │ │ └── actions.js │ │ ├── filament │ │ │ ├── app.js │ │ │ └── echo.js │ │ ├── forms │ │ │ └── components │ │ │ │ ├── checkbox-list.js │ │ │ │ ├── code-editor.js │ │ │ │ ├── color-picker.js │ │ │ │ ├── date-time-picker.js │ │ │ │ ├── file-upload.js │ │ │ │ ├── key-value.js │ │ │ │ ├── markdown-editor.js │ │ │ │ ├── rich-editor.js │ │ │ │ ├── select.js │ │ │ │ ├── slider.js │ │ │ │ ├── tags-input.js │ │ │ │ └── textarea.js │ │ ├── notifications │ │ │ └── notifications.js │ │ ├── schemas │ │ │ ├── components │ │ │ │ ├── actions.js │ │ │ │ ├── tabs.js │ │ │ │ └── wizard.js │ │ │ └── schemas.js │ │ ├── support │ │ │ ├── async-alpine.js │ │ │ └── support.js │ │ ├── tables │ │ │ ├── components │ │ │ │ ├── columns │ │ │ │ │ ├── checkbox.js │ │ │ │ │ ├── select.js │ │ │ │ │ ├── text-input.js │ │ │ │ │ └── toggle.js │ │ │ │ └── table.js │ │ │ └── tables.js │ │ └── widgets │ │ │ └── components │ │ │ ├── chart.js │ │ │ └── stats-overview │ │ │ └── stat │ │ │ └── chart.js │ └── saade │ │ └── filament-laravel-log │ │ └── components │ │ └── filament-laravel-log.js ├── logo.png ├── logo.svg ├── placeholder.png └── robots.txt ├── rebuild.sh ├── refresh.sh ├── resources ├── css │ ├── app.css │ ├── components │ │ ├── footer.css │ │ ├── tables.scss │ │ └── widgets.css │ └── globals │ │ └── global.scss ├── js │ ├── app.js │ ├── echo.js │ └── vendor │ │ ├── epg-viewer.js │ │ ├── multi-stream-manager.js │ │ ├── qrcode.js │ │ └── stream-viewer.js └── views │ ├── components │ ├── copy-to-clipboard.blade.php │ ├── external-ip-display.blade.php │ ├── floating-stream-players.blade.php │ └── qr-modal.blade.php │ ├── errors │ ├── 404.blade.php │ ├── 429.blade.php │ └── 503.blade.php │ ├── filament │ ├── admin │ │ └── logo.blade.php │ ├── guest-panel │ │ └── pages │ │ │ └── guest-dashboard.blade.php │ ├── pages │ │ ├── dashboard.blade.php │ │ ├── m3u-proxy-stream-monitor.blade.php │ │ ├── shared-stream-monitor.blade.php │ │ ├── stats.blade.php │ │ └── streaming-channel-stats.blade.php │ ├── resources │ │ ├── epg-resource │ │ │ └── widgets │ │ │ │ └── import-progress.blade.php │ │ └── playlist-resource │ │ │ └── widgets │ │ │ └── import-progress.blade.php │ └── widgets │ │ ├── discord-widget.blade.php │ │ ├── docs-widget.blade.php │ │ ├── donate-crypto.blade.php │ │ ├── ko-fi-widget.blade.php │ │ ├── live-connection-monitor.blade.php │ │ ├── pay-pal-donate-widget.blade.php │ │ ├── quick-actions.blade.php │ │ ├── streaming-alerts.blade.php │ │ └── update-notice-widget.blade.php │ ├── footer.blade.php │ ├── forms │ └── components │ │ ├── media-flow-proxy-url.blade.php │ │ ├── playlist-epg-url.blade.php │ │ ├── playlist-info.blade.php │ │ ├── playlist-m3u-url.blade.php │ │ └── xtream-api-info.blade.php │ ├── hdhr.blade.php │ ├── infolists │ └── components │ │ ├── series-preview.blade.php │ │ └── video-preview.blade.php │ ├── livewire │ ├── channel-stream-stats.blade.php │ ├── epg-viewer.blade.php │ ├── media-flow-proxy-url.blade.php │ ├── playlist-epg-url.blade.php │ ├── playlist-info.blade.php │ ├── playlist-m3u-url.blade.php │ ├── stream-player.blade.php │ └── xtream-api-info.blade.php │ ├── mail │ └── post_process.blade.php │ ├── modals │ └── hardware-accel-info.blade.php │ ├── tables │ └── columns │ │ ├── playlist-auth-name.blade.php │ │ ├── playlist-auth-url.blade.php │ │ └── sync-stats.blade.php │ └── vendor │ ├── filament-breezy │ └── components │ │ └── grid-section.blade.php │ ├── filament-progress-column │ └── column.blade.php │ ├── filament-spatie-backup │ ├── .gitkeep │ ├── components │ │ ├── backup-destination-list-records.blade.php │ │ └── backup-destination-status-list-records.blade.php │ └── pages │ │ └── backups.blade.php │ └── log-viewer │ ├── .gitkeep │ └── index.blade.php ├── routes ├── api.php ├── channels.php ├── console.php └── web.php ├── screenshots ├── api.png ├── channel-editing.png ├── in-app-epg-preview.png ├── in-app-playlist-epg-playback.png ├── in-app-playlist-epg-preview.png ├── post-processing.png ├── proxy-config.png ├── proxy-monitor.png └── series-mgmt.png ├── start-container ├── storage ├── app │ ├── .gitignore │ ├── private │ │ └── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── stubs └── blueprint │ ├── constructor.stub │ ├── controller.authorize-resource.stub │ ├── controller.class.stub │ ├── controller.method.stub │ ├── draft.stub │ ├── event.stub │ ├── factory.stub │ ├── job.stub │ ├── mail.stub │ ├── mail.view.stub │ ├── migration.stub │ ├── model.casts.stub │ ├── model.class.stub │ ├── model.connection.stub │ ├── model.fillable.stub │ ├── model.guarded.stub │ ├── model.hidden.stub │ ├── model.incrementing.stub │ ├── model.method.comment.stub │ ├── model.method.stub │ ├── model.table.stub │ ├── model.timestamps.stub │ ├── notification.stub │ ├── pest.test.case.stub │ ├── pest.test.class.stub │ ├── phpunit.test.case.stub │ ├── phpunit.test.class.stub │ ├── policy.class.stub │ ├── policy.method.create.stub │ ├── policy.method.delete.stub │ ├── policy.method.forceDelete.stub │ ├── policy.method.restore.stub │ ├── policy.method.update.stub │ ├── policy.method.view.stub │ ├── policy.method.viewAny.stub │ ├── request.stub │ ├── resource.stub │ ├── seeder.stub │ └── view.stub ├── tests ├── Feature │ ├── ChannelCustomGroupFilamentTest.php │ ├── ChannelCustomGroupTest.php │ ├── CopyAttributesToPlaylistTest.php │ ├── GuestDashboardAuthTest.php │ ├── SchedulesDirectArtworkTest.php │ ├── SharedStreamingTest.php │ └── XtreamApiControllerTest.php ├── Pest.php ├── TestCase.php └── Unit │ ├── ExampleTest.php │ ├── MergeChannelsTest.php │ ├── SharedStreamServiceRedirectTest.php │ └── SharedStreamServiceTest.php └── vite.config.js /.blueprint: -------------------------------------------------------------------------------- 1 | created: 2 | - database/factories/ChannelFailoverFactory.php 3 | - database/migrations/2025_05_30_131848_create_channel_failovers_table.php 4 | - app/Models/ChannelFailover.php 5 | models: 6 | ChannelFailover: { user_id: 'id foreign', channel_id: 'id foreign', channel_failover_id: 'id foreign:channels', sort: 'unsigned integer', metadata: jsonb } 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | docker-compose.yml 2 | vendor 3 | node_modules 4 | storage/app/private/* 5 | storage/app/public/* 6 | storage/framework/cache/* 7 | storage/framework/sessions/* 8 | storage/framework/testing/* 9 | storage/framework/views/* 10 | stubs 11 | *.sh 12 | *.sqlite 13 | *.sql 14 | *.csv 15 | *.m3u 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /storage/pail 8 | /vendor 9 | /docker/sail-mariadb 10 | docker-config/ 11 | .env 12 | .env.backup 13 | .env.production 14 | .phpactor.json 15 | .phpunit.result.cache 16 | Homestead.json 17 | Homestead.yaml 18 | auth.json 19 | npm-debug.log 20 | yarn-error.log 21 | /.fleet 22 | /.idea 23 | /.nova 24 | /.vscode 25 | /.zed 26 | *.sqlite 27 | *.sql 28 | *.csv 29 | *.m3u 30 | docker-compose.yml 31 | DockerfileDev 32 | .rr.yaml 33 | rr 34 | websockets.sh -------------------------------------------------------------------------------- /app/Console/Commands/RestartQueue.php: -------------------------------------------------------------------------------- 1 | info("🔄 Restarting Horizon queue...\n"); 29 | $this->call('horizon:terminate'); 30 | $this->call('queue:flush'); 31 | $this->info("✅ Horizon queue restarted\n"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Enums/ChannelLogoType.php: -------------------------------------------------------------------------------- 1 | 'success', 14 | self::Epg => 'gray', 15 | }; 16 | } 17 | 18 | public function getLabel(): ?string 19 | { 20 | return match ($this) { 21 | self::Channel => 'Channel', 22 | self::Epg => 'EPG', 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Enums/EpgSourceType.php: -------------------------------------------------------------------------------- 1 | 'URL/XML File', 14 | self::SCHEDULES_DIRECT => 'Schedules Direct', 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Enums/PlaylistChannelId.php: -------------------------------------------------------------------------------- 1 | 'success', 16 | self::ChannelId => 'gray', 17 | self::Name => 'gray', 18 | self::Title => 'gray', 19 | }; 20 | } 21 | 22 | public function getLabel(): ?string 23 | { 24 | return match ($this) { 25 | self::TvgId => 'TVG ID/Stream ID (default)', 26 | self::ChannelId => 'Channel Number', 27 | self::Name => 'Channel Name', 28 | self::Title => 'Channel Title', 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Enums/Status.php: -------------------------------------------------------------------------------- 1 | 'gray', 16 | self::Processing => 'warning', 17 | self::Completed => 'success', 18 | self::Failed => 'danger', 19 | }; 20 | } 21 | } -------------------------------------------------------------------------------- /app/Events/CustomPlaylistCreated.php: -------------------------------------------------------------------------------- 1 | header('referer'); 12 | $refererSegment2 = $referer ? (explode('/', parse_url($referer, PHP_URL_PATH))[3] ?? null) : null; 13 | $uuid = request()->route('uuid') ?? request()->attributes->get('playlist_uuid') ?? $refererSegment2; 14 | return $uuid; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Filament/Pages/CustomDashboard.php: -------------------------------------------------------------------------------- 1 | slideOver(), 20 | ]; 21 | } 22 | 23 | /** 24 | * @deprecated Override the `table()` method to configure the table. 25 | */ 26 | protected function getTableQuery(): ?Builder 27 | { 28 | return static::getResource()::getEloquentQuery() 29 | ->where('user_id', auth()->id()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Filament/Resources/Groups/Pages/CreateGroup.php: -------------------------------------------------------------------------------- 1 | slideOver() 20 | ->successRedirectUrl(fn($record): string => EditMergedPlaylist::getUrl(['record' => $record])), 21 | 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Filament/Resources/PersonalAccessTokens/Pages/CreatePersonalAccessToken.php: -------------------------------------------------------------------------------- 1 | id(); 16 | return $data; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Filament/Resources/PlaylistAuths/Pages/EditPlaylistAuth.php: -------------------------------------------------------------------------------- 1 | dispatch('refreshRelation'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Filament/Resources/Playlists/Pages/ListPlaylists.php: -------------------------------------------------------------------------------- 1 | where('user_id', auth()->id()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Filament/Resources/Playlists/RelationManagers/SyncStatusesRelationManager.php: -------------------------------------------------------------------------------- 1 | headerActions([ 20 | Actions\CreateAction::make(), 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Filament/Resources/Playlists/Resources/PlaylistSyncStatuses/Pages/CreatePlaylistSyncStatus.php: -------------------------------------------------------------------------------- 1 | id(); 16 | return $data; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Filament/Resources/PostProcesses/Pages/EditPostProcess.php: -------------------------------------------------------------------------------- 1 | dispatch('refreshRelation'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Filament/Resources/Series/Pages/CreateSeries.php: -------------------------------------------------------------------------------- 1 | getResource()::getUrl('index'); 19 | } 20 | 21 | protected function getSteps(): array 22 | { 23 | return SeriesResource::getFormSteps(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Filament/Widgets/DiscordWidget.php: -------------------------------------------------------------------------------- 1 | 1, 11 | 'lg' => 2, 12 | ]; 13 | 14 | protected string $view = 'filament.widgets.ko-fi-widget'; 15 | } 16 | -------------------------------------------------------------------------------- /app/Filament/Widgets/PayPalDonateWidget.php: -------------------------------------------------------------------------------- 1 | all(); 12 | return response()->json([ 13 | 'message' => 'Webhook received', 14 | 'method' => $request->method(), 15 | 'data' => $data 16 | ]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Middleware/AutoLoginMiddleware.php: -------------------------------------------------------------------------------- 1 | check()) { 20 | $user = User::where('email', config('auth.auto_login_email'))->first(); 21 | if ($user) { 22 | auth()->login($user); 23 | } 24 | } 25 | 26 | return $next($request); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Middleware/DashboardMiddleware.php: -------------------------------------------------------------------------------- 1 | check() && ! config('app.redirect_guest_to_login', false)) { 19 | // Check if the user is trying the login page 20 | if ($request->path() === trim(config('app.login_path', 'login'), '/')) { 21 | return $next($request); 22 | } 23 | return redirect()->to('/not-found'); 24 | } 25 | return $next($request); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Infolists/Components/SeriesPreview.php: -------------------------------------------------------------------------------- 1 | showDetails = $withDetails; 17 | return $this; 18 | } 19 | 20 | public function autoPlay(bool $autoPlay = true): static 21 | { 22 | $this->autoPlayEnabled = $autoPlay; 23 | return $this; 24 | } 25 | 26 | public function isAutoPlay(): bool 27 | { 28 | return (bool) $this->evaluate($this->autoPlayEnabled); 29 | } 30 | 31 | public function isWithDetails(): bool 32 | { 33 | return (bool) $this->evaluate($this->showDetails); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Jobs/EpisodeFindAndReplace.php: -------------------------------------------------------------------------------- 1 | info('StreamingStarted event fired for playlist: ' . $event->uuid); 18 | 19 | // Additional logic can be added here if needed... 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Listeners/StreamingStopListener.php: -------------------------------------------------------------------------------- 1 | info('StreamingStopped event fired for playlist: ' . $event->playlistId); 18 | 19 | // Additional logic can be added here if needed... 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Livewire/ChannelStreamStats.php: -------------------------------------------------------------------------------- 1 | streamStats = $record->stream_stats; 15 | } 16 | 17 | public function placeholder() 18 | { 19 | return <<<'HTML' 20 |
21 | 22 |

Fetching stream stats, hold tight...

23 |
24 | HTML; 25 | } 26 | 27 | public function render() 28 | { 29 | return view('livewire.channel-stream-stats'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Livewire/MediaFlowProxyUrl.php: -------------------------------------------------------------------------------- 1 | 'integer', 15 | 'user_id' => 'integer', 16 | 'channel_id' => 'integer', 17 | 'channel_failover_id' => 'integer', 18 | 'metadata' => 'array', 19 | ]; 20 | 21 | public function user(): BelongsTo 22 | { 23 | return $this->belongsTo(User::class); 24 | } 25 | 26 | public function channel(): BelongsTo 27 | { 28 | return $this->belongsTo(Channel::class); 29 | } 30 | 31 | public function channelFailover(): BelongsTo 32 | { 33 | return $this->belongsTo(Channel::class, 'channel_failover_id'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Models/Job.php: -------------------------------------------------------------------------------- 1 | 'array', 20 | 'variables' => 'array', 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /app/Models/PersonalAccessToken.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, 'tokenable_id') 13 | ->where('tokenable_type', User::class); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/Models/PostProcessLog.php: -------------------------------------------------------------------------------- 1 | belongsTo(PostProcess::class); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/Models/SourceGroup.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 14 | } 15 | 16 | public function playlist() 17 | { 18 | return $this->belongsTo(Playlist::class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Providers/LogoServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton('logo.service', function () { 16 | return new LogoService(); 17 | }); 18 | } 19 | 20 | /** 21 | * Bootstrap services. 22 | */ 23 | public function boot(): void 24 | { 25 | // 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Rules/Cron.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | [ 16 | \Wnx\LaravelBackupRestore\HealthChecks\Checks\DatabaseHasTables::class, 17 | ], 18 | ]; 19 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['*'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/xtream.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'series' => env('XTREAM_SERIES_FOLDER', 'Series'), 8 | 'movies' => env('XTREAM_MOVIE_FOLDER', 'Movies'), 9 | ], 10 | 11 | // Lang stripping 12 | 'lang_strip' => [ 13 | 'EN', 14 | 'FR', 15 | 'ES', 16 | 'DE', 17 | 'IT', 18 | 'PT', 19 | 'NL', 20 | 'RU', 21 | 'AR', 22 | 'TR', 23 | 'PL', 24 | 'JP', 25 | 'CN', 26 | ], 27 | 28 | // base strm output path (inside storage/app/private/[output_path]/[playlist_id]) 29 | 'output_path' => env('XTREAM_STRM_FOLDER', 'strm'), 30 | ]; 31 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/CategoryFactory.php: -------------------------------------------------------------------------------- 1 | fake()->name(), 27 | 'name_internal' => fake()->word(), 28 | 'source_category_id' => fake()->randomNumber(), 29 | 'user_id' => User::factory(), 30 | 'playlist_id' => Playlist::factory(), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/factories/ChannelFailoverFactory.php: -------------------------------------------------------------------------------- 1 | User::factory(), 27 | 'channel_id' => Channel::factory(), 28 | 'channel_failover_id' => Channel::factory()->create()->failover_id, 29 | 'sort' => fake()->randomNumber(), 30 | 'metadata' => '{}', 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/factories/CustomPlaylistFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 26 | 'uuid' => $this->faker->uuid(), 27 | 'user_id' => User::factory(), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/factories/EpgChannelFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 26 | 'display_name' => $this->faker->word(), 27 | 'lang' => $this->faker->word(), 28 | 'channel_id' => $this->faker->word(), 29 | 'epg_id' => Epg::factory(), 30 | 'programmes' => $this->faker->text(), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/factories/EpgFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 26 | 'url' => $this->faker->url(), 27 | 'user_id' => User::factory(), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/factories/GroupFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 27 | 'user_id' => User::factory(), 28 | 'playlist_id' => Playlist::factory(), 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/factories/MergedPlaylistFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name(), 26 | 'uuid' => $this->faker->uuid(), 27 | 'user_id' => User::factory(), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/factories/PlaylistAuthFactory.php: -------------------------------------------------------------------------------- 1 | fake()->name(), 26 | 'enabled' => fake()->boolean(), 27 | 'user_id' => User::factory(), 28 | 'username' => fake()->userName(), 29 | 'password' => fake()->password(), 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/factories/PostProcessFactory.php: -------------------------------------------------------------------------------- 1 | fake()->name(), 26 | 'enabled' => fake()->boolean(), 27 | 'user_id' => User::factory(), 28 | 'event' => fake()->word(), 29 | 'metadata' => '{}', 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2022_12_14_083707_create_settings_table.php: -------------------------------------------------------------------------------- 1 | id(); 13 | 14 | $table->string('group'); 15 | $table->string('name'); 16 | $table->boolean('locked')->default(false); 17 | $table->json('payload'); 18 | 19 | $table->timestamps(); 20 | 21 | $table->unique(['group', 'name']); 22 | }); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /database/migrations/2024_12_17_203030_create_admin_user.php: -------------------------------------------------------------------------------- 1 | where('name', 'admin')->first(); 16 | if (!$user) { 17 | User::query()->create([ 18 | 'name' => 'admin', 19 | 'email' => 'admin@test.com', 20 | 'password' => bcrypt('admin') 21 | ]); 22 | } 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void {} 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2024_12_18_233316_create_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 16 | $table->string('type'); 17 | $table->morphs('notifiable'); 18 | $table->text('data'); 19 | $table->timestamp('read_at')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | */ 27 | public function down(): void 28 | { 29 | Schema::dropIfExists('notifications'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /database/migrations/2024_12_19_034848_create_failed_import_rows_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->json('data'); 17 | $table->foreignId('import_id')->constrained()->cascadeOnDelete(); 18 | $table->text('validation_error')->nullable(); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::dropIfExists('failed_import_rows'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_01_06_161153_add_import_batch_no_to_channels.php: -------------------------------------------------------------------------------- 1 | string('import_batch_no')->nullable()->after('country'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_19_220431_add_import_batch_no_to_groups.php: -------------------------------------------------------------------------------- 1 | string('import_batch_no')->nullable()->after('playlist_id'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('groups', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_170519_add_synced_column_to_epgs.php: -------------------------------------------------------------------------------- 1 | string('import_batch_no')->nullable()->after('url'); 16 | $table->dateTime('synced')->nullable()->after('user_id'); 17 | 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('epgs', function (Blueprint $table) { 27 | // 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_170900_add_import_batch_to_epg_channels.php: -------------------------------------------------------------------------------- 1 | string('import_batch_no')->nullable()->after('programmes'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_171508_add_status_to_epg.php: -------------------------------------------------------------------------------- 1 | enum('status', ['pending', 'processing', 'completed', 'failed']) 18 | ->default('pending') 19 | ->after('url'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::table('epgs', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_171725_add_errors_columnd_to_epg.php: -------------------------------------------------------------------------------- 1 | longText('errors')->nullable()->after('synced'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_173519_add_uploads_column_to_epg.php: -------------------------------------------------------------------------------- 1 | mediumText('uploads')->nullable()->after('url'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_174319_add_user_id_to_epg_channels.php: -------------------------------------------------------------------------------- 1 | foreignId('user_id')->after('epg_id')->constrained()->cascadeOnDelete()->cascadeOnUpdate(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_21_224113_add_icon_to_epg_channels.php: -------------------------------------------------------------------------------- 1 | string('icon')->nullable()->after('lang'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_22_200738_remove_programme_column_from_epg_channels.php: -------------------------------------------------------------------------------- 1 | dropColumn('programmes'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_23_204711_add_sync_time_to_playlists.php: -------------------------------------------------------------------------------- 1 | float('sync_time')->after('synced')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_28_225053_add_sync_time_to_epgs.php: -------------------------------------------------------------------------------- 1 | float('sync_time')->after('synced')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_01_28_225829_add_index_to_epg_channels.php: -------------------------------------------------------------------------------- 1 | unique($this->uniqueColumns); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('epg_channels', function (Blueprint $table) { 27 | $table->dropUnique($this->uniqueColumns); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_01_29_190403_add_epg_channel_id_column_to_channels.php: -------------------------------------------------------------------------------- 1 | foreignId('epg_channel_id')->nullable()->after('stream_id')->constrained()->nullOnDelete()->nullOnUpdate(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | $table->dropForeign(['epg_channel_id']); 26 | $table->dropColumn('epg_channel_id'); 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_02_01_151321_drop_epg_programmes_table.php: -------------------------------------------------------------------------------- 1 | uuid('uuid')->after('name')->unique()->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_06_161829_add_auto_sync_toggle_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('auto_sync')->default(true)->after('channels'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_06_161839_add_auto_sync_toggle_to_epgs.php: -------------------------------------------------------------------------------- 1 | boolean('auto_sync')->default(true)->after('user_id'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_09_165446_add_avatar_url_to_users.php: -------------------------------------------------------------------------------- 1 | string('avatar_url')->nullable()->after('email'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('users', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_09_215228_add_progress_column_to_playlists.php: -------------------------------------------------------------------------------- 1 | float('progress')->default(100)->after('status'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_09_215233_add_progress_column_to_playlists.php: -------------------------------------------------------------------------------- 1 | float('progress')->default(100)->after('status'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_13_171533_add_processing_flag_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('processing')->after('status')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_13_171537_add_processing_flag_to_epgs.php: -------------------------------------------------------------------------------- 1 | boolean('processing')->after('status')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_13_224233_add_sync_interval_to_playlists.php: -------------------------------------------------------------------------------- 1 | string('sync_interval')->default('24hr')->after('synced'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_13_224236_add_sync_interval_to_epgs.php: -------------------------------------------------------------------------------- 1 | string('sync_interval')->default('24hr')->after('synced'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_14_214426_add_playlist_import_prefs_column_to_playlists.php: -------------------------------------------------------------------------------- 1 | json('import_prefs')->after('sync_interval')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_14_222330_add_groups_column_to_playlists.php: -------------------------------------------------------------------------------- 1 | longText('groups')->after('import_prefs')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_15_150422_reset_playlist_interval_on_playlists.php: -------------------------------------------------------------------------------- 1 | update(['sync_interval' => '24 hours']); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | // 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /database/migrations/2025_02_15_150833_reset_playlist_interval_on_epgs.php: -------------------------------------------------------------------------------- 1 | update(['sync_interval' => '24 hours']); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | // 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /database/migrations/2025_02_16_184838_add_counts_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | integer('channel_count')->after('sync_time')->default(0); 16 | $table->integer('mapped_count')->after('channel_count')->default(0); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('epg_maps', function (Blueprint $table) { 26 | // 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_02_16_193535_add_override_column_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | boolean('override')->after('mapped_count')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_maps', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_18_173424_add_enable_toggle_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('enable_channels')->after('auto_sync') 16 | ->default(false); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('playlists', function (Blueprint $table) { 26 | // 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_02_19_152211_add_is_custom_column_to_groups.php: -------------------------------------------------------------------------------- 1 | boolean('custom')->after('name_internal')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('groups', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_21_155914_drop_queue_monitors_table.php: -------------------------------------------------------------------------------- 1 | mediumText('uploads')->nullable()->after('url'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_02_26_200928_add_xtream_columns_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('xtream')->after('uploads')->default(false); 16 | $table->json('xtream_config')->after('xtream')->nullable(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('playlists', function (Blueprint $table) { 26 | // 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_02_27_221833_add_user_agent_ssl_prefs_to_epgs.php: -------------------------------------------------------------------------------- 1 | text('user_agent') 16 | ->after('sync_interval') 17 | ->nullable(); 18 | 19 | $table->boolean('disable_ssl_verification') 20 | ->default(false) 21 | ->after('user_agent'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::table('epgs', function (Blueprint $table) { 31 | // 32 | }); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2025_02_27_224518_add_default_user_agent_to_playlists_and_epgs.php: -------------------------------------------------------------------------------- 1 | update([ 14 | 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', 15 | ]); 16 | DB::table('epgs')->update([ 17 | 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', 18 | ]); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | // 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_04_140444_add_editable_attributes_to_channels.php: -------------------------------------------------------------------------------- 1 | string('title_custom')->nullable()->after('title'); 16 | $table->string('name_custom')->nullable()->after('name'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('channels', function (Blueprint $table) { 26 | // 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_03_04_145644_add_icon_preference_toggle_to_channels.php: -------------------------------------------------------------------------------- 1 | string('logo_type')->default('channel')->after('logo'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_05_142409_add_recurring_toggle_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | boolean('recurring')->after('override')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_maps', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_05_145318_add_playlist_id_column_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | foreignId('playlist_id')->nullable()->after('epg_id')->constrained()->nullOnDelete(); 15 | }); 16 | } 17 | 18 | /** 19 | * Reverse the migrations. 20 | */ 21 | public function down(): void 22 | { 23 | Schema::table('epg_maps', function (Blueprint $table) { 24 | // 25 | }); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /database/migrations/2025_03_05_165633_add_mapped_at_column_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | dateTime('mapped_at')->nullable()->after('progress'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_maps', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_06_155450_add_custom_id_and_url_to_channels.php: -------------------------------------------------------------------------------- 1 | mediumText('url_custom')->nullable()->after('url'); 16 | $table->string('stream_id_custom')->nullable()->after('stream_id'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('channels', function (Blueprint $table) { 26 | // 27 | }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2025_03_07_153936_add_sort_column_to_channels.php: -------------------------------------------------------------------------------- 1 | decimal('sort', 12, 4) 16 | ->after('group_id') 17 | ->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('channels', function (Blueprint $table) { 27 | // 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_03_09_161832_add_xtream_status_to_playlists.php: -------------------------------------------------------------------------------- 1 | json('xtream_status')->after('xtream_config')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_10_205435_add_extvlcopt_column_to_channels.php: -------------------------------------------------------------------------------- 1 | json('extvlcopt')->nullable()->after('stream_id'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_18_132548_add_auto_sort_order_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('auto_sort')->after('auto_sync')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_19_161419_add_enable_proxy_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('enable_proxy')->default(false)->after('enable_channels'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_19_161433_add_enable_proxy_to_custom_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('enable_proxy')->default(false)->after('channel_start'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('custom_playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_19_161447_add_enable_proxy_to_merged_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('enable_proxy')->default(false)->after('channel_start'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('merged_playlists', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_20_174644_add_kodi_drop_column_to_channels.php: -------------------------------------------------------------------------------- 1 | json('kodidrop')->nullable()->after('extvlcopt'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_03_25_204141_add_preferred_local_to_epg.php: -------------------------------------------------------------------------------- 1 | string('preferred_local')->nullable()->after('name'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_04_24_205934_add_new_column_to_channels.php: -------------------------------------------------------------------------------- 1 | boolean('new')->default(false)->after('enabled'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_04_24_205937_add_new_column_to_groups.php: -------------------------------------------------------------------------------- 1 | boolean('new')->default(false)->after('custom'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('groups', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_04_24_214454_add_stats_meta_to_playlist_sync_status.php: -------------------------------------------------------------------------------- 1 | json('sync_stats')->nullable()->after('playlist_id'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlist_sync_statuses', function (Blueprint $table) { 25 | $table->dropColumn('sync_stats'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_04_26_124521_add_dummy_epg_category_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('dummy_epg_category') 16 | ->default(false) 17 | ->after('dummy_epg_length'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('playlists', function (Blueprint $table) { 27 | $table->dropColumn('dummy_epg_category'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_05_05_180136_add_catchup_to_channels.php: -------------------------------------------------------------------------------- 1 | string('catchup')->nullable()->after('shift'); 16 | $table->string('catchup_source')->nullable()->after('catchup'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('channels', function (Blueprint $table) { 26 | $table->dropColumn('catchup'); 27 | $table->dropColumn('catchup_source'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_05_07_202251_remove_batch_job_tables.php: -------------------------------------------------------------------------------- 1 | truncate(); 17 | DB::table('failed_jobs')->truncate(); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | // 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /database/migrations/2025_05_09_220958_update_epg_channel_title_column.php: -------------------------------------------------------------------------------- 1 | text('display_name')->nullable()->change(); 16 | $table->text('icon')->nullable()->change(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | // 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /database/migrations/2025_05_21_185917_add_series_progress_to_playlists.php: -------------------------------------------------------------------------------- 1 | float('series_progress') 16 | ->default(100) 17 | ->after('progress'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('playlists', function (Blueprint $table) { 27 | // 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_05_21_192755_add_source_series_id_to_series.php: -------------------------------------------------------------------------------- 1 | unsignedInteger('source_series_id') 16 | ->nullable() 17 | ->after('source_category_id'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('series', function (Blueprint $table) { 27 | // 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_05_21_213847_update_date_time_column.php: -------------------------------------------------------------------------------- 1 | string('added')->nullable()->change(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | // 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /database/migrations/2025_05_22_114818_add_sort_order_to_series.php: -------------------------------------------------------------------------------- 1 | decimal('sort', 12, 4) 16 | ->after('category_id') 17 | ->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('series', function (Blueprint $table) { 27 | // 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_05_22_201214_add_sync_location_to_series.php: -------------------------------------------------------------------------------- 1 | jsonb('sync_settings')->nullable()->after('enabled'); 16 | $table->string('sync_location')->nullable()->after('sync_settings'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('series', function (Blueprint $table) { 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_05_30_152649_add_available_streams_to_playlists.php: -------------------------------------------------------------------------------- 1 | unsignedInteger('available_streams') 16 | ->default(0) 17 | ->after('streams'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('playlists', function (Blueprint $table) { 27 | $table->dropColumn('available_streams'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_06_01_020531_add_sort_order_to_groups_table.php: -------------------------------------------------------------------------------- 1 | decimal('sort_order', 12, 4) 16 | ->after('playlist_id') 17 | ->default(9999); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('groups', function (Blueprint $table) { 27 | $table->dropColumn('sort_order'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_06_09_133035_add_addtl_display_names_to_epg_channels.php: -------------------------------------------------------------------------------- 1 | jsonb('additional_display_names') 16 | ->after('display_name') 17 | ->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('epg_channels', function (Blueprint $table) { 27 | $table->dropColumn('additional_display_names'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_06_10_212659_add_additional_attributes_to_channels.php: -------------------------------------------------------------------------------- 1 | string('station_id') 17 | ->after('channel') 18 | ->nullable(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::table('channels', function (Blueprint $table) { 28 | $table->dropColumn('station_id'); 29 | }); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /database/migrations/2025_06_11_150943_add_available_streams_to_custom_playlists.php: -------------------------------------------------------------------------------- 1 | unsignedInteger('available_streams') 16 | ->default(0) 17 | ->after('streams'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('custom_playlists', function (Blueprint $table) { 27 | $table->dropColumn('available_streams'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_06_11_190844_add_prefix_suffix_ignore_tags_to_epg_maps.php: -------------------------------------------------------------------------------- 1 | jsonb('settings')->after('recurring')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epg_maps', function (Blueprint $table) { 25 | $table->dropColumn('settings'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_11_205559_update_channel_name_to_allow_null_on_channels.php: -------------------------------------------------------------------------------- 1 | string('name')->nullable()->change(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_12_165152_add_tvg_shift_to_channels.php: -------------------------------------------------------------------------------- 1 | string('tvg_shift')->nullable()->after('catchup_source'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | $table->dropColumn('tvg_shift'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_12_200107_update_shift_column_on_channels.php: -------------------------------------------------------------------------------- 1 | unsignedInteger('shift')->nullable()->default(0)->change(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | $table->unsignedInteger('shift')->default(0)->change(); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_13_135855_add_vod_column_on_channels.php: -------------------------------------------------------------------------------- 1 | boolean('is_vod')->default(false)->after('is_custom'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | $table->dropColumn('is_vod'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_17_222347_add_error_message_to_shared_streams_table.php: -------------------------------------------------------------------------------- 1 | text('error_message')->nullable()->after('status'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('shared_streams', function (Blueprint $table) { 25 | $table->dropColumn('error_message'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_24_145609_add_info_meta_column_to_episodes.php: -------------------------------------------------------------------------------- 1 | jsonb('info')->nullable()->after('container_extension'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('episodes', function (Blueprint $table) { 25 | $table->dropColumn('info'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_06_30_161208_update_source_id_column_on_channels.php: -------------------------------------------------------------------------------- 1 | text('source_id')->nullable()->change(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | // 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_07_14_153343_change_playlist_uuid_column_type.php: -------------------------------------------------------------------------------- 1 | string('uuid', 36)->unique()->change(); // Ensure the column 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | $table->uuid('uuid')->unique()->change(); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_07_21_082745_add_backup_before_sync_column_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('backup_before_sync')->default(false)->after('sync_interval'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | $table->dropColumn('backup_before_sync'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_07_25_082930_add_is_cached_column_to_epgs.php: -------------------------------------------------------------------------------- 1 | boolean('is_cached')->after('user_id')->default(false); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | $table->dropColumn('is_cached'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_08_03_172048_add_conditions_to_post_processes.php: -------------------------------------------------------------------------------- 1 | json('conditions') 16 | ->nullable() 17 | ->after('metadata'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::table('post_processes', function (Blueprint $table) { 27 | $table->dropColumn('conditions'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_08_06_072847_add_cache_meta_column_to_epgs.php: -------------------------------------------------------------------------------- 1 | json('cache_meta')->after('is_cached')->nullable(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | $table->dropColumn('cache_meta'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_08_08_070415_add_sync_toggle_to_playlists.php: -------------------------------------------------------------------------------- 1 | boolean('sync_logs_enabled')->default(true)->after('sync_enabled'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('playlists', function (Blueprint $table) { 25 | $table->dropColumn('sync_logs_enabled'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_08_12_154638_add_cache_progress_to_epg_table.php: -------------------------------------------------------------------------------- 1 | float('cache_progress')->default(100); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | $table->dropColumn('cache_progress'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_08_22_210003_add_mfa_column_to_users.php: -------------------------------------------------------------------------------- 1 | text('app_authentication_secret')->nullable(); 16 | $table->text('app_authentication_recovery_codes')->nullable(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('users', function (Blueprint $table) { 26 | $table->dropColumn('app_authentication_secret'); 27 | $table->dropColumn('app_authentication_recovery_codes'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_09_02_200144_add_enable_toggle_column_to_episodes.php: -------------------------------------------------------------------------------- 1 | boolean('enabled')->default(true); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('episodes', function (Blueprint $table) { 25 | $table->dropColumn('enabled'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_09_04_064050_add_stream_file_fields_to_channels.php: -------------------------------------------------------------------------------- 1 | jsonb('sync_settings')->nullable()->after('enabled'); 16 | $table->string('sync_location')->nullable()->after('sync_settings'); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | */ 23 | public function down(): void 24 | { 25 | Schema::table('channels', function (Blueprint $table) { 26 | $table->dropColumn('sync_settings'); 27 | $table->dropColumn('sync_location'); 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2025_09_04_081310_add_last_metadata_fetch_column_to_channels.php: -------------------------------------------------------------------------------- 1 | dateTime('last_metadata_fetch')->nullable()->after('updated_at'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('channels', function (Blueprint $table) { 25 | $table->dropColumn('last_metadata_fetch'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_09_18_151019_add_sd_metadata_column_to_epgs.php: -------------------------------------------------------------------------------- 1 | jsonb('sd_metadata')->nullable()->after('sd_days_to_import'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('epgs', function (Blueprint $table) { 25 | $table->dropColumn('sd_metadata'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_09_22_081912_change_series_name_column.php: -------------------------------------------------------------------------------- 1 | text('name')->change(); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('series', function (Blueprint $table) { 25 | $table->string('name', 255)->change(); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/migrations/2025_10_05_085522_add_metadata_to_series_seasons.php: -------------------------------------------------------------------------------- 1 | jsonb('metadata')->nullable()->after('cover_big'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('seasons', function (Blueprint $table) { 25 | $table->dropColumn('metadata'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | User::factory()->create([ 19 | 'name' => 'Test User', 20 | 'email' => 'test@example.com', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/settings/2025_05_01_160604_add_mfp_user_agent_fields.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.mediaflow_proxy_playlist_user_agent')) { 10 | $this->migrator->add('general.mediaflow_proxy_playlist_user_agent', true); 11 | } 12 | if (!$this->migrator->exists('general.mediaflow_proxy_user_agent')) { 13 | $this->migrator->add('general.mediaflow_proxy_user_agent', null); 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /database/settings/2025_05_16_070340_add_ffmpeg_codec_fields.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.ffmpeg_codec_video')) { 10 | $this->migrator->add('general.ffmpeg_codec_video', config('proxy.ffmpeg_codec_video') ?? null); 11 | } 12 | if (!$this->migrator->exists('general.ffmpeg_codec_audio')) { 13 | $this->migrator->add('general.ffmpeg_codec_audio', config('proxy.ffmpeg_codec_audio') ?? null); 14 | } 15 | if (!$this->migrator->exists('general.ffmpeg_codec_subtitles')) { 16 | $this->migrator->add('general.ffmpeg_codec_subtitles', config('proxy.ffmpeg_codec_subtitles') ?? null); 17 | } 18 | } 19 | }; -------------------------------------------------------------------------------- /database/settings/2025_05_19_130544_add_ffmpeg_path_select_option.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.ffmpeg_path')) { 10 | $this->migrator->add('general.ffmpeg_path', config('proxy.ffmpeg_path') ?? null); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /database/settings/2025_05_29_180906_add_ffmpeg_custom_command_template_settings.php: -------------------------------------------------------------------------------- 1 | migrator->inGroup('general', function (SettingsBlueprint $blueprint): void { 11 | $blueprint->add('ffmpeg_custom_command_template', null); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | $this->migrator->inGroup('general', function (SettingsBlueprint $blueprint): void { 18 | $blueprint->delete('ffmpeg_custom_command_template'); 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/settings/2025_07_22_000000_add_ffprobe_path_select_option.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.ffprobe_path')) { 10 | $this->migrator->add('general.ffprobe_path', config('proxy.ffprobe_path') ?? 'jellyfin-ffprobe'); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /database/settings/2025_08_16_110411_add_video_player_proxy_options.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.force_video_player_proxy')) { 10 | $this->migrator->add('general.force_video_player_proxy', false); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /database/settings/2025_08_20_100536_add_sync_abort_options.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.invalidate_import')) { 10 | $this->migrator->add('general.invalidate_import', false); 11 | } 12 | if (!$this->migrator->exists('general.invalidate_import_threshold')) { 13 | $this->migrator->add('general.invalidate_import_threshold', 100); 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /database/settings/2025_09_05_082037_add_backup_schedule_to_settings.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.auto_backup_database')) { 10 | $this->migrator->add('general.auto_backup_database', false); 11 | } 12 | if (!$this->migrator->exists('general.auto_backup_database_schedule')) { 13 | $this->migrator->add('general.auto_backup_database_schedule', '0 3 * * *'); 14 | } 15 | if (!$this->migrator->exists('general.auto_backup_database_max_backups')) { 16 | $this->migrator->add('general.auto_backup_database_max_backups', 5); 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /database/settings/2025_09_17_093017_add_stream_file_sync_include_category.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.stream_file_sync_include_category')) { 10 | $this->migrator->add('general.stream_file_sync_include_category', false); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /database/settings/2025_09_30_160741_add_wan_output_toggle.php: -------------------------------------------------------------------------------- 1 | migrator->exists('general.output_wan_address')) { 10 | $this->migrator->add('general.output_wan_address', false); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /docker/8.4/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | upload_max_filesize = 1024M 3 | post_max_size = 1024M 4 | max_execution_time = 1800 5 | max_input_time = 900 6 | memory_limit = 2048M 7 | variables_order = EGPCS 8 | opcache.jit=1235 9 | opcache.jit_buffer_size=64M 10 | # output_buffering = Off 11 | # implicit_flush = On -------------------------------------------------------------------------------- /docker/8.4/redis.conf: -------------------------------------------------------------------------------- 1 | bind 0.0.0.0 2 | port ${REDIS_SERVER_PORT} 3 | timeout 0 4 | 5 | # Suppress memory overcommit warning by disabling background operations 6 | stop-writes-on-bgsave-error no 7 | # Disable all background saves to avoid memory overcommit issues 8 | save "" 9 | # Disable RDB persistence completely 10 | rdbcompression no 11 | rdbchecksum no 12 | # Set log level to notice to reduce warnings 13 | loglevel notice 14 | # Disable syslog to avoid system-level warnings 15 | syslog-enabled no 16 | # Disable COW warning for ARM64 architecture since we're not using COW 17 | ignore-warnings ARM64-COW-BUG -------------------------------------------------------------------------------- /docker/mariadb/create-testing-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /usr/bin/mariadb --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL 4 | CREATE DATABASE IF NOT EXISTS testing; 5 | GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; 6 | EOSQL 7 | -------------------------------------------------------------------------------- /docker/mysql/create-testing-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL 4 | CREATE DATABASE IF NOT EXISTS testing; 5 | GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; 6 | EOSQL 7 | -------------------------------------------------------------------------------- /docker/pgsql/create-testing-database.sql: -------------------------------------------------------------------------------- 1 | SELECT 'CREATE DATABASE testing' 2 | WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'testing')\gexec 3 | -------------------------------------------------------------------------------- /draft.yml: -------------------------------------------------------------------------------- 1 | models: 2 | ChannelFailover: 3 | user_id: id foreign 4 | channel_id: id foreign 5 | channel_failover_id: id foreign:channels 6 | sort: unsigned integer 7 | metadata: jsonb 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "build": "vite build", 6 | "dev": "vite" 7 | }, 8 | "devDependencies": { 9 | "@tailwindcss/forms": "^0.5.10", 10 | "@tailwindcss/postcss": "^4.1.12", 11 | "@tailwindcss/typography": "^0.5.16", 12 | "axios": ">=1.8.2", 13 | "chokidar": "^4.0.3", 14 | "concurrently": "^9.0.1", 15 | "laravel-echo": "^1.17.1", 16 | "laravel-vite-plugin": "^1.0", 17 | "postcss": "^8.4.47", 18 | "pusher-js": "^8.4.0-rc2", 19 | "sass-embedded": "^1.85.0", 20 | "tailwindcss": "^4.1.12", 21 | "vite": ">=6.3.6" 22 | }, 23 | "dependencies": { 24 | "easyqrcodejs": "^4.6.2", 25 | "hls.js": "^1.6.7", 26 | "mpegts.js": "^1.8.0", 27 | "video.js": "^8.23.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/episode-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/episode-placeholder.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/favicon.png -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-ASVAGXXE.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-cyrillic-ext-wght-normal-XKHXBTUO.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-EWLSKVKN.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-cyrillic-wght-normal-R5CMSONN.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-7GGTF7EK.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-greek-ext-wght-normal-ZEVLMORV.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-greek-wght-normal-AXVTPQD5.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-greek-wght-normal-N43DBLU2.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-5SRY4DMZ.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-GZCIV3NH.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-ext-wght-normal-HA22NDSG.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-wght-normal-NRMW37G5.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-wght-normal-O25CN4JL.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-latin-wght-normal-OPIJAQLS.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-CE5GGD3W.woff2 -------------------------------------------------------------------------------- /public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/fonts/filament/filament/inter/inter-vietnamese-wght-normal-TWG5UU7E.woff2 -------------------------------------------------------------------------------- /public/images/crypto-icons/litecoin.svg: -------------------------------------------------------------------------------- 1 | 2 | litecoin-ltc-logo -------------------------------------------------------------------------------- /public/images/crypto-icons/ripple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/crypto-icons/tether.svg: -------------------------------------------------------------------------------- 1 | 2 | tether-usdt-logo -------------------------------------------------------------------------------- /public/images/xtream-codes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/images/xtream-codes.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/js/devonab/filament-easy-footer/filament-easy-footer-scripts.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/js/devonab/filament-easy-footer/filament-easy-footer-scripts.js -------------------------------------------------------------------------------- /public/js/filament/forms/components/tags-input.js: -------------------------------------------------------------------------------- 1 | function s({state:n,splitKeys:a}){return{newTag:"",state:n,createTag(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag(t){this.state=this.state.filter(e=>e!==t)},reorderTags(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{"x-on:blur":"createTag()","x-model":"newTag","x-on:keydown"(t){["Enter",...a].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},"x-on:paste"(){this.$nextTick(()=>{if(a.length===0){this.createTag();return}let t=a.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{s as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/forms/components/textarea.js: -------------------------------------------------------------------------------- 1 | function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/schemas/components/actions.js: -------------------------------------------------------------------------------- 1 | var o=()=>({isSticky:!1,init(){this.evaluatePageScrollPosition()},evaluatePageScrollPosition(){let i=this.$el.getBoundingClientRect(),t=i.top>window.innerHeight,e=i.topwindow.innerHeight;this.isSticky=t||e}});export{o as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/schemas/components/tabs.js: -------------------------------------------------------------------------------- 1 | function u({activeTab:a,isTabPersistedInQueryString:e,livewireId:h,tab:o,tabQueryStringKey:s}){return{tab:o,init(){let t=this.getTabs(),i=new URLSearchParams(window.location.search);e&&i.has(s)&&t.includes(i.get(s))&&(this.tab=i.get(s)),this.$watch("tab",()=>this.updateQueryString()),(!this.tab||!t.includes(this.tab))&&(this.tab=t[a-1]),Livewire.hook("commit",({component:r,commit:f,succeed:c,fail:l,respond:b})=>{c(({snapshot:d,effect:m})=>{this.$nextTick(()=>{if(r.id!==h)return;let n=this.getTabs();n.includes(this.tab)||(this.tab=n[a-1]??this.tab)})})})},getTabs(){return this.$refs.tabsData?JSON.parse(this.$refs.tabsData.value):[]},updateQueryString(){if(!e)return;let t=new URL(window.location.href);t.searchParams.set(s,this.tab),history.replaceState(null,document.title,t.toString())}}}export{u as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/columns/checkbox.js: -------------------------------------------------------------------------------- 1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default}; 2 | -------------------------------------------------------------------------------- /public/js/filament/tables/components/columns/toggle.js: -------------------------------------------------------------------------------- 1 | function o({name:i,recordKey:s,state:a}){return{error:void 0,isLoading:!1,state:a,init(){Livewire.hook("commit",({component:e,commit:r,succeed:n,fail:h,respond:u})=>{n(({snapshot:f,effect:d})=>{this.$nextTick(()=>{if(this.isLoading||e.id!==this.$root.closest("[wire\\:id]")?.attributes["wire:id"].value)return;let t=this.getServerState();t===void 0||Alpine.raw(this.state)===t||(this.state=t)})})}),this.$watch("state",async()=>{let e=this.getServerState();if(e===void 0||Alpine.raw(this.state)===e)return;this.isLoading=!0;let r=await this.$wire.updateTableColumnState(i,s,this.state);this.error=r?.error??void 0,!this.error&&this.$refs.serverState&&(this.$refs.serverState.value=this.state?"1":"0"),this.isLoading=!1})},getServerState(){if(this.$refs.serverState)return[1,"1"].includes(this.$refs.serverState.value)}}}export{o as default}; 2 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/logo.png -------------------------------------------------------------------------------- /public/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/public/placeholder.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./vendor/bin/sail build --no-cache && ./vendor/bin/sail up --remove-orphans 4 | -------------------------------------------------------------------------------- /refresh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Refresh the data 4 | php artisan blueprint:erase && php artisan blueprint:build && php artisan migrate:fresh 5 | 6 | # Notify the user 7 | echo "=====================" 8 | echo " Data refreshed 🎉 " 9 | echo "=====================" -------------------------------------------------------------------------------- /resources/css/components/footer.css: -------------------------------------------------------------------------------- 1 | 2 | .fi-footer > span { 3 | @apply text-center; 4 | } 5 | 6 | .fi-footer.fi-sidebar > span:not(:first-child) { 7 | @apply pl-0 pr-0 border-0; 8 | } 9 | 10 | .fi-footer > span:first-child { 11 | @apply pr-4 border-r border-gray-200 dark:border-gray-700; 12 | } 13 | 14 | .fi-footer:not(.fi-sidebar) > span:not(:first-child):not(:last-child) { 15 | @apply border-r border-gray-200 dark:border-gray-700; 16 | } 17 | 18 | .fi-footer > span:not(:first-child):not(:last-child) { 19 | @apply pl-0 pr-4; 20 | } 21 | 22 | .fi-footer > span:last-child { 23 | @apply !border-0 !pl-0; 24 | } 25 | 26 | .fi-footer.fi-sidebar > span { 27 | @apply px-2; 28 | } 29 | 30 | .fi-footer.gap-4 > span { 31 | @apply px-4; 32 | } 33 | 34 | 35 | .fi-footer.fi-sidebar.gap-2 > span { 36 | @apply px-2; 37 | } 38 | 39 | .fi-footer.fi-sidebar.gap-2 > span:nth-child(2) 40 | { 41 | @apply pl-0; 42 | } -------------------------------------------------------------------------------- /resources/css/components/tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | 3 | .fi-input, 4 | .fi-select-input, 5 | .fi-ta-cell div { 6 | padding-top: 0px !important; 7 | padding-bottom: 0px !important; 8 | margin-top: 0px !important; 9 | margin-bottom: 0px !important; 10 | } 11 | 12 | .fi-ta-cell { 13 | padding-top: 4px !important; 14 | padding-bottom: 4px !important; 15 | } 16 | } 17 | 18 | .fi-ta-content-ctn { 19 | max-height: 70vh; 20 | 21 | .fi-ta-table { 22 | thead { 23 | position: sticky; 24 | top: 0; 25 | z-index: 1; 26 | @apply bg-gray-50 dark:bg-gray-900; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /resources/css/components/widgets.css: -------------------------------------------------------------------------------- 1 | .fi-sc-component .fi-wi-stats-overview-stat, 2 | .fi-wi-widget section { 3 | @apply h-full; 4 | } 5 | .fi-wi-widget .fi-section-content .flex { 6 | @apply flex-1; 7 | } 8 | -------------------------------------------------------------------------------- /resources/css/globals/global.scss: -------------------------------------------------------------------------------- 1 | @media (max-width: 768px) { 2 | .fi-dropdown-panel { 3 | right: auto !important; 4 | left: 0 !important; 5 | /* stick to the left edge instead of right */ 6 | max-width: 90vw !important; 7 | z-index: 9999 !important; 8 | } 9 | } -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | // Import styles (Support HMR) 2 | import '../css/app.css' 3 | 4 | // Enable websockets 5 | import './echo' 6 | 7 | // Import streaming libraries 8 | import Hls from 'hls.js' 9 | import mpegts from 'mpegts.js' 10 | 11 | // Make streaming libraries globally available 12 | window.Hls = Hls 13 | window.mpegts = mpegts 14 | 15 | // Vendor 16 | import './vendor/qrcode' 17 | import './vendor/epg-viewer' 18 | import './vendor/stream-viewer' 19 | import './vendor/multi-stream-manager' 20 | 21 | // Fix broken images 22 | document.addEventListener('error', event => { 23 | const el = event.target; 24 | if (el.tagName.toLowerCase() === 'img') { 25 | el.onerror = null; 26 | if (el.classList.contains('episode-placeholder')) { 27 | el.src = '/episode-placeholder.png'; 28 | } else { 29 | el.src = '/placeholder.png'; 30 | } 31 | } 32 | }, true); -------------------------------------------------------------------------------- /resources/js/echo.js: -------------------------------------------------------------------------------- 1 | import Echo from 'laravel-echo'; 2 | import Pusher from 'pusher-js'; 3 | const disabled = import.meta.env.VITE_WEBSOCKETS_DISABLED ?? 'false'; 4 | if (disabled === 'true') { 5 | window.Echo = null; 6 | } else { 7 | window.Pusher = Pusher; 8 | const env = import.meta.env.VITE_REVERB_ENV ?? 'production'; 9 | const scheme = import.meta.env.VITE_REVERB_SCHEME ?? 'https' 10 | let wsPort = import.meta.env.VITE_REVERB_PORT; 11 | let wssPort = scheme === 'https' ? 443 : wsPort; 12 | if (env !== 'production') { 13 | wssPort = wsPort 14 | } 15 | window.Echo = new Echo({ 16 | broadcaster: 'reverb', 17 | key: import.meta.env.VITE_REVERB_APP_KEY, 18 | wsHost: import.meta.env.VITE_REVERB_HOST, 19 | wsPort, 20 | wssPort, 21 | forceTLS: scheme === 'https', 22 | enabledTransports: ['ws', 'wss'], 23 | }); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /resources/views/components/qr-modal.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ $label ?? 'QR Code' }} 5 | 6 | 7 | 8 | 9 | {{ $title }} 10 | 11 | 12 |
13 |

14 | {{ $body }} 15 |

16 |
17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 error 5 | 6 | 7 | 8 |
9 |
10 | Logo 11 |

404

12 |

13 | Unable to find the page you are looking for. 14 |

15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /resources/views/errors/429.blade.php: -------------------------------------------------------------------------------- 1 | @extends('errors::minimal') 2 | 3 | @section('title', __('Max Streams Reached')) 4 | @section('code', '429') 5 | @section('message') 6 | {{-- Prefer the specific message from the abort() call via the exception --}} 7 | {{ $exception->getMessage() ?: __('Maximum stream limits have been reached. Please try again later.') }} 8 | @endsection 9 | 10 | @section('sub_message') 11 | The system is currently at its maximum capacity for this channel or playlist. 12 | Please wait a short while before trying again. If the problem persists, please contact support. 13 | @endsection 14 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Maintenence mode 5 | 6 | 7 | 8 |
9 |
10 | Logo 11 |

App is currently in maintenence mode, please check back shortly...

12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /resources/views/filament/pages/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/filament/pages/stats.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/views/forms/components/media-flow-proxy-url.blade.php: -------------------------------------------------------------------------------- 1 | 2 | @php($record = $getRecord()) 3 | @php($urls = \App\Facades\PlaylistFacade::getMediaFlowProxyUrls($record)) 4 | @php($m3uUrl = $urls['m3u']) 5 |
6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /resources/views/mail/post_process.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | M3U Proxy Editor Logo 4 |
5 | 6 | # Sync completed 7 | 8 | ## {{ $body }} 9 | 10 | @if (!empty($variables)) 11 | ### Variables: 12 | 13 | @foreach ($variables as $key => $value) 14 | - {{ $key }}: {{ $value }} 15 | 16 | @endforeach 17 | @endif 18 | 19 |
-------------------------------------------------------------------------------- /resources/views/modals/hardware-accel-info.blade.php: -------------------------------------------------------------------------------- 1 |
2 | To use hardware transcoding you need to map /dev/dri to the container. 3 |
4 |
5 | Docker Compose: 6 |
7 |
 8 | devices:
 9 |   - /dev/dri:/dev/dri
10 |     
11 | Docker Run: 12 |
13 | docker run --device /dev/dri:/dev/dri 14 |
15 |
16 | Confirm Access: 17 |
18 | 19 | docker exec -it m3u-editor ls -l /dev/dri 20 | 21 |
22 |
23 | If you see a list of devices like card0, renderD128, etc., then hardware acceleration is set up correctly. 24 |
25 | -------------------------------------------------------------------------------- /resources/views/tables/columns/playlist-auth-name.blade.php: -------------------------------------------------------------------------------- 1 |
2 |

3 | {{ $getRecord()->type() }} 4 |

5 |

6 | {{ $getRecord()->model->name }} 7 |

8 |
9 | -------------------------------------------------------------------------------- /resources/views/vendor/filament-breezy/components/grid-section.blade.php: -------------------------------------------------------------------------------- 1 | @props(['title','description']) 2 | 3 | 4 | 5 |

{{$title}}

6 | 7 |

8 | {{$description}} 9 |

10 |
11 | 12 | 13 | {{ $slot }} 14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /resources/views/vendor/filament-progress-column/column.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | $color = $getColor(); 3 | $barStyles = \Filament\Support\get_color_css_variables( 4 | $color, 5 | shades: [600], 6 | ); 7 | $progress = $getProgress(); 8 | $poll = $getPoll(); 9 | @endphp 10 | 11 |
17 |
18 |
19 |
20 |
21 | 22 | {{ $progress }}% 23 |
24 |
25 | -------------------------------------------------------------------------------- /resources/views/vendor/filament-spatie-backup/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/resources/views/vendor/filament-spatie-backup/.gitkeep -------------------------------------------------------------------------------- /resources/views/vendor/filament-spatie-backup/components/backup-destination-list-records.blade.php: -------------------------------------------------------------------------------- 1 |
interval() }}> 2 | {{ $this->table }} 3 |
4 | -------------------------------------------------------------------------------- /resources/views/vendor/filament-spatie-backup/components/backup-destination-status-list-records.blade.php: -------------------------------------------------------------------------------- 1 |
interval() }}> 2 | {{ $this->table }} 3 |
4 | -------------------------------------------------------------------------------- /resources/views/vendor/filament-spatie-backup/pages/backups.blade.php: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | @livewire(\App\Livewire\BackupDestinationListRecords::class) 5 |
6 |
7 |
8 | -------------------------------------------------------------------------------- /resources/views/vendor/log-viewer/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/resources/views/vendor/log-viewer/.gitkeep -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 7 | }); 8 | -------------------------------------------------------------------------------- /screenshots/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/api.png -------------------------------------------------------------------------------- /screenshots/channel-editing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/channel-editing.png -------------------------------------------------------------------------------- /screenshots/in-app-epg-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/in-app-epg-preview.png -------------------------------------------------------------------------------- /screenshots/in-app-playlist-epg-playback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/in-app-playlist-epg-playback.png -------------------------------------------------------------------------------- /screenshots/in-app-playlist-epg-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/in-app-playlist-epg-preview.png -------------------------------------------------------------------------------- /screenshots/post-processing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/post-processing.png -------------------------------------------------------------------------------- /screenshots/proxy-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/proxy-config.png -------------------------------------------------------------------------------- /screenshots/proxy-monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/proxy-monitor.png -------------------------------------------------------------------------------- /screenshots/series-mgmt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sparkison/m3u-editor/10c76d88307005cfcaf11d6c34752fc94f295a47/screenshots/series-mgmt.png -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !private/ 3 | !public/ 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /storage/app/private/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /stubs/blueprint/constructor.stub: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a new {{ type }} instance. 3 | */ 4 | public function __construct() 5 | { 6 | {{ body }} 7 | } 8 | -------------------------------------------------------------------------------- /stubs/blueprint/controller.authorize-resource.stub: -------------------------------------------------------------------------------- 1 | public function __construct() 2 | { 3 | $this->authorizeResource({{ modelClass }}::class, '{{ modelVariable }}'); 4 | } 5 | -------------------------------------------------------------------------------- /stubs/blueprint/controller.class.stub: -------------------------------------------------------------------------------- 1 | 'test-uuid'])) 11 | ->assertSee('Playlist Login') 12 | ->assertSee('Username') 13 | ->assertSee('Password'); 14 | }); 15 | 16 | it('authenticates and shows dashboard after login', function () { 17 | // You may want to seed a test playlist and auth here, or mock PlaylistFacade 18 | // For now, just check the login flow UI 19 | livewire(GuestDashboard::class) 20 | ->set('username', 'demo') 21 | ->set('password', 'demo') 22 | ->call('login'); 23 | // This test should be expanded with a real playlist and credentials 24 | }); 25 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | --------------------------------------------------------------------------------