Initial commit — YT Hub
Self-hosted personal YouTube management app. FastAPI + SQLite backend, React + Vite + Tailwind frontend. Dockerfiles and compose included for Portainer deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
135
frontend/src/api/index.js
Normal file
135
frontend/src/api/index.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import axios from "axios";
|
||||
|
||||
const api = axios.create({ baseURL: "/api" });
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
return config;
|
||||
});
|
||||
|
||||
api.interceptors.response.use(
|
||||
(res) => res,
|
||||
(err) => {
|
||||
if (err.response?.status === 401) {
|
||||
localStorage.removeItem("token");
|
||||
window.location.href = "/login";
|
||||
}
|
||||
return Promise.reject(err);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
|
||||
// Auth
|
||||
export const login = (username, password) => {
|
||||
const form = new FormData();
|
||||
form.append("username", username);
|
||||
form.append("password", password);
|
||||
return api.post("/auth/login", form);
|
||||
};
|
||||
export const register = (data) => api.post("/auth/register", data);
|
||||
export const getMe = () => api.get("/auth/me");
|
||||
|
||||
// Search
|
||||
export const search = (q, live = false) =>
|
||||
api.get("/search", { params: { q, live } });
|
||||
export const getSearchHistory = () => api.get("/search/history");
|
||||
|
||||
// Channels
|
||||
export const getChannels = () => api.get("/channels");
|
||||
export const getChannelFeed = (offset = 0) => api.get("/channels/feed", { params: { offset, limit: 24 } });
|
||||
export const getChannel = (id) => api.get(`/channels/${id}`);
|
||||
export const syncAllChannels = () => api.post("/channels/sync-all");
|
||||
export const getChannelVideos = (id) => api.get(`/channels/${id}/videos`);
|
||||
export const followChannel = (id) => api.post(`/channels/${id}/follow`);
|
||||
export const unfollowChannel = (id) => api.delete(`/channels/${id}/follow`);
|
||||
export const indexChannel = (id) => api.post(`/channels/${id}/index`);
|
||||
export const followChannelByUrl = (data) => api.post("/channels/follow-by-url", data);
|
||||
export const setChannelAutoDownload = (id, value) => api.patch(`/channels/${id}/auto-download`, { auto_download: value });
|
||||
export const markChannelsSeen = () => api.post("/channels/mark-seen");
|
||||
export const followBulk = (handles) => api.post("/channels/follow-bulk", { handles });
|
||||
export const updateChannelNotes = (id, notes) => api.patch(`/channels/${id}/notes`, { notes });
|
||||
export const muteChannel = (id) => api.post(`/channels/${id}/mute`);
|
||||
export const unmuteChannel = (id) => api.delete(`/channels/${id}/mute`);
|
||||
export const getChannelGroups = () => api.get("/channels/groups");
|
||||
export const createChannelGroup = (name) => api.post("/channels/groups", { name });
|
||||
export const deleteChannelGroup = (id) => api.delete(`/channels/groups/${id}`);
|
||||
export const renameChannelGroup = (id, name) => api.patch(`/channels/groups/${id}`, { name });
|
||||
export const addChannelToGroup = (groupId, channelId) => api.post(`/channels/groups/${groupId}/channels/${channelId}`);
|
||||
export const removeChannelFromGroup = (groupId, channelId) => api.delete(`/channels/groups/${groupId}/channels/${channelId}`);
|
||||
export const bulkChannelAction = (channel_ids, action) => api.post("/channels/bulk-action", { channel_ids, action });
|
||||
|
||||
// Videos
|
||||
export const homeFeed = (page = 0, limit = 25, mode = "ranked", duration = "") =>
|
||||
api.get("/videos/home-feed", { params: { offset: page * limit, limit, mode, ...(duration ? { duration } : {}) } });
|
||||
export const continueWatching = () => api.get("/videos/continue-watching");
|
||||
export const longVideos = () => api.get("/videos/long");
|
||||
export const surpriseMe = () => api.get("/videos/surprise");
|
||||
export const getVideo = (id) => api.get(`/videos/${id}`);
|
||||
export const getVideoByYtId = (ytId) => api.get(`/videos/by-yt/${ytId}`);
|
||||
export const updateProgress = (id, data) => api.patch(`/videos/${id}/progress`, data);
|
||||
export const toggleQueue = (id) => api.post(`/videos/${id}/queue`);
|
||||
export const getQueue = () => api.get("/videos/queue");
|
||||
export const toggleLike = (id) => api.post(`/videos/${id}/like`);
|
||||
export const getLikedVideos = () => api.get("/videos/liked");
|
||||
export const rateVideo = (id, rating) => api.post(`/videos/${id}/rate`, { rating });
|
||||
export const getRelatedVideos = (videoId, mode = "weighted") => api.get(`/videos/${videoId}/related`, { params: { mode } });
|
||||
export const getHistory = (page = 0, limit = 25, channel_id = null) =>
|
||||
api.get("/videos/history", { params: { offset: page * limit, limit, ...(channel_id ? { channel_id } : {}) } });
|
||||
export const getBookmarks = (videoId) => api.get(`/videos/${videoId}/bookmarks`);
|
||||
export const createBookmark = (videoId, data) => api.post(`/videos/${videoId}/bookmarks`, data);
|
||||
export const updateBookmark = (videoId, bookmarkId, data) => api.patch(`/videos/${videoId}/bookmarks/${bookmarkId}`, data);
|
||||
export const deleteBookmark = (videoId, bookmarkId) => api.delete(`/videos/${videoId}/bookmarks/${bookmarkId}`);
|
||||
export const importChapters = (videoId) => api.post(`/videos/${videoId}/bookmarks/import-chapters`);
|
||||
export const clearChapters = (videoId) => api.delete(`/videos/${videoId}/bookmarks/clear-chapters`);
|
||||
|
||||
// Downloads
|
||||
export const createDownload = (youtube_video_id, quality) =>
|
||||
api.post("/downloads", { youtube_video_id, ...(quality ? { quality } : {}) });
|
||||
export const getDownloads = () => api.get("/downloads");
|
||||
export const getDownload = (id) => api.get(`/downloads/${id}`);
|
||||
export const deleteDownload = (id) => api.delete(`/downloads/${id}`);
|
||||
export const deleteAllDownloads = () => api.delete("/downloads/all");
|
||||
export const restoreDownload = (id) => api.post(`/downloads/${id}/restore`);
|
||||
export const downloadChannel = (channelId) => api.post(`/downloads/channel/${channelId}`);
|
||||
export const downloadFollowing = () => api.post("/downloads/following");
|
||||
|
||||
// Export
|
||||
export const exportData = () => api.get("/export", { responseType: "blob" });
|
||||
|
||||
// Settings
|
||||
export const getSettings = () => api.get("/settings");
|
||||
export const updateSettings = (data) => api.patch("/settings", data);
|
||||
|
||||
// Discovery
|
||||
export const getDiscovery = (offset = 0, limit = 50) =>
|
||||
api.get("/discovery", { params: { offset, limit } });
|
||||
export const dismissDiscoveryVideo = (youtubeVideoId) =>
|
||||
api.post(`/discovery/videos/${youtubeVideoId}/dismiss`);
|
||||
export const getDiscoveryVideos = (offset = 0, limit = 50) =>
|
||||
api.get("/discovery/videos", { params: { offset, limit } });
|
||||
export const followDiscovery = (channelId) =>
|
||||
api.post(`/discovery/${channelId}/follow`);
|
||||
export const dismissDiscovery = (channelId) =>
|
||||
api.post(`/discovery/${channelId}/dismiss`);
|
||||
export const refreshDiscovery = () => api.post("/discovery/refresh");
|
||||
export const getCommunityShelf = () => api.get("/discovery/community");
|
||||
|
||||
// Stats
|
||||
export const getStats = () => api.get("/stats");
|
||||
|
||||
// Admin
|
||||
export const getAdminUsers = () => api.get("/admin/users");
|
||||
export const deleteAdminUser = (id) => api.delete(`/admin/users/${id}`);
|
||||
export const getAdminConfig = () => api.get("/admin/config");
|
||||
export const updateAdminConfig = (data) => api.patch("/admin/config", data);
|
||||
|
||||
// Collections
|
||||
export const getCollections = () => api.get("/collections");
|
||||
export const createCollection = (name) => api.post("/collections", { name });
|
||||
export const renameCollection = (id, name) => api.patch(`/collections/${id}`, { name });
|
||||
export const deleteCollection = (id) => api.delete(`/collections/${id}`);
|
||||
export const getCollectionVideos = (id) => api.get(`/collections/${id}/videos`);
|
||||
export const addToCollection = (collectionId, videoId) => api.post(`/collections/${collectionId}/videos`, { video_id: videoId });
|
||||
export const removeFromCollection = (collectionId, videoId) => api.delete(`/collections/${collectionId}/videos/${videoId}`);
|
||||
Reference in New Issue
Block a user