595 lines
13 KiB
Markdown
595 lines
13 KiB
Markdown
# EasyStream API Quick Start Guide
|
|
|
|
## Get Started in 5 Minutes
|
|
|
|
This guide will get you up and running with the EasyStream API quickly.
|
|
|
|
## Prerequisites
|
|
|
|
- EasyStream installed and running
|
|
- Modern web browser
|
|
- Basic JavaScript knowledge
|
|
|
|
---
|
|
|
|
## 1. Authentication
|
|
|
|
### Login and Get Token
|
|
|
|
```javascript
|
|
// The API client is automatically available as 'api'
|
|
const result = await api.login('myusername', 'mypassword');
|
|
|
|
if (result.success) {
|
|
console.log('Logged in!');
|
|
console.log('User:', result.user);
|
|
console.log('Token:', result.token);
|
|
// Token is automatically stored
|
|
}
|
|
```
|
|
|
|
### Check if Logged In
|
|
|
|
```javascript
|
|
if (api.isAuthenticated()) {
|
|
console.log('User is logged in');
|
|
} else {
|
|
console.log('Please log in');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Working with Videos
|
|
|
|
### List Videos
|
|
|
|
```javascript
|
|
const videos = await api.getVideos({
|
|
page: 1,
|
|
limit: 20,
|
|
sort: 'popular'
|
|
});
|
|
|
|
console.log('Videos:', videos.data.videos);
|
|
```
|
|
|
|
### Get Single Video
|
|
|
|
```javascript
|
|
const video = await api.getVideo('123456');
|
|
console.log('Video:', video.data);
|
|
```
|
|
|
|
### Search Videos
|
|
|
|
```javascript
|
|
const results = await api.searchVideos('funny cats');
|
|
console.log('Found:', results.data.videos);
|
|
```
|
|
|
|
### Like a Video
|
|
|
|
```javascript
|
|
await api.likeVideo('123456', 'like');
|
|
console.log('Video liked!');
|
|
```
|
|
|
|
### Create a Video
|
|
|
|
```javascript
|
|
const newVideo = await api.createVideo({
|
|
title: 'My Awesome Video',
|
|
description: 'This is a great video',
|
|
privacy: 'public',
|
|
category: 'entertainment'
|
|
});
|
|
|
|
console.log('Created video:', newVideo.data.file_key);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. User Profiles
|
|
|
|
### Get Current User Profile
|
|
|
|
```javascript
|
|
const myProfile = await api.getMyProfile();
|
|
console.log('My profile:', myProfile.data);
|
|
```
|
|
|
|
### Get Another User's Profile
|
|
|
|
```javascript
|
|
const userProfile = await api.getUserProfile(123);
|
|
console.log('User:', userProfile.data);
|
|
```
|
|
|
|
### Update Profile
|
|
|
|
```javascript
|
|
await api.updateProfile({
|
|
usr_dname: 'My New Name',
|
|
usr_about: 'I love making videos!'
|
|
});
|
|
|
|
console.log('Profile updated!');
|
|
```
|
|
|
|
### Upload Avatar
|
|
|
|
```html
|
|
<input type="file" id="avatar-input" accept="image/*">
|
|
<button onclick="uploadAvatar()">Upload</button>
|
|
|
|
<script>
|
|
async function uploadAvatar() {
|
|
const fileInput = document.getElementById('avatar-input');
|
|
const file = fileInput.files[0];
|
|
|
|
if (file) {
|
|
const result = await api.uploadAvatar(file);
|
|
console.log('Avatar uploaded:', result.data.avatar_url);
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Comments
|
|
|
|
### Get Comments for a Video
|
|
|
|
```javascript
|
|
const comments = await api.getComments('123456', {
|
|
page: 1,
|
|
sort: 'recent'
|
|
});
|
|
|
|
console.log('Comments:', comments.data.comments);
|
|
```
|
|
|
|
### Post a Comment
|
|
|
|
```javascript
|
|
const newComment = await api.createComment(
|
|
'123456', // video file_key
|
|
'Great video!', // comment text
|
|
null // parent_id (null for top-level)
|
|
);
|
|
|
|
console.log('Comment posted:', newComment.data);
|
|
```
|
|
|
|
### Reply to a Comment
|
|
|
|
```javascript
|
|
const reply = await api.createComment(
|
|
'123456', // video file_key
|
|
'Thanks!', // reply text
|
|
789 // parent comment ID
|
|
);
|
|
|
|
console.log('Reply posted:', reply.data);
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Subscriptions
|
|
|
|
### Subscribe to a Channel
|
|
|
|
```javascript
|
|
await api.subscribe(456); // channel user ID
|
|
console.log('Subscribed!');
|
|
```
|
|
|
|
### Check if Subscribed
|
|
|
|
```javascript
|
|
const status = await api.checkSubscription(456);
|
|
|
|
if (status.data.is_subscribed) {
|
|
console.log('Already subscribed');
|
|
} else {
|
|
console.log('Not subscribed');
|
|
}
|
|
```
|
|
|
|
### Get Subscription Feed
|
|
|
|
```javascript
|
|
const feed = await api.getSubscriptionFeed({ page: 1 });
|
|
console.log('New videos from subscriptions:', feed.data.videos);
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Complete Examples
|
|
|
|
### Video Player Page
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Watch Video</title>
|
|
<style>
|
|
.video-container { max-width: 800px; margin: 0 auto; }
|
|
.actions button { margin: 5px; }
|
|
.comments { margin-top: 20px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="video-container">
|
|
<h1 id="video-title"></h1>
|
|
<p id="video-description"></p>
|
|
|
|
<div class="actions">
|
|
<button id="like-btn">👍 Like</button>
|
|
<button id="subscribe-btn">Subscribe</button>
|
|
<button id="watch-later-btn">⏰ Watch Later</button>
|
|
</div>
|
|
|
|
<div class="comments">
|
|
<h2>Comments</h2>
|
|
<div id="comments-list"></div>
|
|
|
|
<form id="comment-form">
|
|
<textarea id="comment-text" placeholder="Add a comment..." required></textarea>
|
|
<button type="submit">Post Comment</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/f_scripts/fe/js/api-helper.js"></script>
|
|
<script>
|
|
const fileKey = '123456'; // Get from URL
|
|
|
|
// Load video and comments
|
|
async function init() {
|
|
try {
|
|
// Load video
|
|
const video = await api.getVideo(fileKey);
|
|
document.getElementById('video-title').textContent = video.data.file_title;
|
|
document.getElementById('video-description').textContent = video.data.file_description;
|
|
|
|
// Record view
|
|
await api.recordVideoView(fileKey);
|
|
|
|
// Load comments
|
|
await loadComments();
|
|
|
|
// Setup buttons
|
|
setupButtons(video.data);
|
|
|
|
} catch (error) {
|
|
api.handleError(error);
|
|
}
|
|
}
|
|
|
|
async function loadComments() {
|
|
const comments = await api.getComments(fileKey);
|
|
const list = document.getElementById('comments-list');
|
|
|
|
list.innerHTML = comments.data.comments.map(c => `
|
|
<div class="comment">
|
|
<strong>${c.usr_dname}</strong>
|
|
<p>${c.comment_text}</p>
|
|
<small>${c.comment_date}</small>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function setupButtons(video) {
|
|
// Like button
|
|
document.getElementById('like-btn').addEventListener('click', async () => {
|
|
await api.likeVideo(fileKey, 'like');
|
|
alert('Video liked!');
|
|
});
|
|
|
|
// Subscribe button
|
|
document.getElementById('subscribe-btn').addEventListener('click', async () => {
|
|
await api.subscribe(video.usr_id);
|
|
alert('Subscribed!');
|
|
});
|
|
|
|
// Watch later button
|
|
document.getElementById('watch-later-btn').addEventListener('click', async () => {
|
|
await api.toggleWatchLater(fileKey);
|
|
alert('Added to Watch Later!');
|
|
});
|
|
|
|
// Comment form
|
|
document.getElementById('comment-form').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const text = document.getElementById('comment-text').value;
|
|
|
|
await api.createComment(fileKey, text);
|
|
document.getElementById('comment-text').value = '';
|
|
await loadComments();
|
|
});
|
|
}
|
|
|
|
// Start
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
### Browse Videos Page
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Browse Videos</title>
|
|
<style>
|
|
.video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
|
|
.video-card { border: 1px solid #ddd; padding: 15px; }
|
|
.filters { margin-bottom: 20px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Browse Videos</h1>
|
|
|
|
<div class="filters">
|
|
<select id="sort-select">
|
|
<option value="recent">Recent</option>
|
|
<option value="popular">Popular</option>
|
|
<option value="featured">Featured</option>
|
|
</select>
|
|
|
|
<input type="text" id="search-input" placeholder="Search videos...">
|
|
<button id="search-btn">Search</button>
|
|
</div>
|
|
|
|
<div class="video-grid" id="video-grid"></div>
|
|
|
|
<button id="load-more">Load More</button>
|
|
</div>
|
|
|
|
<script src="/f_scripts/fe/js/api-helper.js"></script>
|
|
<script>
|
|
let currentPage = 1;
|
|
let currentSort = 'recent';
|
|
let searchQuery = '';
|
|
|
|
async function loadVideos(append = false) {
|
|
try {
|
|
let result;
|
|
|
|
if (searchQuery) {
|
|
result = await api.searchVideos(searchQuery, {
|
|
page: currentPage,
|
|
limit: 20
|
|
});
|
|
} else {
|
|
result = await api.getVideos({
|
|
page: currentPage,
|
|
limit: 20,
|
|
sort: currentSort
|
|
});
|
|
}
|
|
|
|
displayVideos(result.data.videos, append);
|
|
|
|
// Show/hide load more button
|
|
const loadMoreBtn = document.getElementById('load-more');
|
|
if (currentPage >= result.data.pagination.pages) {
|
|
loadMoreBtn.style.display = 'none';
|
|
} else {
|
|
loadMoreBtn.style.display = 'block';
|
|
}
|
|
|
|
} catch (error) {
|
|
api.handleError(error);
|
|
}
|
|
}
|
|
|
|
function displayVideos(videos, append = false) {
|
|
const grid = document.getElementById('video-grid');
|
|
|
|
if (!append) {
|
|
grid.innerHTML = '';
|
|
}
|
|
|
|
videos.forEach(video => {
|
|
const card = document.createElement('div');
|
|
card.className = 'video-card';
|
|
card.innerHTML = `
|
|
<h3>${video.file_title}</h3>
|
|
<p>${video.file_description}</p>
|
|
<p>
|
|
<small>
|
|
By ${video.usr_dname} •
|
|
${video.file_views} views •
|
|
${video.like_count} likes
|
|
</small>
|
|
</p>
|
|
<button onclick="watchVideo('${video.file_key}')">
|
|
Watch
|
|
</button>
|
|
`;
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
window.watchVideo = function(fileKey) {
|
|
window.location.href = `/watch.php?v=${fileKey}`;
|
|
};
|
|
|
|
// Sort change
|
|
document.getElementById('sort-select').addEventListener('change', (e) => {
|
|
currentSort = e.target.value;
|
|
currentPage = 1;
|
|
loadVideos(false);
|
|
});
|
|
|
|
// Search
|
|
document.getElementById('search-btn').addEventListener('click', () => {
|
|
searchQuery = document.getElementById('search-input').value;
|
|
currentPage = 1;
|
|
loadVideos(false);
|
|
});
|
|
|
|
// Load more
|
|
document.getElementById('load-more').addEventListener('click', () => {
|
|
currentPage++;
|
|
loadVideos(true);
|
|
});
|
|
|
|
// Initial load
|
|
loadVideos();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Error Handling
|
|
|
|
Always wrap API calls in try-catch:
|
|
|
|
```javascript
|
|
try {
|
|
const result = await api.someAPICall();
|
|
// Handle success
|
|
} catch (error) {
|
|
// Handle error
|
|
console.error('Error:', error.message);
|
|
|
|
// Use built-in error handler
|
|
api.handleError(error);
|
|
|
|
// Or custom handling
|
|
if (error.message.includes('Authentication')) {
|
|
window.location.href = '/signin';
|
|
} else {
|
|
alert('Error: ' + error.message);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Common Patterns
|
|
|
|
### Loading State
|
|
|
|
```javascript
|
|
async function loadData() {
|
|
showLoading(); // Your loading function
|
|
|
|
try {
|
|
const result = await api.getVideos();
|
|
displayData(result.data);
|
|
} catch (error) {
|
|
api.handleError(error);
|
|
} finally {
|
|
hideLoading(); // Always hide loading
|
|
}
|
|
}
|
|
```
|
|
|
|
### Infinite Scroll
|
|
|
|
```javascript
|
|
let currentPage = 1;
|
|
let loading = false;
|
|
|
|
window.addEventListener('scroll', async () => {
|
|
if (loading) return;
|
|
|
|
const scrolledToBottom =
|
|
window.innerHeight + window.scrollY >= document.body.offsetHeight - 500;
|
|
|
|
if (scrolledToBottom) {
|
|
loading = true;
|
|
currentPage++;
|
|
|
|
try {
|
|
const result = await api.getVideos({ page: currentPage });
|
|
appendVideos(result.data.videos);
|
|
} catch (error) {
|
|
api.handleError(error);
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
### Debounced Search
|
|
|
|
```javascript
|
|
let searchTimeout;
|
|
|
|
document.getElementById('search-input').addEventListener('input', (e) => {
|
|
clearTimeout(searchTimeout);
|
|
|
|
searchTimeout = setTimeout(async () => {
|
|
const query = e.target.value;
|
|
|
|
if (query.length >= 2) {
|
|
const results = await api.searchVideos(query);
|
|
displaySearchResults(results.data.videos);
|
|
}
|
|
}, 300); // Wait 300ms after user stops typing
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Browser Console Testing
|
|
|
|
Test API calls directly in the browser console:
|
|
|
|
```javascript
|
|
// Check authentication
|
|
api.isAuthenticated()
|
|
|
|
// Login
|
|
await api.login('username', 'password')
|
|
|
|
// Get videos
|
|
await api.getVideos({ page: 1 })
|
|
|
|
// Get current user
|
|
await api.getMyProfile()
|
|
|
|
// View stored token
|
|
localStorage.getItem('jwt_token')
|
|
|
|
// Clear token
|
|
api.clearToken()
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Next Steps
|
|
|
|
- Read [API_DOCUMENTATION.md](API_DOCUMENTATION.md) for complete reference
|
|
- Check [FRONTEND_BACKEND_INTEGRATION_GUIDE.md](FRONTEND_BACKEND_INTEGRATION_GUIDE.md) for advanced patterns
|
|
- Review example code in [f_scripts/fe/js/api-helper.js](../f_scripts/fe/js/api-helper.js)
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
If you encounter issues:
|
|
|
|
1. Check browser console for errors
|
|
2. Check Network tab to see API requests/responses
|
|
3. Verify you're logged in: `api.isAuthenticated()`
|
|
4. Check API documentation for correct parameters
|
|
|
|
---
|
|
|
|
**Last Updated:** January 2025
|