Skip to content

Commit

Permalink
wip: work
Browse files Browse the repository at this point in the history
  • Loading branch information
brainwo committed May 5, 2024
1 parent 16249e4 commit 9e3d644
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 21 deletions.
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'helper/command_parser.dart';
import 'intent.dart';
import 'locale/en_us.dart';
import 'model/config.dart';
import 'model/database.dart';
import 'model/setting_options.dart';
import 'model/state.dart';
import 'model/theme.dart';
Expand Down Expand Up @@ -39,6 +40,7 @@ class _ThemedAppState extends ConsumerState<ThemedApp> {
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((final _) async {
await HistoryDatabase.load();
final intialBrightnessMode = ref.read(brightnessModeProvider);
final prefs = await UserConfig.load();
final userBrightness = prefs.theme.brightness;
Expand Down
129 changes: 129 additions & 0 deletions lib/model/database.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import 'dart:io';

// import 'package:csv/csv.dart';
// import 'package:csv/csv.dart';
// import 'package:fast_csv/csv_parser.dart';
import 'package:xdg_directories/xdg_directories.dart' as xdg;

import '../util/csv_parser.dart';

enum ItemType {
video,
playlist,
channel,
}

class HistoryDatabase {
const HistoryDatabase({required this.history});

final List<HistoryModel> history;

static Future<HistoryDatabase> load({final int? limit}) async {
final file = File('${xdg.dataHome.path}/yatta/history.csv');
return HistoryDatabase(
history: const CsvParser()
.parse(await file.readAsLines())
.map((final e) => switch (e) {
{
'id': final String id,
'type': final String type,
'provider': final String provider,
'title': final String title,
'description': final String description,
'url': final String url,
'viewCount': final String viewCount,
'channelId': final String channelId,
'channelTitle': final String channelTitle,
'iconUrl': final String iconUrl,
'thumbnailUrl': final String thumbnailUrl,
'previewUrl': final String previewUrl,
'publishDate': final String publishDate,
'duration': _,
'romanizedMetadata': final String romanizedMetadata,
'history': final String history,
} =>
HistoryModel(
id: id,
title: title,
description: description,
duration: Duration.zero,
romanizedMetadata: romanizedMetadata,
publishDate: publishDate,
type: switch (type) {
'video' => ItemType.video,
'playlist' => ItemType.playlist,
'channel' => ItemType.channel,
_ => ItemType.video,
},
history: history.split(','),
channelId: channelId,
iconUrl: iconUrl,
thumbnailUrl: thumbnailUrl,
previewUrl: previewUrl,
provider: provider,
channelTitle: channelTitle,
url: url,
viewCount: int.tryParse(viewCount),
),
_ => throw UnimplementedError(),
})
.toList());
}
}

class HistoryModel {
const HistoryModel({
required this.id,
required this.title,
required this.duration,
required this.description,
required this.romanizedMetadata,
required this.publishDate,
required this.type,
required this.history,
required this.channelId,
required this.iconUrl,
required this.thumbnailUrl,
required this.previewUrl,
required this.provider,
required this.channelTitle,
required this.url,
required this.viewCount,
});

/// Can be video id or playlist id
final String id;

final ItemType type;

/// Video site (e.g. "youtube")
final String provider;

final String title;
final String description;
final String url;
final int? viewCount;

/// Channel name
final String channelTitle;
final String channelId;

/// Cover image < 120px width
final String iconUrl;

/// Cover image < 32px width
final String thumbnailUrl;

/// Cover image < 48px width
final String previewUrl;

/// ISO 8601 format time
final String publishDate;
final Duration duration;

/// ISO 8601 format time
final List<String> history;

/// Search friendly string
final String romanizedMetadata;
}
31 changes: 14 additions & 17 deletions lib/page/history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:youtube_api/youtube_api.dart';

import '../../intent.dart';
import '../helper/command_parser.dart';
import '../model/database.dart';
import '../widget/keyboard_navigation.dart';
import '../widget/list_items/list_item.dart';

Expand All @@ -19,8 +20,8 @@ class HistoryPage extends StatefulWidget {

class _HistoryPageState extends State<HistoryPage> {
final FocusNode searchBarFocus = FocusNode();
List<YoutubeVideo>? filteredList;
List<YoutubeVideo>? historyList;
List<HistoryModel>? filteredList;
List<HistoryModel>? historyList;
late final Map<Type, Action<Intent>> _actionMap = {
SearchBarFocusIntent: CallbackAction<Intent>(
onInvoke: (final _) => _requestSearchBarFocus(),
Expand All @@ -40,13 +41,10 @@ class _HistoryPageState extends State<HistoryPage> {
}

Future<void> fetchHistory() async {
final prefs = await SharedPreferences.getInstance();
final database = await HistoryDatabase.load();

setState(() {
historyList = prefs
.getStringList('history')
?.map((final e) => YoutubeVideo.fromString(e))
.toList();
historyList = database.history;
filteredList = historyList;
});
}
Expand Down Expand Up @@ -91,26 +89,25 @@ class _HistoryPageState extends State<HistoryPage> {
filteredList![filteredList!.length - index - 1];
final title = youtubeVideo.title.parseHtmlEntities();

final listItem = switch (youtubeVideo.kind) {
'video' => ListItemVideo(
final listItem = switch (youtubeVideo.type) {
ItemType.video => ListItemVideo(
title: title,
channelTitle: youtubeVideo.channelTitle,
description: youtubeVideo.description,
duration: youtubeVideo.duration!,
thumbnailUrl: youtubeVideo.thumbnail.medium.url,
publishedAt: youtubeVideo.publishedAt,
duration: youtubeVideo.duration.toString(),
thumbnailUrl: youtubeVideo.thumbnailUrl,
publishedAt: youtubeVideo.publishDate,
),
'channel' => ListItemChannel(
ItemType.channel => ListItemChannel(
channelTitle: youtubeVideo.channelTitle,
thumbnailUrl: youtubeVideo.thumbnail.medium.url,
thumbnailUrl: youtubeVideo.thumbnailUrl,
),
'playlist' => ListItemPlaylist(
ItemType.playlist => ListItemPlaylist(
title: title,
channelTitle: youtubeVideo.channelTitle,
description: youtubeVideo.description,
thumbnailUrl: youtubeVideo.thumbnail.medium.url,
thumbnailUrl: youtubeVideo.thumbnailUrl,
),
_ => const SizedBox.shrink()
};

return ListItem(
Expand Down
42 changes: 42 additions & 0 deletions lib/util/csv_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/// Not exactly RFC 4810
class CsvParser {
const CsvParser({this.separator = ';'});
final String separator;

static List<String> _splitLine(final String line) {
var buff = <String>[''];
var inQuotationMark = false;

for (var i = 0; i < line.length; i++) {
if (!inQuotationMark) {
if (line[i] == ',') {
buff = [...buff, ''];
continue;
}
}
if (line[i] == '"') {
inQuotationMark = !inQuotationMark;
continue;
}
buff.last += line[i];
}
return buff;
}

List<Map<String, dynamic>> parse(final List<String> csv) {
var lineNumber = 0;
late final List<String> header;
final content = <List<String>>[];

csv.forEach((final line) {
if (lineNumber == 0) {
header = _splitLine(line);
} else {
content.add(_splitLine(line));
}
lineNumber++;
});

return content.map((final e) => Map.fromIterables(header, e)).toList();
}
}
37 changes: 37 additions & 0 deletions test/csv_parser_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:test/test.dart';
import 'package:yatta/util/csv_parser.dart';

const String csv = '''
id,type,provider,title,description,url,viewCount,channelId,channelTitle,iconUrl,thumbnailUrl,previewUrl,publishDate,duration,history,romanized
W2muWA-40Uk,video,youtube,三月のパンタシア 『夜光』,三月のパンタシア-夜光 小説「さよならの空はあの青い花の輝きとよく似ていた」(みあ著)主題歌 ...,https://youtu.be/W2muWA-40Uk,,UC4lk0Ob-F3ptOQUUq8s0pzQ,三月のパンタシア Official YouTube Channel,https://i.ytimg.com/vi/W2muWA-40Uk/default.jpg,https://i.ytimg.com/vi/W2muWA-40Uk/mqdefault.jpg,https://i.ytimg.com/vi/W2muWA-40Uk/hqdefault.jpg,2021-07-21T13:00:10Z,03:40,"2021-07-21T13:00:10Z,2022-07-21T13:00:10Z",sangatsu no phantasia yakou sangatsu no phantasia yakou sousetsu sayonara no sora wa ano aoi hana no kagayaki to yoku nite ita mia cho shudaika''';

void main() {
group('Parse history', () {
const parsedCsv = [
{
'id': 'W2muWA-40Uk',
'type': 'video',
'provider': 'youtube',
'title': '三月のパンタシア 『夜光』',
'description': '三月のパンタシア-夜光 小説「さよならの空はあの青い花の輝きとよく似ていた」(みあ著)主題歌 ...',
'url': 'https://youtu.be/W2muWA-40Uk',
'viewCount': '',
'channelId': 'UC4lk0Ob-F3ptOQUUq8s0pzQ',
'channelTitle': '三月のパンタシア Official YouTube Channel',
'iconUrl': 'https://i.ytimg.com/vi/W2muWA-40Uk/default.jpg',
'thumbnailUrl': 'https://i.ytimg.com/vi/W2muWA-40Uk/mqdefault.jpg',
'previewUrl': 'https://i.ytimg.com/vi/W2muWA-40Uk/hqdefault.jpg',
'publishDate': '2021-07-21T13:00:10Z',
'duration': '03:40',
'history': '2021-07-21T13:00:10Z,2022-07-21T13:00:10Z',
'romanized': '''
sangatsu no phantasia yakou sangatsu no phantasia yakou sousetsu sayonara no sora wa ano aoi hana no kagayaki to yoku nite ita mia cho shudaika'''
}
];

test(
'history',
() => expect(const CsvParser().parse(csv.split('\n')), parsedCsv),
);
});
}
8 changes: 4 additions & 4 deletions test/helper_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:test/test.dart';
import 'package:yatta/helper.dart';
import 'package:yatta/helper/command_parser.dart';

void main() {
group('All available', () {
Expand Down Expand Up @@ -48,8 +48,8 @@ void main() {
];
const parsedNotifySend = [
'notify-send',
'"Playing video"',
'"This is a title\\nThis is a description"',
'Playing video',
'This is a title\\nThis is a description',
];

final helper = (final String command) {
Expand Down Expand Up @@ -101,7 +101,7 @@ void main() {
'background=#1f1f1f'
];
const parsedCurl = ['curl', '--output', '/tmp/notifyicon.png'];
const parsedNotifySend = ['notify-send', '"Playing video"', '"\\n"'];
const parsedNotifySend = ['notify-send', 'Playing video', '\\n'];

test('xwinwrap', () => expect(parseCommand(xwinwrap), parsedXwinwrap));
test('kitty', () => expect(parseCommand(kitty), parsedKitty));
Expand Down

0 comments on commit 9e3d644

Please sign in to comment.