mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
199 lines
5.6 KiB
Go
199 lines
5.6 KiB
Go
package services
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// YouTubeChannelService handles specific channel integrations
|
|
type YouTubeChannelService struct {
|
|
YouTubeService *YouTubeService
|
|
CacheService *YouTubeCacheService
|
|
}
|
|
|
|
// NewYouTubeChannelService creates a new instance of YouTubeChannelService
|
|
func NewYouTubeChannelService(youtubeService *YouTubeService, cacheService *YouTubeCacheService) *YouTubeChannelService {
|
|
return &YouTubeChannelService{
|
|
YouTubeService: youtubeService,
|
|
CacheService: cacheService,
|
|
}
|
|
}
|
|
|
|
// GetPredefinedChannels returns the list of predefined channels
|
|
func GetPredefinedChannels() []Channel {
|
|
return []Channel{
|
|
{
|
|
ID: "fireship",
|
|
Name: "Fireship",
|
|
Description: "Rapid web development tutorials and courses",
|
|
Thumbnail: "https://img.youtube.com/vi/UCsBjURrPoezykLs9EqgAJVQ/mqdefault.jpg",
|
|
},
|
|
{
|
|
ID: "networkchuck",
|
|
Name: "NetworkChuck",
|
|
Description: "Cybersecurity and networking tutorials",
|
|
Thumbnail: "https://img.youtube.com/vi/UCNlz1cb4DvEx7rTnT2s7B3A/mqdefault.jpg",
|
|
},
|
|
{
|
|
ID: "programmingwithmosh",
|
|
Name: "Programming with Mosh",
|
|
Description: "Comprehensive programming tutorials",
|
|
Thumbnail: "https://img.youtube.com/vi/UC8butUNob-8kuy47X7vH6ws/mqdefault.jpg",
|
|
},
|
|
{
|
|
ID: "traversymedia",
|
|
Name: "Traversy Media",
|
|
Description: "Web development and design tutorials",
|
|
Thumbnail: "https://img.youtube.com/vi/UC29J8QxEQ7QmM0TJ_8Jt_gQ/mqdefault.jpg",
|
|
},
|
|
{
|
|
ID: "thenewboston",
|
|
Name: "The New Boston",
|
|
Description: "Computer science and programming courses",
|
|
Thumbnail: "https://img.youtube.com/vi/UCrwkHaJ-9Sd74Kx1n-9Qjg/mqdefault.jpg",
|
|
},
|
|
}
|
|
}
|
|
|
|
// Channel represents a YouTube channel
|
|
type Channel struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
}
|
|
|
|
// GetFireshipVideos fetches latest videos from Fireship channel
|
|
func (ycs *YouTubeChannelService) GetFireshipVideos(limit int) ([]YouTubeVideo, error) {
|
|
// Use cached data to avoid rate limiting
|
|
response, err := ycs.CacheService.GetCachedChannelVideos("fireship", limit)
|
|
if err != nil {
|
|
if err.Error() == "YouTube is rate limiting us. Please try again later." {
|
|
// Return rate limiting error
|
|
return nil, fmt.Errorf("YouTube is rate limiting us. Please try again later.")
|
|
}
|
|
return nil, fmt.Errorf("failed to fetch Fireship videos: %w", err)
|
|
}
|
|
|
|
// Return all videos without filtering
|
|
return response.Videos, nil
|
|
}
|
|
|
|
// GetNetworkChuckVideos fetches latest videos from Network Chuck channel
|
|
func (ycs *YouTubeChannelService) GetNetworkChuckVideos(limit int) ([]YouTubeVideo, error) {
|
|
// Use cached data to avoid rate limiting
|
|
response, err := ycs.CacheService.GetCachedChannelVideos("networkchuck", limit)
|
|
if err != nil {
|
|
if err.Error() == "YouTube is rate limiting us. Please try again later." {
|
|
// Return rate limiting error
|
|
return nil, fmt.Errorf("YouTube is rate limiting us. Please try again later.")
|
|
}
|
|
return nil, fmt.Errorf("failed to fetch Network Chuck videos: %w", err)
|
|
}
|
|
|
|
// Return all videos without filtering
|
|
return response.Videos, nil
|
|
}
|
|
|
|
// GetChannelInfo fetches basic information about a channel
|
|
func (ycs *YouTubeChannelService) GetChannelInfo(channelID string) (*ChannelInfo, error) {
|
|
// For now, return basic info from predefined channels
|
|
channels := GetPredefinedChannels()
|
|
for _, channel := range channels {
|
|
if channel.ID == channelID {
|
|
return &ChannelInfo{
|
|
ID: channel.ID,
|
|
Title: channel.Name,
|
|
Description: channel.Description,
|
|
Thumbnail: channel.Thumbnail,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("channel not found")
|
|
}
|
|
|
|
// ChannelInfo represents basic channel information
|
|
type ChannelInfo struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
// parseYouTubeDuration converts ISO 8601 duration string to seconds
|
|
func parseYouTubeDuration(duration string) int {
|
|
// YouTube duration format: PT4M13S (4 minutes 13 seconds)
|
|
// Simple parser for common formats
|
|
seconds := 0
|
|
current := 0
|
|
|
|
for _, char := range duration {
|
|
switch char {
|
|
case 'H', 'h':
|
|
seconds += current * 3600
|
|
current = 0
|
|
case 'M', 'm':
|
|
seconds += current * 60
|
|
current = 0
|
|
case 'S', 's':
|
|
seconds += current
|
|
current = 0
|
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
current = current*10 + int(char-'0')
|
|
}
|
|
}
|
|
|
|
return seconds
|
|
}
|
|
|
|
func containsIgnoreCase(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr ||
|
|
(len(s) > len(substr) &&
|
|
(s[:len(substr)] == substr ||
|
|
s[len(s)-len(substr):] == substr ||
|
|
containsSubstringIgnoreCase(s, substr))))
|
|
}
|
|
|
|
func containsSubstringIgnoreCase(s, substr string) bool {
|
|
s = toLower(s)
|
|
substr = toLower(substr)
|
|
return contains(s, substr)
|
|
}
|
|
|
|
func toLower(s string) string {
|
|
result := make([]rune, len([]rune(s)))
|
|
for i, r := range []rune(s) {
|
|
if r >= 'A' && r <= 'Z' {
|
|
result[i] = r + ('a' - 'A')
|
|
} else {
|
|
result[i] = r
|
|
}
|
|
}
|
|
return string(result)
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isRecentVideo(publishedAt string, months int) bool {
|
|
// Parse the published date (ISO 8601 format)
|
|
layout := "2006-01-02T15:04:05Z"
|
|
publishedTime, err := time.Parse(layout, publishedAt)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Check if the video is within the specified months
|
|
cutoffTime := time.Now().AddDate(0, -months, 0)
|
|
return publishedTime.After(cutoffTime)
|
|
}
|