Download utils/database.js from MineRoma/tv: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/MineRoma/tv/resolve/main/utils/database.js
- Command line
-
hf download hf://spaces/MineRoma/tv/utils/database.js
-
curl -L -o database.js https://huggingface.co/spaces/MineRoma/tv/resolve/main/utils/database.js
10.6 kB
| const axios = require('axios'); | |
| const crypto = require('./crypto'); | |
| let GIST_ID = process.env.GIST_ID; | |
| const GITHUB_TOKEN = process.env.GITHUB_TOKEN; | |
| if (!GITHUB_TOKEN) { | |
| console.error('❌ GITHUB_TOKEN not set!'); | |
| } | |
| let GIST_API = GIST_ID ? `https://api.github.com/gists/${GIST_ID}` : null; | |
| // Cache | |
| let usersCache = null; | |
| let scheduleCache = null; | |
| let cacheTime = 0; | |
| const CACHE_DURATION = 60000; // 1 minute | |
| let gistInitialized = false; | |
| // Создание нового Gist с дефолтными данными | |
| async function createDefaultGist() { | |
| if (!GITHUB_TOKEN) { | |
| console.error('Cannot create Gist without GITHUB_TOKEN'); | |
| return null; | |
| } | |
| try { | |
| console.log('📝 Creating new Gist with default data...'); | |
| const defaultData = { | |
| users: [], | |
| schedule: [ | |
| { | |
| channelId: 'TV-1', | |
| channelName: 'TV-1', | |
| programs: [ | |
| { | |
| time: '08:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 1-4', | |
| file: 'nu-pogodi-1-4.mp4', | |
| description: 'Классика советской анимации' | |
| }, | |
| { | |
| time: '10:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 5-8', | |
| file: 'nu-pogodi-5-8.mp4', | |
| description: 'Продолжение приключений' | |
| }, | |
| { | |
| time: '12:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 9-12', | |
| file: 'nu-pogodi-9-12.mp4', | |
| description: 'Новые серии' | |
| }, | |
| { | |
| time: '14:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 13-16', | |
| file: 'nu-pogodi-13-16.mp4', | |
| description: 'Финальные серии' | |
| }, | |
| { | |
| time: '16:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 1-4 (повтор)', | |
| file: 'nu-pogodi-1-4.mp4', | |
| description: 'Повтор' | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| const response = await axios.post('https://api.github.com/gists', { | |
| description: 'V-OTV Database (users + schedule)', | |
| public: false, | |
| files: { | |
| 'users.json': { | |
| content: JSON.stringify(defaultData.users, null, 2) | |
| }, | |
| 'schedule.json': { | |
| content: JSON.stringify(defaultData.schedule, null, 2) | |
| } | |
| } | |
| }, { | |
| headers: { | |
| 'Authorization': `token ${GITHUB_TOKEN}`, | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }); | |
| GIST_ID = response.data.id; | |
| GIST_API = `https://api.github.com/gists/${GIST_ID}`; | |
| console.log(`✅ Gist created successfully! ID: ${GIST_ID}`); | |
| console.log(`⚠️ Add this to your HF Secrets: GIST_ID=${GIST_ID}`); | |
| return defaultData; | |
| } catch (error) { | |
| console.error('Failed to create Gist:', error.message); | |
| return null; | |
| } | |
| } | |
| // Инициализация Gist (создание если не существует) | |
| async function initializeGist() { | |
| if (gistInitialized) return true; | |
| if (!GIST_ID && GITHUB_TOKEN) { | |
| console.log('📋 No GIST_ID provided, creating new Gist...'); | |
| const data = await createDefaultGist(); | |
| if (data) { | |
| usersCache = data.users; | |
| scheduleCache = data.schedule; | |
| cacheTime = Date.now(); | |
| gistInitialized = true; | |
| return true; | |
| } | |
| return false; | |
| } | |
| if (!GIST_ID || !GITHUB_TOKEN) { | |
| console.warn('⚠️ GIST_ID or GITHUB_TOKEN not set. Using local fallback.'); | |
| gistInitialized = true; | |
| return false; | |
| } | |
| // Проверка существования Gist | |
| try { | |
| await axios.get(GIST_API, { | |
| headers: { | |
| 'Authorization': `token ${GITHUB_TOKEN}`, | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }); | |
| gistInitialized = true; | |
| return true; | |
| } catch (error) { | |
| if (error.response && error.response.status === 404) { | |
| console.log('📋 Gist not found, creating new one...'); | |
| const data = await createDefaultGist(); | |
| if (data) { | |
| usersCache = data.users; | |
| scheduleCache = data.schedule; | |
| cacheTime = Date.now(); | |
| gistInitialized = true; | |
| return true; | |
| } | |
| } | |
| console.error('Gist initialization error:', error.message); | |
| return false; | |
| } | |
| } | |
| // Get Gist data | |
| async function getGistData() { | |
| await initializeGist(); | |
| if (!GIST_ID || !GITHUB_TOKEN) { | |
| return getLocalData(); | |
| } | |
| try { | |
| const response = await axios.get(GIST_API, { | |
| headers: { | |
| 'Authorization': `token ${GITHUB_TOKEN}`, | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }); | |
| const files = response.data.files; | |
| let users = []; | |
| let schedule = []; | |
| // Загрузка и расшифровка пользователей | |
| if (files['users.json']) { | |
| const encryptedUsers = JSON.parse(files['users.json'].content); | |
| users = crypto.decryptUsers(encryptedUsers); | |
| } | |
| // Загрузка расписания (не шифруется) | |
| if (files['schedule.json']) { | |
| schedule = JSON.parse(files['schedule.json'].content); | |
| } | |
| return { users, schedule }; | |
| } catch (error) { | |
| console.error('Gist fetch error:', error.message); | |
| return getLocalData(); | |
| } | |
| } | |
| // Update Gist data | |
| async function updateGistData(data) { | |
| await initializeGist(); | |
| if (!GIST_ID || !GITHUB_TOKEN) { | |
| return saveLocalData(data); | |
| } | |
| try { | |
| const files = {}; | |
| if (data.users) { | |
| // Шифрование пользователей перед сохранением | |
| const encryptedUsers = crypto.encryptUsers(data.users); | |
| files['users.json'] = { | |
| content: JSON.stringify(encryptedUsers, null, 2) | |
| }; | |
| } | |
| if (data.schedule) { | |
| // Расписание не шифруется | |
| files['schedule.json'] = { | |
| content: JSON.stringify(data.schedule, null, 2) | |
| }; | |
| } | |
| await axios.patch(GIST_API, { files }, { | |
| headers: { | |
| 'Authorization': `token ${GITHUB_TOKEN}`, | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| }); | |
| // Clear cache | |
| usersCache = null; | |
| scheduleCache = null; | |
| cacheTime = 0; | |
| return true; | |
| } catch (error) { | |
| console.error('Gist update error:', error.message); | |
| return false; | |
| } | |
| } | |
| // Local fallback | |
| function getLocalData() { | |
| return { | |
| users: [], | |
| schedule: [ | |
| { | |
| channelId: 'TV-1', | |
| channelName: 'TV-1', | |
| programs: [ | |
| { | |
| time: '08:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 1-4', | |
| file: 'nu-pogodi-1-4.mp4', | |
| description: 'Классика советской анимации' | |
| }, | |
| { | |
| time: '10:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 5-8', | |
| file: 'nu-pogodi-5-8.mp4', | |
| description: 'Продолжение приключений' | |
| }, | |
| { | |
| time: '12:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 9-12', | |
| file: 'nu-pogodi-9-12.mp4', | |
| description: 'Новые серии' | |
| }, | |
| { | |
| time: '14:00', // Время в МСК (UTC+3) | |
| name: 'Ну, погоди! — Выпуск 13-16', | |
| file: 'nu-pogodi-13-16.mp4', | |
| description: 'Финальные серии' | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| } | |
| function saveLocalData(data) { | |
| // In production with Gist, this won't be used | |
| console.log('Local save (no-op in production)'); | |
| return true; | |
| } | |
| // Public API | |
| async function getUsers() { | |
| const now = Date.now(); | |
| if (usersCache && (now - cacheTime) < CACHE_DURATION) { | |
| return usersCache; | |
| } | |
| const data = await getGistData(); | |
| usersCache = data.users; | |
| cacheTime = now; | |
| return usersCache; | |
| } | |
| async function saveUsers(users) { | |
| usersCache = users; | |
| return await updateGistData({ users }); | |
| } | |
| async function getSchedule() { | |
| const now = Date.now(); | |
| if (scheduleCache && (now - cacheTime) < CACHE_DURATION) { | |
| return scheduleCache; | |
| } | |
| const data = await getGistData(); | |
| scheduleCache = data.schedule; | |
| cacheTime = now; | |
| return scheduleCache; | |
| } | |
| async function saveSchedule(schedule) { | |
| scheduleCache = schedule; | |
| return await updateGistData({ schedule }); | |
| } | |
| module.exports = { | |
| getUsers, | |
| saveUsers, | |
| getSchedule, | |
| saveSchedule, | |
| initializeGist | |
| }; | |