├── .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 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/resources/views/errors/404.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 404 error
5 |
6 |
7 |
8 |
9 |
10 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------